Verified Commit 0c1e40a4 authored by Kevin Morris's avatar Kevin Morris
Browse files

Merge branch 'master' into live

parents 89f081a8 d7cb04b9
Pipeline #16436 passed with stages
in 7 minutes and 55 seconds
...@@ -6,7 +6,7 @@ from typing import Any ...@@ -6,7 +6,7 @@ from typing import Any
# Publicly visible version of aurweb. This is used to display # Publicly visible version of aurweb. This is used to display
# aurweb versioning in the footer and must be maintained. # aurweb versioning in the footer and must be maintained.
# Todo: Make this dynamic/automated. # Todo: Make this dynamic/automated.
AURWEB_VERSION = "v6.0.24" AURWEB_VERSION = "v6.0.25"
_parser = None _parser = None
......
...@@ -2,6 +2,7 @@ import html ...@@ -2,6 +2,7 @@ import html
import typing import typing
from http import HTTPStatus from http import HTTPStatus
from typing import Any, Dict
from fastapi import APIRouter, Form, HTTPException, Request from fastapi import APIRouter, Form, HTTPException, Request
from fastapi.responses import RedirectResponse, Response from fastapi.responses import RedirectResponse, Response
...@@ -33,6 +34,21 @@ ADDVOTE_SPECIFICS = { ...@@ -33,6 +34,21 @@ ADDVOTE_SPECIFICS = {
} }
def populate_trusted_user_counts(context: Dict[str, Any]) -> None:
tu_query = db.query(User).filter(
or_(User.AccountTypeID == TRUSTED_USER_ID,
User.AccountTypeID == TRUSTED_USER_AND_DEV_ID)
)
context["trusted_user_count"] = tu_query.count()
# In case any records have a None InactivityTS.
active_tu_query = tu_query.filter(
or_(User.InactivityTS.is_(None),
User.InactivityTS == 0)
)
context["active_trusted_user_count"] = active_tu_query.count()
@router.get("/tu") @router.get("/tu")
@requires_auth @requires_auth
async def trusted_user(request: Request, async def trusted_user(request: Request,
...@@ -40,6 +56,8 @@ async def trusted_user(request: Request, ...@@ -40,6 +56,8 @@ async def trusted_user(request: Request,
cby: str = "desc", # current by cby: str = "desc", # current by
poff: int = 0, # past offset poff: int = 0, # past offset
pby: str = "desc"): # past by pby: str = "desc"): # past by
""" Proposal listings. """
if not request.user.has_credential(creds.TU_LIST_VOTES): if not request.user.has_credential(creds.TU_LIST_VOTES):
return RedirectResponse("/", status_code=HTTPStatus.SEE_OTHER) return RedirectResponse("/", status_code=HTTPStatus.SEE_OTHER)
...@@ -102,6 +120,8 @@ async def trusted_user(request: Request, ...@@ -102,6 +120,8 @@ async def trusted_user(request: Request,
context["current_by_next"] = "asc" if current_by == "desc" else "desc" context["current_by_next"] = "asc" if current_by == "desc" else "desc"
context["past_by_next"] = "asc" if past_by == "desc" else "desc" context["past_by_next"] = "asc" if past_by == "desc" else "desc"
populate_trusted_user_counts(context)
context["q"] = { context["q"] = {
"coff": current_off, "coff": current_off,
"cby": current_by, "cby": current_by,
......
...@@ -2334,3 +2334,7 @@ msgid "This action will close any pending package requests " ...@@ -2334,3 +2334,7 @@ msgid "This action will close any pending package requests "
"related to it. If %sComments%s are omitted, a closure " "related to it. If %sComments%s are omitted, a closure "
"comment will be autogenerated." "comment will be autogenerated."
msgstr "" msgstr ""
#: templates/partials/tu/proposal/details.html
msgid "assigned"
msgstr ""
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
# #
[tool.poetry] [tool.poetry]
name = "aurweb" name = "aurweb"
version = "v6.0.24" version = "v6.0.25"
license = "GPL-2.0-only" license = "GPL-2.0-only"
description = "Source code for the Arch User Repository's website" description = "Source code for the Arch User Repository's website"
homepage = "https://aur.archlinux.org" homepage = "https://aur.archlinux.org"
......
...@@ -21,6 +21,11 @@ ...@@ -21,6 +21,11 @@
</strong> </strong>
</div> </div>
<div class="field">
{{ "Active" | tr }} {{ "Trusted Users" | tr }} {{ "assigned" | tr }}:
{{ voteinfo.ActiveTUs }}
</div>
{% set submitter = voteinfo.Submitter.Username %} {% set submitter = voteinfo.Submitter.Username %}
{% set submitter_uri = "/account/%s" | format(submitter) %} {% set submitter_uri = "/account/%s" | format(submitter) %}
{% set submitter = '<a href="%s">%s</a>' | format(submitter_uri, submitter) %} {% set submitter = '<a href="%s">%s</a>' | format(submitter_uri, submitter) %}
......
{% extends "partials/layout.html" %} {% extends "partials/layout.html" %}
{% block pageContent %} {% block pageContent %}
<div class="box">
<h2>{{ "Statistics" | tr }}</h2>
<table class="no-width">
<tbody>
<tr>
<td class="text-right">{{ "Total" | tr }} {{ "Trusted Users" | tr }}:</td>
<td>{{ trusted_user_count }}</td>
</tr>
<tr>
<td class="text-right">{{ "Active" | tr }} {{ "Trusted Users" | tr }}:</td>
<td>{{ active_trusted_user_count }}</td>
</tr>
</tbody>
</table>
</div>
{% {%
with table_class = "current-votes", with table_class = "current-votes",
total_votes = current_votes_count, total_votes = current_votes_count,
......
...@@ -267,6 +267,48 @@ def test_tu_index(client, tu_user): ...@@ -267,6 +267,48 @@ def test_tu_index(client, tu_user):
assert int(vote_id.text.strip()) == vote_records[1].ID assert int(vote_id.text.strip()) == vote_records[1].ID
def test_tu_stats(client: TestClient, tu_user: User):
cookies = {"AURSID": tu_user.login(Request(), "testPassword")}
with client as request:
response = request.get("/tu", cookies=cookies, allow_redirects=False)
assert response.status_code == HTTPStatus.OK
root = parse_root(response.text)
stats = root.xpath('//table[@class="no-width"]')[0]
rows = stats.xpath("./tbody/tr")
# We have one trusted user.
total = rows[0]
label, count = total.xpath("./td")
assert int(count.text.strip()) == 1
# And we have one active TU.
active = rows[1]
label, count = active.xpath("./td")
assert int(count.text.strip()) == 1
with db.begin():
tu_user.InactivityTS = time.utcnow()
with client as request:
response = request.get("/tu", cookies=cookies, allow_redirects=False)
assert response.status_code == HTTPStatus.OK
root = parse_root(response.text)
stats = root.xpath('//table[@class="no-width"]')[0]
rows = stats.xpath("./tbody/tr")
# We have one trusted user.
total = rows[0]
label, count = total.xpath("./td")
assert int(count.text.strip()) == 1
# But we have no more active TUs.
active = rows[1]
label, count = active.xpath("./td")
assert int(count.text.strip()) == 0
def test_tu_index_table_paging(client, tu_user): def test_tu_index_table_paging(client, tu_user):
ts = time.utcnow() ts = time.utcnow()
...@@ -515,6 +557,8 @@ def test_tu_proposal_unauthorized(client: TestClient, user: User, ...@@ -515,6 +557,8 @@ def test_tu_proposal_unauthorized(client: TestClient, user: User,
def test_tu_running_proposal(client: TestClient, def test_tu_running_proposal(client: TestClient,
proposal: Tuple[User, User, TUVoteInfo]): proposal: Tuple[User, User, TUVoteInfo]):
tu_user, user, voteinfo = proposal tu_user, user, voteinfo = proposal
with db.begin():
voteinfo.ActiveTUs = 1
# Initiate an authenticated GET request to /tu/{proposal_id}. # Initiate an authenticated GET request to /tu/{proposal_id}.
proposal_id = voteinfo.ID proposal_id = voteinfo.ID
...@@ -536,6 +580,11 @@ def test_tu_running_proposal(client: TestClient, ...@@ -536,6 +580,11 @@ def test_tu_running_proposal(client: TestClient,
'./div[contains(@class, "user")]/strong/a/text()')[0] './div[contains(@class, "user")]/strong/a/text()')[0]
assert username.strip() == user.Username assert username.strip() == user.Username
active = details.xpath('./div[contains(@class, "field")]')[1]
content = active.text.strip()
assert "Active Trusted Users assigned:" in content
assert "1" in content
submitted = details.xpath( submitted = details.xpath(
'./div[contains(@class, "submitted")]/text()')[0] './div[contains(@class, "submitted")]/text()')[0]
assert re.match(r'^Submitted: \d{4}-\d{2}-\d{2} \d{2}:\d{2} \(.+\) by$', assert re.match(r'^Submitted: \d{4}-\d{2}-\d{2} \d{2}:\d{2} \(.+\) by$',
......
...@@ -282,3 +282,16 @@ pre.traceback { ...@@ -282,3 +282,16 @@ pre.traceback {
white-space: -o-pre-wrap; white-space: -o-pre-wrap;
word-wrap: break-all; word-wrap: break-all;
} }
/* A text aligning alias. */
.text-right {
text-align: right;
}
/* By default, tables use 100% width, which we do not always want. */
table.no-width {
width: auto;
}
table.no-width > tbody > tr > td {
padding-right: 2px;
}
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