Verified Commit ed41a4fe authored by Kevin Morris's avatar Kevin Morris
Browse files

feat: add paging to package depends & required by



This patch does not include a javascript implementating, but
provides a pure HTML/HTTP method of paging through these lists.

Also fixes erroneous limiting. We now use a hardcoded limit of 20
by default.
Signed-off-by: Kevin Morris's avatarKevin Morris <kevr@0cost.org>
parent d8564e44
......@@ -214,7 +214,7 @@ def query_notified(query: List[models.Package],
return output
def pkg_required(pkgname: str, provides: List[str], limit: int) \
def pkg_required(pkgname: str, provides: List[str]) \
-> List[PackageDependency]:
"""
Get dependencies that match a string in `[pkgname] + provides`.
......@@ -227,8 +227,8 @@ def pkg_required(pkgname: str, provides: List[str], limit: int) \
targets = set([pkgname] + provides)
query = db.query(PackageDependency).join(Package).filter(
PackageDependency.DepName.in_(targets)
).order_by(Package.Name.asc()).limit(limit)
return query.all()
).order_by(Package.Name.asc())
return query
@register_filter("source_uri")
......
......@@ -11,15 +11,24 @@ from aurweb.models.package_request import PENDING_ID, PackageRequest
from aurweb.models.package_vote import PackageVote
from aurweb.scripts import notify
from aurweb.templates import make_context as _make_context
from aurweb.templates import make_variable_context as _make_variable_context
def make_context(request: Request, pkgbase: PackageBase) -> Dict[str, Any]:
async def make_variable_context(request: Request, pkgbase: PackageBase) \
-> Dict[str, Any]:
ctx = await _make_variable_context(request, pkgbase.Name)
return make_context(request, pkgbase, ctx)
def make_context(request: Request, pkgbase: PackageBase,
context: Dict[str, Any] = None) -> Dict[str, Any]:
""" Make a basic context for package or pkgbase.
:param request: FastAPI request
:param pkgbase: PackageBase instance
:return: A pkgbase context without specific differences
"""
if not context:
context = _make_context(request, pkgbase.Name)
context["git_clone_uri_anon"] = config.get("options", "git_clone_uri_anon")
......
......@@ -2,7 +2,7 @@ from collections import defaultdict
from http import HTTPStatus
from typing import Any, Dict, List
from fastapi import APIRouter, Form, Request, Response
from fastapi import APIRouter, Form, Query, Request, Response
import aurweb.filters # noqa: F401
......@@ -33,7 +33,7 @@ async def packages_get(request: Request, context: Dict[str, Any],
context["O"] = offset
# Limit PP to options.max_search_results
max_search_results = aurweb.config.getint("options", "max_search_results")
max_search_results = config.getint("options", "max_search_results")
context["PP"] = per_page = min(per_page, max_search_results)
# Query search by.
......@@ -123,7 +123,22 @@ async def packages(request: Request) -> Response:
@router.get("/packages/{name}")
async def package(request: Request, name: str) -> Response:
async def package(request: Request, name: str,
all_deps: bool = Query(default=False),
all_reqs: bool = Query(default=False)) -> Response:
"""
Get a package by name.
By default, we limit the number of depends and requires results
to 20. To bypass this and load all of them, which should be triggered
via a "Show more" link near the limited listing.
:param name: Package.Name
:param all_deps: Boolean indicating whether we should load all depends
:param all_reqs: Boolean indicating whether we should load all requires
:return: FastAPI Response
"""
# Get the Package.
pkg = get_pkg_or_base(name, models.Package)
pkgbase = pkg.PackageBase
......@@ -139,23 +154,41 @@ async def package(request: Request, name: str) -> Response:
rels_data["r"].append(rel)
# Add our base information.
context = pkgbaseutil.make_context(request, pkgbase)
context = await pkgbaseutil.make_variable_context(request, pkgbase)
context.update(
{
"all_deps": all_deps,
"all_reqs": all_reqs
}
)
context["package"] = pkg
# Package sources.
context["sources"] = pkg.package_sources.order_by(
models.PackageSource.Source.asc()).all()
# Listing metadata.
context["max_listing"] = max_listing = 20
# Package dependencies.
max_depends = config.getint("options", "max_depends")
context["dependencies"] = pkg.package_dependencies.order_by(
deps = pkg.package_dependencies.order_by(
models.PackageDependency.DepTypeID.asc(),
models.PackageDependency.DepName.asc()
).limit(max_depends).all()
)
context["depends_count"] = deps.count()
if not all_deps:
deps = deps.limit(max_listing)
context["dependencies"] = deps.all()
# Package requirements (other packages depend on this one).
context["required_by"] = pkgutil.pkg_required(
pkg.Name, [p.RelName for p in rels_data.get("p", [])], max_depends)
reqs = pkgutil.pkg_required(
pkg.Name, [p.RelName for p in rels_data.get("p", [])])
context["reqs_count"] = reqs.count()
if not all_reqs:
reqs = reqs.limit(max_listing)
context["required_by"] = reqs.all()
context["licenses"] = pkg.package_licenses
......
......@@ -100,6 +100,8 @@ async def make_variable_context(request: Request, title: str, next: str = None):
for k, v in to_copy.items():
context[k] = v
context["q"] = dict(request.query_params)
return context
......
......@@ -2338,3 +2338,11 @@ msgstr ""
#: templates/partials/tu/proposal/details.html
msgid "assigned"
msgstr ""
#: templaets/partials/packages/package_metadata.html
msgid "Show %d more"
msgstr ""
#: templates/partials/packages/package_metadata.html
msgid "dependencies"
msgstr ""
<div id="pkgdeps" class="listing">
<h3>{{ "Dependencies" | tr }} ({{ dependencies | length }})</h3>
<h3>{{ "Dependencies" | tr }} ({{ depends_count }})</h3>
<ul id="pkgdepslist">
{% for dep in dependencies %}
{# Collect provides for `dep`. #}
......@@ -34,11 +34,18 @@
{% endif %}
</li>
{% endfor %}
{% if not all_deps and depends_count > max_listing %}
<li>
<a href="/packages/{{ package.Name }}?{{ q | extend_query(['all_deps', '1']) | urlencode }}#pkgdeps">
{{ "Show %d more" | tr | format(depends_count - (dependencies | length)) }} {{ "dependencies" | tr }}...
</a>
</li>
{% endif %}
</ul>
</div>
<div id="pkgreqs" class="listing">
<h3>{{ "Required by" | tr }} ({{ required_by | length }})</h3>
<h3>{{ "Required by" | tr }} ({{ reqs_count }})</h3>
<ul id="pkgreqslist">
{% for dep in required_by %}
<li>
......@@ -55,6 +62,11 @@
<em>{{ dep | dep_extra }}</em>
</li>
{% endfor %}
{% if not all_reqs and (required_by | length) > max_listing %}
<a href="/packages/{{ name }}?{{ q | extend_query(['all_reqs', '1']) | urlencode }}#pkgreqs">
{{ "Show %d more" | tr | format(reqs_count - (required_by | length)) }}...
</a>
{% endif %}
</ul>
</div>
......
......@@ -276,6 +276,51 @@ def test_package(client: TestClient, package: Package):
assert conflicts[0].text.strip() == ", ".join(expected)
def paged_depends_required(client: TestClient, package: Package):
maint = package.PackageBase.Maintainer
new_pkgs = []
with db.begin():
# Create 25 new packages that'll be used to depend on our package.
for i in range(26):
base = db.create(PackageBase, Name=f"new_pkg{i}", Maintainer=maint)
new_pkgs.append(db.create(Package, Name=base.Name))
# Create 25 deps.
for i in range(25):
create_package_dep(package, f"dep_{i}")
with db.begin():
# Create depends on this package so we get some required by listings.
for new_pkg in new_pkgs:
create_package_dep(new_pkg, package.Name)
with client as request:
resp = request.get(package_endpoint(package))
assert resp.status_code == int(HTTPStatus.OK)
# Test depends show link.
assert "Show 5 more" in resp.text
# Test required by show more link, we added 26 packages.
assert "Show 6 more" in resp.text
# Follow both links at the same time.
with client as request:
resp = request.get(
package_endpoint(package),
params={
"all_deps": True,
"all_reqs": True,
}
)
assert resp.status_code == int(HTTPStatus.OK)
# We're should see everything and have no link.
assert "Show 5 more" not in resp.text
assert "Show 6 more" not in resp.text
def test_package_comments(client: TestClient, user: User, package: Package):
now = (time.utcnow())
with db.begin():
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment