Skip to content

Commit 04d66a5

Browse files
authored
Print disability access request dashboard for admins (#10822)
* Add basic print-disability access dashboard w/ admin menu link Updates the query to return the totals, by status. These are used to populate a summary section above the dashboard table. Table styling has been improved. Full name of qualifying organizations are now included in the table (as opposed to their identifiers).
1 parent c715c1c commit 04d66a5

File tree

9 files changed

+227
-1
lines changed

9 files changed

+227
-1
lines changed

openlibrary/core/pd.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
from . import db
2+
3+
4+
def make_pd_request_query():
5+
oldb = db.get_db()
6+
7+
query = """
8+
WITH pda_key AS (
9+
SELECT id AS key_id FROM property WHERE name = 'notifications.pda'
10+
),
11+
status_key AS (
12+
SELECT id AS key_id FROM property WHERE name = 'notifications.rpd'
13+
),
14+
combined AS (
15+
-- Per-qualifying authority rows
16+
SELECT
17+
pda.value AS pda,
18+
COUNT(CASE WHEN status.value = 0 THEN 1 END) AS requested,
19+
COUNT(CASE WHEN status.value = 1 THEN 1 END) AS emailed,
20+
COUNT(CASE WHEN status.value = 2 THEN 1 END) AS fulfilled,
21+
1 AS sort_order
22+
FROM
23+
datum_str AS pda
24+
JOIN
25+
datum_int AS status ON pda.thing_id = status.thing_id
26+
JOIN
27+
pda_key ON pda.key_id = pda_key.key_id
28+
JOIN
29+
status_key ON status.key_id = status_key.key_id
30+
GROUP BY
31+
pda.value
32+
33+
UNION ALL
34+
35+
-- Total row
36+
SELECT
37+
'TOTAL' AS pda,
38+
COUNT(CASE WHEN status.value = 0 THEN 1 END),
39+
COUNT(CASE WHEN status.value = 1 THEN 1 END),
40+
COUNT(CASE WHEN status.value = 2 THEN 1 END),
41+
0 AS sort_order
42+
FROM
43+
datum_str AS pda
44+
JOIN
45+
datum_int AS status ON pda.thing_id = status.thing_id
46+
JOIN
47+
pda_key ON pda.key_id = pda_key.key_id
48+
JOIN
49+
status_key ON status.key_id = status_key.key_id
50+
)
51+
52+
SELECT
53+
pda,
54+
requested,
55+
emailed,
56+
fulfilled
57+
FROM
58+
combined
59+
ORDER BY
60+
sort_order, pda
61+
"""
62+
63+
return list(oldb.query(query))

openlibrary/i18n/messages.pot

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3328,7 +3328,8 @@ msgstr ""
33283328
msgid "New Books Imported Per Day"
33293329
msgstr ""
33303330

3331-
#: admin/imports.html admin/imports_by_date.html site/stats.html
3331+
#: admin/imports.html admin/imports_by_date.html admin/pd_dashboard.html
3332+
#: site/stats.html
33323333
msgid "Summary"
33333334
msgstr ""
33343335

@@ -3579,6 +3580,10 @@ msgstr ""
35793580
msgid "Admin Center"
35803581
msgstr ""
35813582

3583+
#: admin/menu.html
3584+
msgid "PD Access Requests"
3585+
msgstr ""
3586+
35823587
#: admin/menu.html
35833588
msgid "Block IPs"
35843589
msgstr ""
@@ -3603,6 +3608,10 @@ msgstr ""
36033608
msgid "Inspect memcache"
36043609
msgstr ""
36053610

3611+
#: admin/pd_dashboard.html
3612+
msgid "Print Disability Access Requests"
3613+
msgstr ""
3614+
36063615
#: admin/permissions.html
36073616
msgid "[Admin Center] Site Permissions"
36083617
msgstr ""

openlibrary/plugins/admin/code.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
imports,
3434
)
3535
from openlibrary.core.models import Work
36+
from openlibrary.plugins.openlibrary.pd import get_pd_dashboard_data
3637
from openlibrary.plugins.upstream import forms, spamcheck
3738

3839
logger = logging.getLogger("openlibrary.admin")
@@ -829,6 +830,13 @@ def GET(self):
829830
return f.read()
830831

831832

833+
class pd_dashboard:
834+
def GET(self):
835+
dashboard_data = get_pd_dashboard_data()
836+
837+
return render_template("admin/pd_dashboard", dashboard_data)
838+
839+
832840
def setup():
833841
register_admin_page('/admin/git-pull', gitpull, label='git-pull')
834842
register_admin_page('/admin/reload', reload, label='Reload Templates')
@@ -862,6 +870,7 @@ def setup():
862870
r'/admin/imports/(\d\d\d\d-\d\d-\d\d)', imports_by_date, label=""
863871
)
864872
register_admin_page('/admin/spamwords', spamwords, label="")
873+
register_admin_page("/admin/pd", pd_dashboard)
865874

866875
from openlibrary.plugins.admin import mem
867876

openlibrary/plugins/openlibrary/pd.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
from infogami import config
66
from openlibrary.core import cache
7+
from openlibrary.core.pd import make_pd_request_query
78
from openlibrary.i18n import gettext as _
89
from openlibrary.utils.dateutil import DAY_SECS
910

@@ -72,5 +73,29 @@ def cached_pd_org_query() -> list:
7273
return results
7374

7475

76+
def get_pd_dashboard_data() -> dict:
77+
def enrich_data(_request_data):
78+
for d in _request_data:
79+
pda = d['pda']
80+
d['display_name'] = (
81+
"No Qualifying Authority Selected"
82+
if pda == "unqualified"
83+
else get_pd_org(pda)['title']
84+
)
85+
86+
request_data = make_pd_request_query()
87+
totals = request_data and request_data.pop(0)
88+
enrich_data(request_data)
89+
return {
90+
"data": request_data,
91+
"totals": {
92+
'requested': totals['requested'],
93+
'emailed': totals['emailed'],
94+
'fulfilled': totals['fulfilled'],
95+
'total': totals['requested'] + totals['emailed'] + totals['fulfilled'],
96+
},
97+
}
98+
99+
75100
def setup():
76101
pass

openlibrary/templates/admin/menu.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
<div class="superNav" id="adminLinks">
1212
$:link("/admin", _("Admin Center"))
1313
| $:link("/admin/people", _("People"))
14+
| $:link("/admin/pd", _("PD Access Requests"))
1415
| $:link("/admin/block", _("Block IPs"))
1516
| $:link("/admin/spamwords", _("Spam Words"))
1617
| $:link("/admin/solr", _("Solr"))
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
$def with(dashboard_data)
2+
3+
$code:
4+
request_data = dashboard_data.get('data', [])
5+
totals = dashboard_data.get('totals')
6+
requested_total = 0
7+
emailed_total = 0
8+
fulfilled_total = 0
9+
10+
<div id="contentHead">
11+
$:render_template("admin/menu")
12+
<h1>$_("Print Disability Access Requests")</h1>
13+
</div>
14+
15+
<div id="contentBody" class="pd-access-dashboard">
16+
<h2>$_("Summary")</h2>
17+
<div class="dashboard-summary">
18+
<div class="pd-access-stat">
19+
Total number of patrons awaiting follow-up email: <span class="pd-access-total">$totals['requested']</span>
20+
</div>
21+
<hr>
22+
<div class="pd-access-stat">
23+
Total number of patrons emailed: <span class="pd-access-total">$totals['emailed']</span>
24+
</div>
25+
<hr>
26+
<div class="pd-access-stat">
27+
Total number of patron requests fulfilled: <span class="pd-access-total">$totals['fulfilled']</span>
28+
</div>
29+
<hr>
30+
<div class="pd-access-stat">
31+
Total patrons that have requested print-disability access: <span class="pd-access-total">$totals['total']</span>
32+
</div>
33+
</div>
34+
35+
<h2>Breakdown by Qualifying Authority</h2>
36+
<table class="pd-request-table">
37+
<thead>
38+
<th class="table-header" scope="col">PDA</th>
39+
<th class="table-header" scope="col">Requested</th>
40+
<th class="table-header" scope="col">Emailed</th>
41+
<th class="table-header" scope="col">Fulfilled</th>
42+
<th class="table-header" scope="col">Total</th>
43+
</thead>
44+
<tbody>
45+
$for d in request_data:
46+
$ pda = d['pda']
47+
$ display_name = d['display_name']
48+
$ requested_count = d['requested']
49+
$ emailed_count = d['emailed']
50+
$ fulfilled_count = d['fulfilled']
51+
$ total = requested_count + emailed_count + fulfilled_count
52+
$ requested_total += requested_count
53+
$ emailed_total += emailed_count
54+
$ fulfilled_total += fulfilled_count
55+
<tr>
56+
<td>
57+
<a href="https://archive.org/details/$(pda)">$display_name</a>
58+
</td>
59+
<td>$requested_count</td>
60+
<td>$emailed_count</td>
61+
<td>$fulfilled_count</td>
62+
<td>$total</td>
63+
</tr>
64+
<tr class="totals">
65+
<td>Totals</td>
66+
<td>$requested_total</td>
67+
<td>$emailed_total</td>
68+
<td>$fulfilled_total</td>
69+
<td>$(requested_total + emailed_total + fulfilled_total)</td>
70+
</tr>
71+
</tbody>
72+
</table>
73+
</div>

scripts/detect_missing_i18n.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
"static/status-500.html",
2525
# Uses jsdef and the current stance is no i18n in JS.
2626
"openlibrary/templates/jsdef/LazyAuthorPreview.html",
27+
# Admin-only dashboard
28+
"openlibrary/templates/admin/pd_dashboard.html",
2729
}
2830

2931
default_directories = ('openlibrary/templates/', 'openlibrary/macros/')
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
.pd-access-dashboard {
2+
.dashboard-summary {
3+
margin-bottom: 40px;
4+
5+
.pd-access-stat {
6+
display: flex;
7+
justify-content: space-between;
8+
}
9+
}
10+
11+
.pd-request-table {
12+
border: 1px solid @black;
13+
width: 100%;
14+
/* stylelint-disable selector-max-specificity */
15+
.table-header,
16+
tr.totals {
17+
font-weight: 700;
18+
}
19+
/* stylelint-enable selector-max-specificity */
20+
.table-header {
21+
background: @white;
22+
text-align: center;
23+
padding: 8px;
24+
}
25+
td {
26+
padding: 4px 8px;
27+
}
28+
/* stylelint-disable selector-max-specificity */
29+
td:not(:first-child) {
30+
text-align: right;
31+
}
32+
/* stylelint-enable selector-max-specificity */
33+
tr {
34+
background: @white;
35+
}
36+
/* stylelint-disable selector-max-specificity */
37+
tr:nth-child(odd) {
38+
background: @lightest-grey;
39+
}
40+
/* stylelint-enable selector-max-specificity */
41+
}
42+
}

static/css/page-admin.less

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,3 +227,5 @@ div.sparkDisplay {
227227
height: 50px;
228228
}
229229
}
230+
231+
@import (less) "components/pd-dashboard.less";

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy