feat: Add "Requests" filter option for package name

- Add package name textbox for filtering requests (with auto-suggest)
- Make "x pending requests" a link for TU/Dev on the package details page

Signed-off-by: moson-mo <mo-son@mailbox.org>
This commit is contained in:
moson-mo 2023-05-08 18:22:16 +02:00
parent acdb2864de
commit f24fae0ce6
No known key found for this signature in database
GPG key ID: 4A4760AB4EE15296
7 changed files with 109 additions and 8 deletions

View file

@ -40,13 +40,21 @@ async def requests(
filter_accepted: bool = False,
filter_rejected: bool = False,
filter_maintainer_requests: bool = False,
filter_pkg_name: str = None,
):
context = make_context(request, "Requests")
context["q"] = dict(request.query_params)
# Set pending filter by default if no status filter was provided.
# In case we got a package name filter, but no status filter,
# we enable the other ones too.
if not dict(request.query_params).keys() & FILTER_PARAMS:
filter_pending = True
if filter_pkg_name:
filter_closed = True
filter_accepted = True
filter_rejected = True
O, PP = util.sanitize_params(str(O), str(PP))
context["O"] = O
@ -56,6 +64,7 @@ async def requests(
context["filter_accepted"] = filter_accepted
context["filter_rejected"] = filter_rejected
context["filter_maintainer_requests"] = filter_maintainer_requests
context["filter_pkg_name"] = filter_pkg_name
Maintainer = orm.aliased(User)
# A PackageRequest query
@ -78,7 +87,7 @@ async def requests(
rejected_count = 0 + query.filter(PackageRequest.Status == REJECTED_ID).count()
context["rejected_requests"] = rejected_count
# Apply filters
# Apply status filters
in_filters = []
if filter_pending:
in_filters.append(PENDING_ID)
@ -89,6 +98,11 @@ async def requests(
if filter_rejected:
in_filters.append(REJECTED_ID)
filtered = query.filter(PackageRequest.Status.in_(in_filters))
# Name filter
if filter_pkg_name:
filtered = filtered.filter(PackageBase.Name == filter_pkg_name)
# Additionally filter for requests made from package maintainer
if filter_maintainer_requests:
filtered = filtered.filter(PackageRequest.UsersID == PackageBase.MaintainerUID)

View file

@ -2362,3 +2362,7 @@ msgstr ""
#: templates/partials/packages/comment_form.html
msgid "Cancel"
msgstr ""
#: templates/requests.html
msgid "Package name"
msgstr ""

View file

@ -0,0 +1,6 @@
document.addEventListener('DOMContentLoaded', function() {
const input = document.getElementById('id_filter_pkg_name');
const form = document.getElementById('todolist_filter');
const type = 'suggest-pkgbase';
typeahead.init(type, input, form);
});

View file

@ -97,11 +97,19 @@
</li>
{% endif %}
{% if requests %}
<li>
<span class="flagged">
{{ requests | tn("%d pending request", "%d pending requests") | format(requests) }}
</span>
</li>
{% if request.user.has_credential(creds.PKGREQ_LIST) %}
<li>
<a class="flagged" href="/requests?filter_pkg_name={{ pkgbase.Name }}">
{{ requests | tn("%d pending request", "%d pending requests") | format(requests) }}
</a>
</li>
{% else %}
<li>
<span class="flagged">
{{ requests | tn("%d pending request", "%d pending requests") | format(requests) }}
</span>
</li>
{% endif %}
{% endif %}
<li>
<a href="/pkgbase/{{ pkgbase.Name }}/request?{{ {'next': '/pkgbase/%s' | format(pkgbase.Name)} | urlencode }}">

View file

@ -62,7 +62,13 @@
value="True" {{ "checked" if filter_maintainer_requests == true }}/>
</div>
<div>
<button type='submit' class='button' name='submit' value='Filter'>{{ "Filter" | tr }}</button>
<label for="id_filter_pkg_name" title="Package name">{{ "Package name" | tr }}</label>
<input type="text" name="filter_pkg_name" id="id_filter_pkg_name" autocomplete="off"
value="{{ filter_pkg_name if filter_pkg_name }}"/>
</div>
<div>
<br/>
<input type="submit" class="button" name="filter" value="{{ 'Filter' | tr }}"></input>
</div>
</fieldset>
</form>
@ -194,4 +200,7 @@
{% include "partials/pager.html" %}
{% endif %}
</div>
<!-- Bootstrap typeahead for requests page. -->
<script type="text/javascript" src="/static/js/typeahead-requests.js"></script>
{% endblock %}

View file

@ -555,6 +555,16 @@ def test_package_authenticated(client: TestClient, user: User, package: Package)
for expected_text in expected:
assert expected_text in resp.text
# make sure we don't have these. Only for Maintainer/TUs/Devs
not_expected = [
"Disown Package",
"View Requests",
"Delete Package",
"Merge Package",
]
for unexpected_text in not_expected:
assert unexpected_text not in resp.text
# When no requests are up, make sure we don't see the display for them.
root = parse_root(resp.text)
selector = '//div[@id="actionlist"]/ul/li/span[@class="flagged"]'
@ -586,8 +596,19 @@ def test_package_authenticated_maintainer(
for expected_text in expected:
assert expected_text in resp.text
# make sure we don't have these. Only for TUs/Devs
not_expected = [
"1 pending request",
"Delete Package",
"Merge Package",
]
for unexpected_text in not_expected:
assert unexpected_text not in resp.text
def test_package_authenticated_tu(client: TestClient, tu_user: User, package: Package):
def test_package_authenticated_tu(
client: TestClient, tu_user: User, package: Package, pkgreq: PackageRequest
):
cookies = {"AURSID": tu_user.login(Request(), "testPassword")}
with client as request:
request.cookies = cookies
@ -603,6 +624,7 @@ def test_package_authenticated_tu(client: TestClient, tu_user: User, package: Pa
"Vote for this package",
"Enable notifications",
"Manage Co-Maintainers",
"1 pending request",
"Submit Request",
"Delete Package",
"Merge Package",

View file

@ -912,6 +912,44 @@ def test_requests_for_maintainer_requests(
assert len(rows) == 2
def test_requests_with_package_name_filter(
client: TestClient,
tu_user: User,
user2: User,
packages: list[Package],
requests: list[PackageRequest],
):
# test as TU
cookies = {"AURSID": tu_user.login(Request(), "testPassword")}
with client as request:
request.cookies = cookies
resp = request.get(
"/requests",
params={"filter_pkg_name": packages[0].PackageBase.Name},
)
assert resp.status_code == int(HTTPStatus.OK)
root = parse_root(resp.text)
rows = root.xpath('//table[@class="results"]/tbody/tr')
# We only expect 1 request for our first package
assert len(rows) == 1
# test as regular user, not related to our package
cookies = {"AURSID": user2.login(Request(), "testPassword")}
with client as request:
request.cookies = cookies
resp = request.get(
"/requests",
params={"filter_pkg_name": packages[0].PackageBase.Name},
)
assert resp.status_code == int(HTTPStatus.OK)
root = parse_root(resp.text)
rows = root.xpath('//table[@class="results"]/tbody/tr')
# We don't expect to get any requests
assert len(rows) == 0
def test_requests_by_deleted_users(
client: TestClient, user: User, tu_user: User, pkgreq: PackageRequest
):