-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.py
183 lines (159 loc) · 6.47 KB
/
index.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
"""
Create paths to serve different dashboards. Add new paths in the display_page callback.
"""
import logging
import os
from dash import dcc, html, Input, Output, State
from gov_uk_dashboards.components.plotly.filter_panel import hidden_filter
from gov_uk_dashboards.components.plotly.banners import message_banner
from gov_uk_dashboards.components.plotly.dashboard_container import dashboard_container
from gov_uk_dashboards.components.plotly.phase_banner import phase_banner_with_feedback
from gov_uk_dashboards.components.plotly.footer import footer
from gov_uk_dashboards.components.plotly.side_navbar import side_navbar
from gov_uk_dashboards.components.plotly.header import header
from app import app
from dashboards.template_dashboard import template_dashboard
from dashboards.accessibility_statement import accessibility_statement
from lib.dashboard_page import DashboardPage
from lib.dashboard_storage_and_lookup import DashboardStorageAndLookup
from lib.generate_navbar import generate_side_navbar
from lib.url import selected_filters, dict_to_query_string
app.title = "Template Dashboard" # UPDATE
app.layout = html.Div(
[
html.A(
"Skip to main content",
href="#main-content",
className="govuk-skip-link",
),
header(app.title),
html.Div(
id="nav-section",
style={"display": "none"},
),
html.Div(
[
phase_banner_with_feedback(
phase="alpha",
# #UPDATE Add an e-mail address for people to provide feedback.
feedback_link="mailto:<contact e-mail address>?"
f"subject=Feedback on {app.title}",
link_id="feedback-link",
),
html.Div(
[
dcc.Location(id="url", refresh=False),
message_banner(
category="UPDATE",
message="This is an example update banner",
),
html.Div(id="page-content"),
],
className="govuk-main-wrapper--auto-spacing govuk-!-padding-top-2",
),
],
className="govuk-width-container",
**{"aria-live": "polite", "aria-atomic": "true"},
),
footer([dcc.Link("Accessibility statement", href="/accessibility")]),
]
)
dashboards = DashboardStorageAndLookup()
dashboards.add_dashboards(
[
DashboardPage(
title="Dashboard 1",
pathname="/dashboard-1",
function_to_call=template_dashboard,
filters=["example_dropdown"],
),
DashboardPage(
title="Accessibility Statement",
pathname="/accessibility",
function_to_call=accessibility_statement,
filters=[],
hide_from_menu=True,
),
]
)
all_filters = ["example_dropdown"]
@app.callback(
Output("protective-marking", "children"),
Output("page-content", "children"),
Output("nav-section", "children"),
Output("feedback-link", "href"),
Input("url", "pathname"),
State("url", "search"),
)
def display_page(pathname, query_string):
"""Show the user the correct dashboard for the given path"""
try:
dashboard = dashboards.get_dashboard_from_pathname(pathname)
hidden_filters = create_missing_filters_for_dashboard(dashboard)
side_nav_links = generate_side_navbar(dashboards, pathname, query_string)
return (
dashboard.protective_marking,
dashboard_container(
[
side_navbar(side_nav_links, identifier="navigation-items"),
dashboard.display_dashboard(query_string),
hidden_filters,
]
),
side_navbar(
side_nav_links,
identifier="mobile-navigation-items",
nav_id="mobile-nav-section",
),
"mailto:<contact e-mail address>?" # #UPDATE Add an e-mail address
# for people to provide feedback.
f"subject=Feedback on {app.title} - {dashboard.title}",
)
except Exception as exception:
if os.environ.get("STAGE") == "production":
dashboard = dashboards.error_dashboard
logging.exception(exception)
return (
dashboard.protective_marking,
dashboard_container([dashboard.display_dashboard(query_string)]),
[],
"mailto:<contact e-mail address>?" # #UPDATE Add an e-mail address
# for people to provide feedback.
f"subject=Feedback on {app.title} - {dashboard.title}",
)
raise exception
def create_missing_filters_for_dashboard(dashboard):
"""Finds filters missing from the dashboard & creates div containing matching hidden filters"""
return html.Div(
[
hidden_filter(filter_name)
for filter_name in all_filters
if filter_name not in dashboard.filters
]
)
@app.callback(
Output(component_id="url", component_property="search"),
Output(component_id="mobile-navigation-items", component_property="children"),
Output(component_id="navigation-items", component_property="children"),
State(component_id="url", component_property="pathname"),
State(component_id="url", component_property="search"),
Input(component_id="submit-button", component_property="n_clicks"),
[
State(component_id=filter_name, component_property="value")
for filter_name in all_filters
],
)
def update_url(
pathname, query_string, filters_submitted, *filter_values
): # pylint: disable= unused-argument
"""When the user changes any filter panel elements, update the URL query parameters"""
dashboard = dashboards.get_dashboard_from_pathname(pathname)
# We're having to access the query string so that we can only update the value that has changed
# in the dropdown, otherwise we lose those filters which created dynamically.
params = selected_filters(query_string)
for filter_name, filter_value in zip(all_filters, filter_values):
if filter_name in dashboard.filters:
params[filter_name] = filter_value
query_string = dict_to_query_string(**params)
nav_bar = generate_side_navbar(dashboards, pathname, query_string)
return query_string, nav_bar, nav_bar