diff --git a/aurweb/routers/html.py b/aurweb/routers/html.py index ae012901..c2375f69 100644 --- a/aurweb/routers/html.py +++ b/aurweb/routers/html.py @@ -9,11 +9,15 @@ from fastapi.responses import HTMLResponse, RedirectResponse from sqlalchemy import and_, or_ import aurweb.config +import aurweb.models.package_request from aurweb import db, util from aurweb.cache import db_count_cache from aurweb.models.account_type import TRUSTED_USER_AND_DEV_ID, TRUSTED_USER_ID +from aurweb.models.package import Package from aurweb.models.package_base import PackageBase +from aurweb.models.package_comaintainer import PackageComaintainer +from aurweb.models.package_request import PackageRequest from aurweb.models.user import User from aurweb.packages.util import updated_packages from aurweb.templates import make_context, render_template @@ -132,6 +136,41 @@ async def index(request: Request): # Get the 15 most recently updated packages. context["package_updates"] = updated_packages(15, updates_expire) + if request.user.is_authenticated(): + # Authenticated users get a few extra pieces of data for + # the dashboard display. + packages = db.query(Package).join(PackageBase) + + maintained = packages.join( + User, PackageBase.MaintainerUID == User.ID + ).filter( + PackageBase.MaintainerUID == request.user.ID + ) + + context["flagged_packages"] = maintained.filter( + PackageBase.OutOfDateTS.isnot(None) + ).order_by( + PackageBase.ModifiedTS.desc(), Package.Name.asc() + ).limit(50).all() + + archive_time = aurweb.config.getint('options', 'request_archive_time') + start = now - archive_time + context["package_requests"] = request.user.package_requests.filter( + PackageRequest.RequestTS >= start + ).limit(50).all() + + # Packages that the request user maintains or comaintains. + context["packages"] = maintained.order_by( + PackageBase.ModifiedTS.desc(), Package.Name.desc() + ).limit(50).all() + + # Any packages that the request user comaintains. + context["comaintained"] = packages.join( + PackageComaintainer).filter( + PackageComaintainer.UsersID == request.user.ID).order_by( + PackageBase.ModifiedTS.desc(), Package.Name.desc() + ).limit(50).all() + return render_template(request, "index.html", context) diff --git a/templates/dashboard.html b/templates/dashboard.html new file mode 100644 index 00000000..5ad89992 --- /dev/null +++ b/templates/dashboard.html @@ -0,0 +1,54 @@ +
+

{{ "Dashboard" | tr }}

+ +

{{ "My Flagged Packages" | tr }}

+ {% if not flagged_packages %} +

{{ "No packages matched your search criteria." | tr }}

+ {% else %} + {% with table_id = "flagged-packages", packages = flagged_packages %} + {% include 'partials/packages/results.html' %} + {% endwith %} + {% endif %} + +

{{ "My Requests" | tr }}

+ {% if not package_requests %} +

{{ "No requests matched your search criteria." | tr }}

+ {% else %} + {% with requests = package_requests %} + {% include 'partials/packages/requests.html' %} + {% endwith %} + {% endif %} +
+ +
+

{{ "My Packages" | tr }}

+

+ + {{ "Search for packages I maintain" | tr }} + +

+ {% if not packages %} +

{{ "No packages matched your search criteria." | tr }}

+ {% else %} + {% with table_id = "my-packages" %} + {% include 'partials/packages/results.html' %} + {% endwith %} + {% endif %} +
+ +
+

{{ "Co-Maintained Packages" | tr }}

+

+ + {{ "Search for packages I co-maintain" | tr }} + +

+ {% if not comaintained %} +

{{ "No packages matched your search criteria." | tr }}

+ {% else %} + {% with table_id = "comaintained-packages", packages = comaintained %} + {% include 'partials/packages/results.html' %} + {% endwith %} + {% endif %} +
+ diff --git a/templates/index.html b/templates/index.html index e50a99cd..0b6eda50 100644 --- a/templates/index.html +++ b/templates/index.html @@ -3,7 +3,11 @@ {% block pageContent %}
- {% include 'home.html' %} + {% if request.user.is_authenticated() %} + {% include 'dashboard.html' %} + {% else %} + {% include 'home.html' %} + {% endif %}
diff --git a/templates/partials/packages/requests.html b/templates/partials/packages/requests.html new file mode 100644 index 00000000..5239ca72 --- /dev/null +++ b/templates/partials/packages/requests.html @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + {% for request in requests %} + {% set requested = request.RequestTS | dt | as_timezone(timezone) %} + + + + + + + + + {% endfor %} + + + +
{{ "Package" | tr }}{{ "Type" | tr }}{{ "Comments" | tr }}{{ "Filed by" | tr }}{{ "Date" | tr }}{{ "Status" | tr }}
+ + {{ request.PackageBase.Name }} + + {{ request.RequestType.name_display() | tr }}{{ request.Comments }} + + {{ request.User.Username }} + + {{ requested.strftime("%Y-%m-%d %H:%M") }}{{ request.status_display() | tr }}
diff --git a/templates/partials/packages/results.html b/templates/partials/packages/results.html new file mode 100644 index 00000000..005bd5a9 --- /dev/null +++ b/templates/partials/packages/results.html @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + {% for pkg in packages %} + {% set flagged = pkg.PackageBase.OutOfDateTS %} + + + {% if flagged %} + + {% else %} + + {% endif %} + + + + + + + + {% endfor %} + +
{{ "Name" | tr }}{{ "Version" | tr }}{{ "Votes" | tr }}{{ "Popularity" | tr }}{{ "Voted" | tr }}{{ "Notify" | tr }}{{ "Description" | tr }}{{ "Maintainer" | tr }}
+ + {{ pkg.Name }} + + {{ pkg.Version }}{{ pkg.Version }}{{ pkg.PackageBase.NumVotes }} + {{ pkg.PackageBase.Popularity | number_format(2) }} + + + {% if request.user.voted_for(pkg) %} + {{ "Yes" | tr }} + {% endif %} + + + {% if request.user.notified(pkg) %} + {{ "Yes" | tr }} + {% endif %} + {{ pkg.Description or '' }} + {% set maintainer = pkg.PackageBase.Maintainer %} + + {{ maintainer.Username }} + +
diff --git a/test/test_homepage.py b/test/test_homepage.py index a629b98c..2cd6682f 100644 --- a/test/test_homepage.py +++ b/test/test_homepage.py @@ -13,10 +13,14 @@ from aurweb.asgi import app from aurweb.models.account_type import USER_ID from aurweb.models.package import Package from aurweb.models.package_base import PackageBase +from aurweb.models.package_comaintainer import PackageComaintainer +from aurweb.models.package_request import PackageRequest +from aurweb.models.request_type import DELETION_ID, RequestType from aurweb.models.user import User from aurweb.redis import redis_connection from aurweb.testing import setup_test_db from aurweb.testing.html import parse_root +from aurweb.testing.requests import Request client = TestClient(app) @@ -26,7 +30,9 @@ def setup(): yield setup_test_db( User.__tablename__, Package.__tablename__, - PackageBase.__tablename__ + PackageBase.__tablename__, + PackageComaintainer.__tablename__, + PackageRequest.__tablename__ ) @@ -149,3 +155,74 @@ def test_homepage_updates(redis, packages): for i, expected in enumerate(expectations): pkgname = updates[i].xpath('./td/a').pop(0) assert pkgname.text.strip() == expected + + +def test_homepage_dashboard(redis, packages, user): + # Create Comaintainer records for all of the packages. + for pkg in packages: + db.create(PackageComaintainer, PackageBase=pkg.PackageBase, + User=user, Priority=1, autocommit=False) + db.commit() + + cookies = {"AURSID": user.login(Request(), "testPassword")} + with client as request: + response = request.get("/", cookies=cookies) + assert response.status_code == int(HTTPStatus.OK) + + root = parse_root(response.text) + + # Assert some expectations that we end up getting all fifty + # packages in the "My Packages" table. + expectations = [f"pkg_{i}" for i in range(50 - 1, 0, -1)] + my_packages = root.xpath('//table[@id="my-packages"]/tbody/tr') + for i, expected in enumerate(expectations): + name, version, votes, pop, voted, notify, desc, maint \ + = my_packages[i].xpath('./td') + assert name.xpath('./a').pop(0).text.strip() == expected + + # Do the same for the Comaintained Packages table. + my_packages = root.xpath('//table[@id="comaintained-packages"]/tbody/tr') + for i, expected in enumerate(expectations): + name, version, votes, pop, voted, notify, desc, maint \ + = my_packages[i].xpath('./td') + assert name.xpath('./a').pop(0).text.strip() == expected + + +def test_homepage_dashboard_requests(redis, packages, user): + now = int(datetime.utcnow().timestamp()) + + pkg = packages[0] + reqtype = db.query(RequestType, RequestType.ID == DELETION_ID).first() + pkgreq = db.create(PackageRequest, PackageBase=pkg.PackageBase, + PackageBaseName=pkg.PackageBase.Name, + User=user, Comments=str(), + ClosureComment=str(), RequestTS=now, + RequestType=reqtype) + + cookies = {"AURSID": user.login(Request(), "testPassword")} + with client as request: + response = request.get("/", cookies=cookies) + assert response.status_code == int(HTTPStatus.OK) + + root = parse_root(response.text) + request = root.xpath('//table[@id="pkgreq-results"]/tbody/tr').pop(0) + pkgname = request.xpath('./td/a').pop(0) + assert pkgname.text.strip() == pkgreq.PackageBaseName + + +def test_homepage_dashboard_flagged_packages(redis, packages, user): + # Set the first Package flagged by setting its OutOfDateTS column. + pkg = packages[0] + pkg.PackageBase.OutOfDateTS = int(datetime.utcnow().timestamp()) + db.commit() + + cookies = {"AURSID": user.login(Request(), "testPassword")} + with client as request: + response = request.get("/", cookies=cookies) + assert response.status_code == int(HTTPStatus.OK) + + # Check to see that the package showed up in the Flagged Packages table. + root = parse_root(response.text) + flagged_pkg = root.xpath('//table[@id="flagged-packages"]/tbody/tr').pop(0) + flagged_name = flagged_pkg.xpath('./td/a').pop(0) + assert flagged_name.text.strip() == pkg.Name