mirror of
https://gitlab.archlinux.org/archlinux/aurweb.git
synced 2025-02-03 10:43:03 +01:00
feat(FastAPI): added /requests (get) route
Introduces `aurweb.defaults` and `aurweb.filters`. `aurweb.filters` is a location developers can put their additional Jinja2 filters and/or functions. We should slowly move all of our filters over here, where it makes sense. `aurweb.defaults` is a new module which hosts some default constants and utility functions, starting with offsets (O) and per page values (PP). As far as the new GET /requests is concerned, we match up here to PHP's implementation, with some minor improvements: Improvements: * PP on this page is now configurable: 50 (default), 100, or 250. * Example: `https://localhost:8444/requests?PP=250` Modifications: * The pagination is a bit different, but serves the exact same purpose. * "Last" no longer goes to an empty page. * Closes: https://gitlab.archlinux.org/archlinux/aurweb/-/issues/14 Signed-off-by: Kevin Morris <kevr@0cost.org>
This commit is contained in:
parent
c164abe256
commit
99482f9962
11 changed files with 341 additions and 17 deletions
|
@ -31,8 +31,23 @@ class StubQuery:
|
|||
|
||||
|
||||
class AnonymousUser:
|
||||
""" A stubbed User class used when an unauthenticated User
|
||||
makes a request against FastAPI. """
|
||||
# Stub attributes used to mimic a real user.
|
||||
ID = 0
|
||||
|
||||
class AccountType:
|
||||
""" A stubbed AccountType static class. In here, we use an ID
|
||||
and AccountType which do not exist in our constant records.
|
||||
All records primary keys (AccountType.ID) should be non-zero,
|
||||
so using a zero here means that we'll never match against a
|
||||
real AccountType. """
|
||||
ID = 0
|
||||
AccountType = "Anonymous"
|
||||
|
||||
# AccountTypeID == AccountType.ID; assign a stubbed column.
|
||||
AccountTypeID = AccountType.ID
|
||||
|
||||
LangPreference = aurweb.config.get("options", "default_lang")
|
||||
Timezone = aurweb.config.get("options", "default_timezone")
|
||||
|
||||
|
|
18
aurweb/defaults.py
Normal file
18
aurweb/defaults.py
Normal file
|
@ -0,0 +1,18 @@
|
|||
""" Constant default values centralized in one place. """
|
||||
|
||||
# Default [O]ffset
|
||||
O = 0
|
||||
|
||||
# Default [P]er [P]age
|
||||
PP = 50
|
||||
|
||||
# A whitelist of valid PP values
|
||||
PP_WHITELIST = {50, 100, 250}
|
||||
|
||||
|
||||
def fallback_pp(per_page: int) -> int:
|
||||
""" If `per_page` is a valid value in PP_WHITELIST, return it.
|
||||
Otherwise, return defaults.PP. """
|
||||
if per_page not in PP_WHITELIST:
|
||||
return PP
|
||||
return per_page
|
|
@ -4,8 +4,8 @@ import paginate
|
|||
|
||||
from jinja2 import pass_context
|
||||
|
||||
from aurweb import util
|
||||
from aurweb.templates import register_filter
|
||||
from aurweb import config, util
|
||||
from aurweb.templates import register_filter, register_function
|
||||
|
||||
|
||||
@register_filter("pager_nav")
|
||||
|
@ -48,3 +48,13 @@ def pager_nav(context: Dict[str, Any],
|
|||
symbol_previous="‹ Previous",
|
||||
symbol_next="Next ›",
|
||||
symbol_last="Last »")
|
||||
|
||||
|
||||
@register_function("config_getint")
|
||||
def config_getint(section: str, key: str) -> int:
|
||||
return config.getint(section, key)
|
||||
|
||||
|
||||
@register_function("round")
|
||||
def do_round(f: float) -> int:
|
||||
return round(f)
|
||||
|
|
|
@ -2,17 +2,18 @@ from datetime import datetime
|
|||
from http import HTTPStatus
|
||||
from typing import Any, Dict
|
||||
|
||||
from fastapi import APIRouter, Form, HTTPException, Request, Response
|
||||
from fastapi import APIRouter, Form, HTTPException, Query, Request, Response
|
||||
from fastapi.responses import JSONResponse, RedirectResponse
|
||||
from sqlalchemy import and_
|
||||
from sqlalchemy import and_, case
|
||||
|
||||
import aurweb.filters
|
||||
import aurweb.models.package_comment
|
||||
import aurweb.models.package_keyword
|
||||
import aurweb.packages.util
|
||||
|
||||
from aurweb import db, l10n
|
||||
from aurweb.auth import auth_required
|
||||
from aurweb import db, defaults, l10n
|
||||
from aurweb.auth import account_type_required, auth_required
|
||||
from aurweb.models.account_type import DEVELOPER, TRUSTED_USER, TRUSTED_USER_AND_DEV
|
||||
from aurweb.models.license import License
|
||||
from aurweb.models.package import Package
|
||||
from aurweb.models.package_base import PackageBase
|
||||
|
@ -22,10 +23,11 @@ from aurweb.models.package_dependency import PackageDependency
|
|||
from aurweb.models.package_license import PackageLicense
|
||||
from aurweb.models.package_notification import PackageNotification
|
||||
from aurweb.models.package_relation import PackageRelation
|
||||
from aurweb.models.package_request import PackageRequest
|
||||
from aurweb.models.package_request import PENDING_ID, PackageRequest
|
||||
from aurweb.models.package_source import PackageSource
|
||||
from aurweb.models.package_vote import PackageVote
|
||||
from aurweb.models.relation_type import CONFLICTS_ID
|
||||
from aurweb.models.request_type import RequestType
|
||||
from aurweb.models.user import User
|
||||
from aurweb.packages.search import PackageSearch
|
||||
from aurweb.packages.util import get_pkg_or_base, get_pkgbase_comment, query_notified, query_voted
|
||||
|
@ -535,3 +537,31 @@ async def package_base_comaintainers_post(
|
|||
|
||||
return RedirectResponse(f"/pkgbase/{pkgbase.Name}",
|
||||
status_code=int(HTTPStatus.SEE_OTHER))
|
||||
|
||||
|
||||
@router.get("/requests")
|
||||
@account_type_required({TRUSTED_USER, DEVELOPER, TRUSTED_USER_AND_DEV})
|
||||
@auth_required(True, redirect="/")
|
||||
async def requests(request: Request,
|
||||
O: int = Query(default=defaults.O),
|
||||
PP: int = Query(default=defaults.PP)):
|
||||
context = make_context(request, "Requests")
|
||||
|
||||
context["q"] = dict(request.query_params)
|
||||
context["O"] = O
|
||||
context["PP"] = PP
|
||||
|
||||
# A PackageRequest query, with left inner joined User and RequestType.
|
||||
query = db.query(PackageRequest).join(
|
||||
User, PackageRequest.UsersID == User.ID
|
||||
).join(RequestType)
|
||||
|
||||
context["total"] = query.count()
|
||||
context["results"] = query.order_by(
|
||||
# Order primarily by the Status column being PENDING_ID,
|
||||
# and secondarily by RequestTS; both in descending order.
|
||||
case([(PackageRequest.Status == PENDING_ID, 1)], else_=0).desc(),
|
||||
PackageRequest.RequestTS.desc()
|
||||
).limit(PP).offset(O).all()
|
||||
|
||||
return render_template(request, "requests.html", context)
|
||||
|
|
|
@ -71,6 +71,20 @@ def register_filter(name: str) -> Callable:
|
|||
return decorator
|
||||
|
||||
|
||||
def register_function(name: str) -> Callable:
|
||||
""" A decorator that can be used to register a function.
|
||||
"""
|
||||
def decorator(func):
|
||||
@functools.wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
return func(*args, **kwargs)
|
||||
if name in _env.globals:
|
||||
raise KeyError(f"Jinja already has a function named '{name}'")
|
||||
_env.globals[name] = wrapper
|
||||
return wrapper
|
||||
return decorator
|
||||
|
||||
|
||||
def make_context(request: Request, title: str, next: str = None):
|
||||
""" Create a context for a jinja2 TemplateResponse. """
|
||||
|
||||
|
@ -83,6 +97,7 @@ def make_context(request: Request, title: str, next: str = None):
|
|||
"timezones": time.SUPPORTED_TIMEZONES,
|
||||
"title": title,
|
||||
"now": datetime.now(tz=zoneinfo.ZoneInfo(timezone)),
|
||||
"utcnow": int(datetime.utcnow().timestamp()),
|
||||
"config": aurweb.config,
|
||||
"next": next if next else request.url.path
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue