gov-uk-dashboards 21.2.2__py3-none-any.whl → 26.26.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. gov_uk_dashboards/__init__.py +1 -1
  2. gov_uk_dashboards/assets/__init__.py +1 -0
  3. gov_uk_dashboards/assets/dashboard.css +177 -0
  4. gov_uk_dashboards/assets/download-map.js +39 -0
  5. gov_uk_dashboards/assets/get_assets_folder.py +1 -0
  6. gov_uk_dashboards/assets/images/CHASE_icon.svg +17 -0
  7. gov_uk_dashboards/assets/images/explore_data_logo.svg +87 -0
  8. gov_uk_dashboards/assets/index.html +3 -0
  9. gov_uk_dashboards/assets/register_maps +15 -0
  10. gov_uk_dashboards/assets/scripts.js +4 -0
  11. gov_uk_dashboards/colours.py +23 -0
  12. gov_uk_dashboards/components/__init__.py +1 -0
  13. gov_uk_dashboards/components/dash/__init__.py +1 -3
  14. gov_uk_dashboards/components/dash/apply_and_reset_filters_buttons.py +1 -0
  15. gov_uk_dashboards/components/dash/banners.py +21 -0
  16. gov_uk_dashboards/components/dash/card.py +1 -0
  17. gov_uk_dashboards/components/dash/card_full_width.py +1 -0
  18. gov_uk_dashboards/components/dash/collapsible_panel.py +1 -0
  19. gov_uk_dashboards/components/dash/comparison_la_filter_button.py +1 -0
  20. gov_uk_dashboards/components/dash/context_banner.py +2 -1
  21. gov_uk_dashboards/components/dash/context_card.py +978 -0
  22. gov_uk_dashboards/components/dash/data_quality_banner.py +91 -0
  23. gov_uk_dashboards/components/dash/details.py +1 -0
  24. gov_uk_dashboards/components/dash/download_button.py +22 -36
  25. gov_uk_dashboards/components/dash/filter_panel.py +1 -0
  26. gov_uk_dashboards/components/dash/footer.py +81 -27
  27. gov_uk_dashboards/components/dash/graph.py +1 -0
  28. gov_uk_dashboards/components/dash/green_button.py +25 -0
  29. gov_uk_dashboards/components/dash/header.py +62 -9
  30. gov_uk_dashboards/components/dash/heading.py +8 -5
  31. gov_uk_dashboards/components/dash/home_page_link_button.py +9 -8
  32. gov_uk_dashboards/components/dash/html_list.py +1 -0
  33. gov_uk_dashboards/components/dash/key_value_pair.py +1 -0
  34. gov_uk_dashboards/components/dash/main_content.py +25 -2
  35. gov_uk_dashboards/components/dash/notification_banner.py +9 -5
  36. gov_uk_dashboards/components/dash/paragraph.py +1 -0
  37. gov_uk_dashboards/components/dash/phase_banner.py +7 -4
  38. gov_uk_dashboards/components/dash/row_component.py +1 -0
  39. gov_uk_dashboards/components/dash/table.py +62 -124
  40. gov_uk_dashboards/components/dash/tooltip.py +2 -1
  41. gov_uk_dashboards/components/dash/tooltip_title.py +2 -1
  42. gov_uk_dashboards/components/dash/visualisation_commentary.py +1 -0
  43. gov_uk_dashboards/components/dash/visualisation_title.py +1 -0
  44. gov_uk_dashboards/components/dash/warning_text.py +1 -0
  45. gov_uk_dashboards/components/helpers/display_chart_or_table_with_header.py +61 -12
  46. gov_uk_dashboards/components/helpers/get_chart_for_download.py +18 -15
  47. gov_uk_dashboards/components/helpers/plotting_helper_functions.py +0 -1
  48. gov_uk_dashboards/components/leaflet/leaflet_choropleth_map.py +108 -31
  49. gov_uk_dashboards/components/plotly/captioned_figure.py +6 -3
  50. gov_uk_dashboards/components/plotly/enums.py +2 -0
  51. gov_uk_dashboards/components/plotly/stacked_barchart.py +166 -73
  52. gov_uk_dashboards/components/plotly/time_series_chart.py +159 -20
  53. gov_uk_dashboards/constants.py +35 -1
  54. gov_uk_dashboards/figures/__init__.py +4 -2
  55. gov_uk_dashboards/figures/enums/__init__.py +1 -0
  56. gov_uk_dashboards/figures/enums/dash_patterns.py +1 -0
  57. gov_uk_dashboards/figures/line_chart.py +71 -71
  58. gov_uk_dashboards/figures/styles/__init__.py +1 -0
  59. gov_uk_dashboards/figures/styles/line_style.py +1 -0
  60. gov_uk_dashboards/formatting/human_readable.py +1 -0
  61. gov_uk_dashboards/formatting/number_formatting.py +14 -0
  62. gov_uk_dashboards/formatting/round_and_add_prefix_and_suffix.py +1 -0
  63. gov_uk_dashboards/formatting/text_functions.py +11 -0
  64. gov_uk_dashboards/lib/dap/dap_deployment.py +1 -0
  65. gov_uk_dashboards/lib/dap/get_dataframe_from_cds.py +96 -95
  66. gov_uk_dashboards/lib/datetime_functions/datetime_functions.py +118 -0
  67. gov_uk_dashboards/lib/download_functions/download_csv_with_headers.py +106 -83
  68. gov_uk_dashboards/lib/http_headers.py +10 -2
  69. gov_uk_dashboards/lib/logging.py +1 -0
  70. gov_uk_dashboards/lib/testing_functions/__init__.py +0 -0
  71. gov_uk_dashboards/lib/testing_functions/barchart_data_test_assertions.py +48 -0
  72. gov_uk_dashboards/lib/testing_functions/data_test_assertions.py +124 -0
  73. gov_uk_dashboards/lib/testing_functions/data_test_helper_functions.py +257 -0
  74. gov_uk_dashboards/lib/testing_functions/timeseries_data_test_assertions.py +29 -0
  75. gov_uk_dashboards/lib/warning_text_sensitive.py +44 -0
  76. gov_uk_dashboards/log_kpi.py +37 -0
  77. gov_uk_dashboards/symbols.py +1 -0
  78. gov_uk_dashboards/template.html +37 -0
  79. gov_uk_dashboards/template.py +14 -3
  80. {gov_uk_dashboards-21.2.2.dist-info → gov_uk_dashboards-26.26.0.dist-info}/METADATA +6 -7
  81. gov_uk_dashboards-26.26.0.dist-info/RECORD +128 -0
  82. {gov_uk_dashboards-21.2.2.dist-info → gov_uk_dashboards-26.26.0.dist-info}/WHEEL +1 -1
  83. gov_uk_dashboards/axes.py +0 -21
  84. gov_uk_dashboards/figures/chart_data.py +0 -24
  85. gov_uk_dashboards-21.2.2.dist-info/RECORD +0 -113
  86. {gov_uk_dashboards-21.2.2.dist-info → gov_uk_dashboards-26.26.0.dist-info}/licenses/LICENSE +0 -0
  87. {gov_uk_dashboards-21.2.2.dist-info → gov_uk_dashboards-26.26.0.dist-info}/top_level.txt +0 -0
@@ -1,109 +1,15 @@
1
1
  """Function for creating a table component from a dataframe"""
2
+
2
3
  from typing import Optional
3
- from pandas import DataFrame
4
+ import polars as pl
4
5
  from dash import html, dcc
5
6
  from gov_uk_dashboards.components.dash.card import card
6
7
  from gov_uk_dashboards.components.dash.paragraph import paragraph
7
-
8
- # from gov_uk_dashboards.components.plotly.row_component import row_component
9
-
10
-
11
- def table_from_dataframe(
12
- dataframe: DataFrame,
13
- title: Optional[str] = None,
14
- first_column_is_header: bool = True,
15
- title_is_subtitle: bool = False,
16
- short_table: bool = True,
17
- last_row_unbolded: bool = False,
18
- format_column_headers_as_markdown: bool = False,
19
- **table_properties,
20
- ): # pylint: disable=too-many-arguments
21
- # pylint: disable=too-many-positional-arguments
22
- """
23
- Displays a pandas DataFrame as a table formatted in the Gov.UK style
24
-
25
- Part of the Gov.UK Design System:
26
- https://design-system.service.gov.uk/components/table/
27
-
28
-
29
- Args:
30
- dataframe (DataFrame): Dataframe containing formatted data to display.
31
- title (str, optional): Title to display above the table. Defaults to None.
32
- first_column_is_header (bool, optional): Sets if the first column is a header column.
33
- Defaults to True.
34
- title_is_subtitle (bool, optional): Sets if the title should be displayed as a subtitle
35
- or full title. Defaults to False.
36
- short_table: (bool, optional): if False the header of the table will scroll with window.
37
- last_row_unbolded: (bool, optional): Sets if the last row should not be bolded if
38
- first_column_is_header is True. Defaults to False.
39
- format_column_headers_as_markdown: (bool, optional): Sets if the column headers should
40
- be formatted as markdown. Defaults to False.
41
- **table_properties: Any additional arguments for the html.Table object,
42
- such as setting a width or id.
43
-
44
- Returns:
45
- html.Table: The dash HTML object for the table.
46
- """
47
- table_contents = []
48
-
49
- if title:
50
- table_contents.append(
51
- html.Caption(
52
- title,
53
- className="govuk-table__caption govuk-table__caption--s"
54
- if title_is_subtitle
55
- else "govuk-table__caption govuk-table__caption--m",
56
- )
57
- )
58
-
59
- table_contents.append(
60
- html.Thead(
61
- html.Tr(
62
- [
63
- html.Th(
64
- dcc.Markdown(header),
65
- scope="col",
66
- className="govuk-table__header",
67
- )
68
- if format_column_headers_as_markdown
69
- else html.Th(header, scope="col", className="govuk-table__header")
70
- for header in dataframe.columns
71
- ],
72
- className="govuk-table__row",
73
- ),
74
- className="govuk-table__head-short" if short_table else "govuk-table__head",
75
- )
76
- )
77
-
78
- last_row_index = len(dataframe) - 1 if last_row_unbolded else len(dataframe)
79
- table_contents.append(
80
- html.Tbody(
81
- [
82
- html.Tr(
83
- [html.Th(row.iloc[0], scope="row", className="govuk-table__header")]
84
- + [html.Td(cell, className="govuk-table__cell") for cell in row[1:]]
85
- )
86
- if first_column_is_header and index != last_row_index
87
- else html.Tr(
88
- [html.Td(cell, className="govuk-table__cell") for cell in row]
89
- )
90
- for index, row in dataframe.iterrows()
91
- ],
92
- className="govuk-table__body",
93
- )
94
- )
95
-
96
- return html.Table(
97
- table_contents,
98
- className="govuk-table",
99
- id="table",
100
- role="table",
101
- **table_properties,
102
- )
8
+ from gov_uk_dashboards.formatting.text_functions import create_id_from_string
103
9
 
104
10
 
105
11
  def table_from_polars_dataframe(
106
- dataframe: DataFrame,
12
+ dataframe: pl.DataFrame,
107
13
  title: Optional[str] = None,
108
14
  subtitle: Optional[str] = None,
109
15
  first_column_is_header: bool = True,
@@ -112,6 +18,7 @@ def table_from_polars_dataframe(
112
18
  format_column_headers_as_markdown: bool = False,
113
19
  sortable_headers: bool = False,
114
20
  table_id: str = "table",
21
+ assign_ids_to_rows: bool = False,
115
22
  table_footer: str = None,
116
23
  column_widths: Optional[list[str]] = None,
117
24
  columns_to_right_align: Optional[list[str]] = None,
@@ -144,6 +51,7 @@ def table_from_polars_dataframe(
144
51
  sortable_headers: (bool, optional): Sets if the column headers should be sortable. Defaults
145
52
  to False.
146
53
  table_id: (str, optional): ID for the table Defaults to "table".
54
+ assign_ids_to_rows: (bool, optional): Adds id's to table rows when True. Defaults to False.
147
55
  table_footer: (str, optional): Text to display underneath table as footer.
148
56
  column_widths: (list[str], optional): Determines width of table columns. Format as a list,
149
57
  "x%". List must be same length as dataframe columns. Defaults to None.
@@ -205,17 +113,26 @@ def table_from_polars_dataframe(
205
113
  # If sortable_headers is True, use a button for the header with sorting
206
114
  # functionality
207
115
  html.Th(
208
- html.Button(
209
- dcc.Markdown(header)
210
- if format_column_headers_as_markdown
211
- else header,
212
- id={"type": f"{table_id}-header-button", "index": idx},
213
- n_clicks=0,
214
- )
215
- if header not in non_sortable_columns
216
- else dcc.Markdown(header)
217
- if format_column_headers_as_markdown
218
- else header,
116
+ (
117
+ html.Button(
118
+ (
119
+ dcc.Markdown(header)
120
+ if format_column_headers_as_markdown
121
+ else header
122
+ ),
123
+ id={
124
+ "type": f"{table_id}-header-button",
125
+ "index": idx,
126
+ },
127
+ n_clicks=0,
128
+ )
129
+ if header not in non_sortable_columns
130
+ else (
131
+ dcc.Markdown(header)
132
+ if format_column_headers_as_markdown
133
+ else header
134
+ )
135
+ ),
219
136
  **(
220
137
  {"aria-sort": sorted_header_dict.get(header, "none")}
221
138
  if header
@@ -227,7 +144,7 @@ def table_from_polars_dataframe(
227
144
  style={
228
145
  **({"width": width} if width else {}),
229
146
  **(
230
- {"text-align": "right"}
147
+ {"textAlign": "right"}
231
148
  if header in columns_to_right_align
232
149
  else {}
233
150
  ),
@@ -235,15 +152,17 @@ def table_from_polars_dataframe(
235
152
  )
236
153
  if sortable_headers
237
154
  else html.Th(
238
- dcc.Markdown(header)
239
- if format_column_headers_as_markdown
240
- else header,
155
+ (
156
+ dcc.Markdown(header)
157
+ if format_column_headers_as_markdown
158
+ else header
159
+ ),
241
160
  scope="col",
242
161
  className="govuk-table__header",
243
162
  style={
244
163
  **({"width": width} if width else {}),
245
164
  **(
246
- {"text-align": "right"}
165
+ {"textAlign": "right"}
247
166
  if header in columns_to_right_align
248
167
  else {}
249
168
  ),
@@ -260,8 +179,8 @@ def table_from_polars_dataframe(
260
179
  style={
261
180
  "position": "sticky",
262
181
  "top": 0,
263
- "z-index": 1,
264
- "background-color": "#fff",
182
+ "zIndex": 1,
183
+ "backgroundColor": "#fff",
265
184
  },
266
185
  )
267
186
  )
@@ -278,12 +197,19 @@ def table_from_polars_dataframe(
278
197
  html.Td(
279
198
  cell,
280
199
  className="govuk-table__cell",
281
- style={"text-align": "right"}
282
- if column_name in columns_to_right_align
283
- else {},
200
+ style=(
201
+ {"textAlign": "right"}
202
+ if column_name in columns_to_right_align
203
+ else {}
204
+ ),
284
205
  )
285
206
  for cell, column_name in zip(row[1:], dataframe.columns[1:])
286
- ]
207
+ ],
208
+ **(
209
+ {"id": create_id_from_string(row[0])}
210
+ if assign_ids_to_rows and create_id_from_string(row[0])
211
+ else {}
212
+ ),
287
213
  )
288
214
  if first_column_is_header and index != last_row_index
289
215
  else html.Tr(
@@ -291,12 +217,24 @@ def table_from_polars_dataframe(
291
217
  html.Td(
292
218
  cell,
293
219
  className="govuk-table__cell",
294
- style={"text-align": "right"}
295
- if column_name in columns_to_right_align
296
- else {},
220
+ style=(
221
+ {"textAlign": "right"}
222
+ if column_name in columns_to_right_align
223
+ else {}
224
+ ),
297
225
  )
298
226
  for cell, column_name in zip(row, dataframe.columns)
299
- ]
227
+ ],
228
+ **(
229
+ {
230
+ "id": (
231
+ create_id_from_string(row[0])
232
+ if assign_ids_to_rows
233
+ and create_id_from_string(row[0])
234
+ else {}
235
+ )
236
+ }
237
+ ),
300
238
  )
301
239
  )
302
240
  for index, row in enumerate(dataframe.rows())
@@ -1,4 +1,5 @@
1
1
  """tooltip"""
2
+
2
3
  from dash import html
3
4
  import dash_bootstrap_components as dbc
4
5
 
@@ -25,7 +26,7 @@ def tooltip(
25
26
  tooltip_text,
26
27
  target=tooltip_id,
27
28
  class_name="tooltip tooltiptext",
28
- style={"color": color, "font-size": font_size},
29
+ style={"color": color, "fontSize": font_size},
29
30
  ),
30
31
  ],
31
32
  id=tooltip_id,
@@ -1,4 +1,5 @@
1
1
  """tooltip_title"""
2
+
2
3
  from dash import html
3
4
  import dash_bootstrap_components as dbc
4
5
 
@@ -23,7 +24,7 @@ def tooltip_title(title, tooltiptext):
23
24
  tooltiptext,
24
25
  target="tooltip-target",
25
26
  class_name="tooltip tooltiptext",
26
- style={"color": "white", "font-size": 12, "width": "10px"},
27
+ style={"color": "white", "fontSize": 12, "width": "10px"},
27
28
  ),
28
29
  ]
29
30
  )
@@ -1,4 +1,5 @@
1
1
  """format_visualisation_commentary"""
2
+
2
3
  from dash import html
3
4
 
4
5
 
@@ -1,4 +1,5 @@
1
1
  """format_visualisation_title"""
2
+
2
3
  from dash import html
3
4
 
4
5
 
@@ -1,4 +1,5 @@
1
1
  """warning_text"""
2
+
2
3
  from dash import html
3
4
 
4
5
 
@@ -18,7 +18,13 @@ def display_chart_or_table_with_header(
18
18
  sub_heading: str = None,
19
19
  download_button_id: str = None,
20
20
  download_data_button_id: str = None,
21
+ download_map_button_id: str = None,
22
+ download_all_data_button_id: str = None,
23
+ alternative_data_button_text: str = None,
24
+ alternative_all_data_button_text: str = None,
21
25
  footnote: str = None,
26
+ instance=1,
27
+ text_below_subheading: str = None,
22
28
  ) -> html.Div:
23
29
  """Generate the wrapping information/header for a chart or table.
24
30
 
@@ -29,7 +35,17 @@ def display_chart_or_table_with_header(
29
35
  download_button_id (str, optional): id for download button if required. Defaults to None.
30
36
  if None then the button will not be included.
31
37
  download_data_button_id (str, optional): the id to be applied to the download data button.
38
+ download_map_button_id (str, optional): the id to be applied to the download map button.
39
+ download_all_data_button_id (str, optional): the id to be applied to the download all data
40
+ button
41
+ alternative_data_button_text (str, optional): Optional alternative to button text
42
+ "Download data"
43
+ alternative_all_data_button_text (str, optional): Optional alternative to button text
44
+ "Download all data"
32
45
  footnote (str, optional): the footnote to be added to charts and downloads.
46
+ instance (int or str): Optional additional paramter for id dict.
47
+ text_below_subheading (str, optional): Optional text to go below subheading but above
48
+ chart_or_table.
33
49
 
34
50
  Returns:
35
51
  html.Div: Div containing Header and chart/table.
@@ -54,26 +70,59 @@ def display_chart_or_table_with_header(
54
70
  ),
55
71
  ]
56
72
  ),
73
+ paragraph(text_below_subheading),
57
74
  chart_or_table,
58
- html.Div([paragraph(footnote)], style={"padding-top": "20px"}),
75
+ html.Div([paragraph(footnote)], style={"paddingTop": "20px"}),
59
76
  html.Div(
60
77
  (
61
78
  [
62
79
  html.Div(
63
80
  [
64
- create_download_button_with_icon(
65
- "Download chart", download_button_id
66
- )
67
- if download_button_id
68
- else [],
69
- create_download_button_with_icon(
70
- "Download data", download_data_button_id
71
- )
72
- if download_data_button_id
73
- else [],
81
+ (
82
+ create_download_button_with_icon(
83
+ "Download chart", download_button_id, instance
84
+ )
85
+ if download_button_id
86
+ else []
87
+ ),
88
+ (
89
+ create_download_button_with_icon(
90
+ "Download map", download_map_button_id, instance
91
+ )
92
+ if download_map_button_id
93
+ else []
94
+ ),
95
+ (
96
+ create_download_button_with_icon(
97
+ (
98
+ alternative_data_button_text
99
+ if alternative_data_button_text
100
+ else "Download data"
101
+ ),
102
+ download_data_button_id,
103
+ instance,
104
+ "data",
105
+ )
106
+ if download_data_button_id
107
+ else []
108
+ ),
109
+ (
110
+ create_download_button_with_icon(
111
+ (
112
+ alternative_all_data_button_text
113
+ if alternative_all_data_button_text
114
+ else "Download all data"
115
+ ),
116
+ download_all_data_button_id,
117
+ instance,
118
+ "data",
119
+ )
120
+ if download_all_data_button_id
121
+ else []
122
+ ),
74
123
  ],
75
124
  className="govuk-button-group",
76
- style={"padding-top": "20px"},
125
+ style={"paddingTop": "20px"},
77
126
  ),
78
127
  ]
79
128
  ),
@@ -1,4 +1,5 @@
1
1
  """get_chart_for_download"""
2
+
2
3
  from gov_uk_dashboards.constants import MAIN_TITLE, SUBTITLE
3
4
 
4
5
 
@@ -17,20 +18,22 @@ def get_chart_for_download(self, fig):
17
18
  title_subtitle={
18
19
  "text": f"<b><span style='color: black; margin: 0px 0px 5px'>{subtitle}</span></b>"
19
20
  },
20
- annotations=[
21
- {
22
- "text": f"<span style='color: black; font-size: 18px;'>{footnote}</span>",
23
- "x": -0.01,
24
- "y": -0.4, # Bottom of the plotting area
25
- "xref": "paper",
26
- "yref": "paper",
27
- "xanchor": "left",
28
- "yanchor": "top",
29
- "showarrow": False,
30
- "align": "left",
31
- }
32
- ]
33
- if footnote is not None
34
- else [],
21
+ annotations=(
22
+ [
23
+ {
24
+ "text": f"<span style='color: black; fontSize: 18px;'>{footnote}</span>",
25
+ "x": -0.01,
26
+ "y": -0.4, # Bottom of the plotting area
27
+ "xref": "paper",
28
+ "yref": "paper",
29
+ "xanchor": "left",
30
+ "yanchor": "top",
31
+ "showarrow": False,
32
+ "align": "left",
33
+ }
34
+ ]
35
+ if footnote is not None
36
+ else []
37
+ ),
35
38
  )
36
39
  return fig
@@ -1,6 +1,5 @@
1
1
  """Helper functions for use to plot charts"""
2
2
 
3
-
4
3
  from gov_uk_dashboards.constants import CHART_LABEL_FONT_SIZE
5
4
 
6
5