Skip to content

Commit 851f768

Browse files
Add consent dashboard
Wires up the new consent numbers from Mavis (v03 reporting view) and displays them, similarly to vaccinations.
1 parent 1d620c0 commit 851f768

File tree

7 files changed

+619
-16
lines changed

7 files changed

+619
-16
lines changed

mavis/reporting/api_client/client.py

Lines changed: 83 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -18,23 +18,55 @@ def add_percentages(self, data: dict):
1818
n = data["cohort"]
1919

2020
if n > 0:
21-
data["vaccinated_percentage"] = data["vaccinated"] / n
22-
data["not_vaccinated_percentage"] = data["not_vaccinated"] / n
23-
data["vaccinated_by_sais_percentage"] = data["vaccinated_by_sais"] / n
24-
data["vaccinated_elsewhere_declared_percentage"] = (
25-
data["vaccinated_elsewhere_declared"] / n
26-
)
27-
data["vaccinated_elsewhere_recorded_percentage"] = (
28-
data["vaccinated_elsewhere_recorded"] / n
29-
)
30-
data["vaccinated_previously_percentage"] = data["vaccinated_previously"] / n
21+
if "vaccinated" in data:
22+
data["vaccinated_percentage"] = data["vaccinated"] / n
23+
data["not_vaccinated_percentage"] = data["not_vaccinated"] / n
24+
data["vaccinated_by_sais_percentage"] = data["vaccinated_by_sais"] / n
25+
data["vaccinated_elsewhere_declared_percentage"] = (
26+
data["vaccinated_elsewhere_declared"] / n
27+
)
28+
data["vaccinated_elsewhere_recorded_percentage"] = (
29+
data["vaccinated_elsewhere_recorded"] / n
30+
)
31+
data["vaccinated_previously_percentage"] = (
32+
data["vaccinated_previously"] / n
33+
)
34+
35+
if "consent_given" in data:
36+
data["consent_given_percentage"] = data["consent_given"] / n
37+
data["consent_no_response_percentage"] = (
38+
data["consent_no_response"] / n
39+
)
40+
data["consent_conflicts_percentage"] = data["consent_conflicts"] / n
41+
data["parent_refused_consent_percentage"] = (
42+
data["parent_refused_consent"] / n
43+
)
44+
data["child_refused_vaccination_percentage"] = (
45+
data["child_refused_vaccination"] / n
46+
)
47+
if "no_consent" in data:
48+
data["no_consent_percentage"] = data["no_consent"] / n
49+
if "consent_requested" in data:
50+
data["consent_requested_percentage"] = data["consent_requested"] / n
3151
else:
32-
data["vaccinated_percentage"] = 0
33-
data["not_vaccinated_percentage"] = 0
34-
data["vaccinated_by_sais_percentage"] = 0
35-
data["vaccinated_elsewhere_declared_percentage"] = 0
36-
data["vaccinated_elsewhere_recorded_percentage"] = 0
37-
data["vaccinated_previously_percentage"] = 0
52+
if "vaccinated" in data:
53+
data["vaccinated_percentage"] = 0
54+
data["not_vaccinated_percentage"] = 0
55+
data["vaccinated_by_sais_percentage"] = 0
56+
data["vaccinated_elsewhere_declared_percentage"] = 0
57+
data["vaccinated_elsewhere_recorded_percentage"] = 0
58+
data["vaccinated_previously_percentage"] = 0
59+
60+
if "consent_given" in data:
61+
data["consent_given_percentage"] = 0
62+
data["consent_no_response_percentage"] = 0
63+
data["consent_conflicts_percentage"] = 0
64+
data["parent_refused_consent_percentage"] = 0
65+
data["child_refused_vaccination_percentage"] = 0
66+
if "no_consent" in data:
67+
data["no_consent_percentage"] = 0
68+
if "consent_requested" in data:
69+
data["consent_requested_percentage"] = 0
3870
return data
3971

4072
def get_vaccination_data(self, filters=None):
@@ -69,6 +101,41 @@ def get_vaccination_data(self, filters=None):
69101

70102
return self.add_percentages(data)
71103

104+
def get_consent_data(self, filters=None):
105+
params = {}
106+
107+
if filters:
108+
filter_keys = [
109+
"programme",
110+
"gender",
111+
"year_group",
112+
"academic_year",
113+
"team_workgroup",
114+
"local_authority",
115+
"school_local_authority",
116+
]
117+
118+
for key in filter_keys:
119+
if key in filters:
120+
params[key] = filters[key]
121+
122+
response = mavis_helper.api_call(
123+
self.app, self.session, "/api/reporting/totals", params=params
124+
)
125+
data = parse_json_response(response, "Consent data")
126+
127+
if "cohort" not in data:
128+
raise MavisApiError(
129+
"Consent data response missing 'cohort' field",
130+
status_code=response.status_code,
131+
response_body=str(data),
132+
)
133+
134+
data["no_consent"] = data["cohort"] - data["consent_given"]
135+
data["consent_requested"] = data["cohort"] - data["consent_no_response"]
136+
137+
return self.add_percentages(data)
138+
72139
def download_totals_csv(self, programme, team_workgroup, variables=None):
73140
params = {"programme": programme, "team_workgroup": team_workgroup}
74141

mavis/reporting/helpers/secondary_nav_helper.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ def generate_secondary_nav_items(team_workgroup: str, current_page: str):
88
"href": url_for("main.vaccinations", workgroup=team_workgroup),
99
"current": current_page == "vaccinations",
1010
},
11+
{
12+
"text": "Consents",
13+
"href": url_for("main.consents", workgroup=team_workgroup),
14+
"current": current_page == "consents",
15+
},
1116
{
1217
"text": "Download data",
1318
"href": url_for("main.start_download", workgroup=team_workgroup),

mavis/reporting/jinja2_config.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
thousands,
1111
)
1212
from mavis.reporting.helpers.static_file_helper import static
13+
from mavis.reporting.helpers.text_helper import humanize
1314

1415

1516
def configure_jinja2(app):
@@ -42,5 +43,6 @@ def configure_jinja2(app):
4243
app.jinja_env.filters["percentage"] = percentage
4344
app.jinja_env.filters["format_child_count"] = format_child_count
4445
app.jinja_env.filters["date"] = format_date_string
46+
app.jinja_env.filters["humanize"] = humanize
4547

4648
return app
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
{% from "tables/macro.jinja" import table %}
2+
{% from "components/filters.jinja" import filters %}
3+
{% from "card/macro.jinja" import card %}
4+
5+
6+
{% extends 'layouts/dashboard.jinja' %}
7+
8+
{% block pageTitle %}Consent report | {{ app_title }}{% endblock %}
9+
10+
{% block dashboard_content %}
11+
12+
<div class="nhsuk-grid-row">
13+
<div class="app-grid-column-filters">
14+
{{ filters({
15+
"programmes": programmes,
16+
"year_groups": year_groups,
17+
"genders": genders,
18+
"current_filters": current_filters or {},
19+
"form_action": url_for('main.consents', workgroup=team.workgroup)
20+
}) }}
21+
</div>
22+
23+
<div id="dashboard" class="app-grid-column-content">
24+
25+
<div class="nhsuk-card-group nhsuk-grid-row app-card-group">
26+
<div class="nhsuk-grid-column-one-third nhsuk-card-group__item app-card-group__item">
27+
{{ card({
28+
"classes": "app-card app-card--blue",
29+
"heading": "Cohort",
30+
"headingClasses": "nhsuk-heading-xs",
31+
"headingLevel": 3,
32+
"descriptionHtml": "<p class='nhsuk-card__description'>" ~ data.cohort | thousands ~ "<span class='nhsuk-card__caption'>children</span></p>",
33+
}) }}
34+
</div>
35+
36+
<div class="nhsuk-grid-column-one-third nhsuk-card-group__item app-card-group__item">
37+
{{ card({
38+
"classes": "app-card app-card--red",
39+
"heading": "No consent",
40+
"headingClasses": "nhsuk-heading-xs",
41+
"headingLevel": 3,
42+
"descriptionHtml": "<p class='nhsuk-card__description'>" ~ data.no_consent_percentage | percentage ~ "<span class='nhsuk-card__caption'>" ~ data.no_consent | thousands ~ " children</span></p>",
43+
}) }}
44+
</div>
45+
46+
<div class="nhsuk-grid-column-one-third nhsuk-card-group__item app-card-group__item">
47+
{{ card({
48+
"classes": "app-card app-card--green",
49+
"heading": "Consent given",
50+
"headingClasses": "nhsuk-heading-xs",
51+
"headingLevel": 3,
52+
"descriptionHtml": "<p class='nhsuk-card__description'>" ~ data.consent_given_percentage | percentage ~ "<span class='nhsuk-card__caption'>" ~ data.consent_given | thousands ~ " children</span></p>",
53+
}) }}
54+
</div>
55+
56+
<div class="nhsuk-grid-column-one-half nhsuk-card-group__item app-card-group__item">
57+
{{ card({
58+
"classes": "app-card",
59+
"heading": "Consent requested",
60+
"headingClasses": "nhsuk-heading-xs",
61+
"headingLevel": 3,
62+
"descriptionHtml": "<p class='nhsuk-card__description'>" ~ data.consent_requested_percentage | percentage ~ "<span class='nhsuk-card__caption'>" ~ data.consent_requested | thousands ~ " children</span></p>",
63+
}) }}
64+
</div>
65+
66+
<div class="nhsuk-grid-column-one-half nhsuk-card-group__item app-card-group__item">
67+
{{ card({
68+
"classes": "app-card",
69+
"heading": "No response",
70+
"headingClasses": "nhsuk-heading-xs",
71+
"headingLevel": 3,
72+
"descriptionHtml": "<p class='nhsuk-card__description'>" ~ data.consent_no_response_percentage | percentage ~ "<span class='nhsuk-card__caption'>" ~ data.consent_no_response | thousands ~ " children</span></p>",
73+
}) }}
74+
</div>
75+
76+
<div class="nhsuk-grid-column-one-third nhsuk-card-group__item app-card-group__item">
77+
{{ card({
78+
"classes": "app-card",
79+
"heading": "Parent refused",
80+
"headingClasses": "nhsuk-heading-xs",
81+
"headingLevel": 3,
82+
"descriptionHtml": "<p class='nhsuk-card__description'>" ~ data.parent_refused_consent_percentage | percentage ~ "<span class='nhsuk-card__caption'>" ~ data.parent_refused_consent | thousands ~ " children</span></p>",
83+
}) }}
84+
</div>
85+
86+
<div class="nhsuk-grid-column-one-third nhsuk-card-group__item app-card-group__item">
87+
{{ card({
88+
"classes": "app-card",
89+
"heading": "Child refused",
90+
"headingClasses": "nhsuk-heading-xs",
91+
"headingLevel": 3,
92+
"descriptionHtml": "<p class='nhsuk-card__description'>" ~ data.child_refused_vaccination_percentage | percentage ~ "<span class='nhsuk-card__caption'>" ~ data.child_refused_vaccination | thousands ~ " children</span></p>",
93+
}) }}
94+
</div>
95+
96+
<div class="nhsuk-grid-column-one-third nhsuk-card-group__item app-card-group__item">
97+
{{ card({
98+
"classes": "app-card",
99+
"heading": "Conflicting consent",
100+
"headingClasses": "nhsuk-heading-xs",
101+
"headingLevel": 3,
102+
"descriptionHtml": "<p class='nhsuk-card__description'>" ~ data.consent_conflicts_percentage | percentage ~ "<span class='nhsuk-card__caption'>" ~ data.consent_conflicts | thousands ~ " children</span></p>",
103+
}) }}
104+
</div>
105+
</div>
106+
107+
{% set refusal_rows = [] %}
108+
{% if data.refusal_reasons %}
109+
{% for reason, count in data.refusal_reasons.items() %}
110+
{% set _ = refusal_rows.append([
111+
{ "text": reason | humanize },
112+
{ "text": count | thousands, "format": "numeric" }
113+
]) %}
114+
{% endfor %}
115+
{% else %}
116+
{% set _ = refusal_rows.append([
117+
{ "text": "No data", "colspan": 2 }
118+
]) %}
119+
{% endif %}
120+
121+
{{ table({
122+
"heading": "Reasons for parental refusal",
123+
"tableClasses": "nhsuk-table--data nhsuk-table--green",
124+
"panel": true,
125+
"head": [
126+
{ "text": "Reason"},
127+
{ "text": "Count", "format": "numeric" }
128+
],
129+
"rows": refusal_rows
130+
}) }}
131+
132+
{% set consent_rows = [] %}
133+
{% if data.consent_routes %}
134+
{% for method, count in data.consent_routes.items() %}
135+
{% set _ = consent_rows.append([
136+
{ "text": method | humanize },
137+
{ "text": count | thousands, "format": "numeric" }
138+
]) %}
139+
{% endfor %}
140+
{% else %}
141+
{% set _ = consent_rows.append([
142+
{ "text": "No data", "colspan": 2 }
143+
]) %}
144+
{% endif %}
145+
146+
{{ table({
147+
"heading": "Types of consent",
148+
"tableClasses": "nhsuk-table--data nhsuk-table--green",
149+
"panel": true,
150+
"head": [
151+
{ "text": "Method"},
152+
{ "text": "Count", "format": "numeric" }
153+
],
154+
"rows": consent_rows
155+
}) }}
156+
157+
<p class="nhsuk-body-m nhsuk-u-margin-bottom-6">
158+
This data was last updated on <strong>{{ last_updated_time }}</strong>.
159+
</p>
160+
161+
</div>
162+
</div>
163+
164+
{% endblock %}

mavis/reporting/views.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,52 @@ def vaccinations(workgroup):
182182
)
183183

184184

185+
@main.route("/team/<workgroup>/consents")
186+
@auth_helper.login_required
187+
def consents(workgroup):
188+
team = Team.get_from_session(session)
189+
if team.workgroup != workgroup:
190+
return redirect(url_for("main.consents", workgroup=team.workgroup))
191+
192+
breadcrumb_items = generate_breadcrumb_items()
193+
194+
selected_item_text = "Consents"
195+
secondary_navigation_items = generate_secondary_nav_items(
196+
team.workgroup,
197+
current_page="consents",
198+
)
199+
200+
filters = {}
201+
202+
filters["programme"] = request.args.get("programme") or "hpv"
203+
filters["team_workgroup"] = team.workgroup
204+
205+
gender_values = request.args.getlist("gender")
206+
if gender_values:
207+
filters["gender"] = gender_values
208+
209+
year_group_values = request.args.getlist("year-group")
210+
if year_group_values:
211+
filters["year_group"] = year_group_values
212+
213+
data = g.api_client.get_consent_data(filters)
214+
215+
return render_template(
216+
"consents.jinja",
217+
team=team,
218+
programmes=g.api_client.get_programmes(),
219+
year_groups=g.api_client.get_year_groups(),
220+
genders=g.api_client.get_genders(),
221+
academic_year=get_current_academic_year_range(),
222+
data=data,
223+
current_filters=filters,
224+
breadcrumb_items=breadcrumb_items,
225+
selected_item_text=selected_item_text,
226+
secondary_navigation_items=secondary_navigation_items,
227+
last_updated_time=get_last_updated_time(),
228+
)
229+
230+
185231
@main.errorhandler(404)
186232
def page_not_found(_error):
187233
return render_template("errors/404.html"), 404

0 commit comments

Comments
 (0)