li-nguyen commited on
Commit
dae8a3d
·
1 Parent(s): 2f4b096

Update version

Browse files

- Update vizro version
- Update app configuration to sync with repo
- Update CSS variable names

app.py CHANGED
@@ -1,16 +1,17 @@
1
  """Example app to show all features of Vizro."""
2
 
3
  from time import sleep
4
- from typing import List, Literal, Optional
5
 
 
6
  import pandas as pd
7
  import plotly.graph_objects as go
8
  import vizro.models as vm
9
  import vizro.plotly.express as px
10
- from dash import dash_table, html, get_asset_url
11
- import dash_bootstrap_components as dbc
12
  from vizro import Vizro
13
  from vizro.actions import export_data, filter_interaction
 
14
  from vizro.models.types import capture
15
  from vizro.tables import dash_ag_grid, dash_data_table
16
 
@@ -26,6 +27,70 @@ waterfall_df = pd.DataFrame(
26
  "y": [60, 80, 0, -40, -20, 0],
27
  }
28
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
 
30
  # HOME ------------------------------------------------------------------------
31
  home = vm.Page(
@@ -38,7 +103,7 @@ home = vm.Page(
38
 
39
  ### Components
40
 
41
- Main components of Vizro include **charts**, **tables**, **cards**, **containers**,
42
  **buttons** and **tabs**.
43
  """,
44
  href="/graphs",
@@ -91,35 +156,42 @@ graphs = vm.Page(
91
  title="Graphs",
92
  components=[
93
  vm.Graph(
94
- figure=px.scatter_matrix(
95
- iris,
96
- dimensions=["sepal_length", "sepal_width", "petal_length", "petal_width"],
97
- color="species",
98
- )
99
- )
 
 
 
 
 
100
  ],
101
- controls=[vm.Filter(column="species", selector=vm.Dropdown(title="Species"))],
102
  )
103
 
104
  ag_grid = vm.Page(
105
  title="AG Grid",
106
  components=[
107
  vm.AgGrid(
108
- title="Dash AG Grid", figure=dash_ag_grid(data_frame=gapminder_2007, dashGridOptions={"pagination": True})
 
 
 
109
  )
110
  ],
111
- controls=[vm.Filter(column="continent")],
112
  )
113
 
114
  table = vm.Page(
115
  title="Table",
116
  components=[
117
  vm.Table(
118
- title="Dash DataTable",
119
  figure=dash_data_table(data_frame=gapminder_2007),
 
 
 
120
  )
121
  ],
122
- controls=[vm.Filter(column="continent")],
123
  )
124
 
125
  cards = vm.Page(
@@ -181,6 +253,14 @@ cards = vm.Page(
181
  ],
182
  )
183
 
 
 
 
 
 
 
 
 
184
  button = vm.Page(
185
  title="Button",
186
  layout=vm.Layout(grid=[[0], [0], [0], [0], [1]]),
@@ -465,7 +545,7 @@ def scatter_with_line(data_frame, x, y, hline=None, title=None):
465
 
466
 
467
  @capture("graph")
468
- def waterfall(data_frame, measure, x, y, text, title=None): # noqa: PLR0913
469
  """Custom waterfall chart based on go."""
470
  fig = go.Figure()
471
  fig.add_traces(
@@ -521,15 +601,15 @@ custom_charts = vm.Page(
521
 
522
  # CUSTOM TABLE ------------------------------------------------------------------
523
  @capture("table")
524
- def my_custom_table(data_frame=None, chosen_columns: Optional[List[str]] = None):
525
  """Custom table with added logic to filter on chosen columns."""
526
  columns = [{"name": i, "id": i} for i in chosen_columns]
527
  defaults = {
528
  "style_as_list_view": True,
529
- "style_data": {"border_bottom": "1px solid var(--border-subtle-alpha-01)", "height": "40px"},
530
  "style_header": {
531
- "border_bottom": "1px solid var(--state-overlays-selected-hover)",
532
- "border_top": "1px solid var(--main-container-bg-color)",
533
  "height": "32px",
534
  },
535
  }
@@ -654,11 +734,51 @@ custom_actions = vm.Page(
654
  controls=[vm.Filter(column="species", selector=vm.Dropdown(title="Species"))],
655
  )
656
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
657
  # DASHBOARD -------------------------------------------------------------------
658
- components = [graphs, ag_grid, table, cards, button, containers, tabs]
659
  controls = [filters, parameters, selectors]
660
  actions = [export_data_action, chart_interaction]
661
- extensions = [custom_charts, custom_tables, custom_components, custom_actions]
662
 
663
  dashboard = vm.Dashboard(
664
  title="Vizro Features",
@@ -670,10 +790,25 @@ dashboard = vm.Dashboard(
670
  vm.NavLink(
671
  label="Features",
672
  pages={
673
- "Components": ["Graphs", "AG Grid", "Table", "Cards", "Button", "Containers", "Tabs"],
 
 
 
 
 
 
 
 
 
674
  "Controls": ["Filters", "Parameters", "Selectors"],
675
  "Actions": ["Export data", "Chart interaction"],
676
- "Extensions": ["Custom Charts", "Custom Tables", "Custom Components", "Custom Actions"],
 
 
 
 
 
 
677
  },
678
  icon="Library Add",
679
  ),
@@ -682,17 +817,16 @@ dashboard = vm.Dashboard(
682
  ),
683
  )
684
 
685
- app = Vizro().build(dashboard)
686
- app.dash.layout.children.append(
687
- dbc.NavLink(
688
- ["Made with ", html.Img(src=get_asset_url("images/logo.svg"), id="banner", alt="Vizro logo"), "vizro"],
689
- href="https://github.com/mckinsey/vizro",
690
- target="_blank",
691
- external_link=True,
692
- className="anchor-container",
693
- )
694
- )
695
- server = app.dash.server
696
 
697
- if __name__ == "__main__":
 
 
 
 
 
 
 
 
 
 
698
  app.run()
 
1
  """Example app to show all features of Vizro."""
2
 
3
  from time import sleep
4
+ from typing import Literal, Optional
5
 
6
+ import dash_bootstrap_components as dbc
7
  import pandas as pd
8
  import plotly.graph_objects as go
9
  import vizro.models as vm
10
  import vizro.plotly.express as px
11
+ from dash import dash_table, dcc, get_asset_url, html
 
12
  from vizro import Vizro
13
  from vizro.actions import export_data, filter_interaction
14
+ from vizro.figures import kpi_card, kpi_card_reference
15
  from vizro.models.types import capture
16
  from vizro.tables import dash_ag_grid, dash_data_table
17
 
 
27
  "y": [60, 80, 0, -40, -20, 0],
28
  }
29
  )
30
+ custom_fig_df = pd.DataFrame(
31
+ {
32
+ "text": [
33
+ "Lorem ipsum dolor sit amet, consetetur sadipscing no sea elitr sed diam nonumy.",
34
+ "Sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat.",
35
+ "Sed diam voluptua. At vero eos et accusam et justo no duo dolores et ea rebum.",
36
+ "Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.",
37
+ "Lorem ipsum dolor sit amet, consetetur sadipscing no sea est elitr dolor sit amet.",
38
+ "Sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat.",
39
+ ]
40
+ * 2
41
+ }
42
+ )
43
+
44
+ df_kpi = pd.DataFrame({"Actual": [100, 200, 700], "Reference": [100, 300, 500], "Category": ["A", "B", "C"]})
45
+
46
+ example_cards = [
47
+ kpi_card(data_frame=df_kpi, value_column="Actual", title="KPI with value"),
48
+ kpi_card(data_frame=df_kpi, value_column="Actual", title="KPI with aggregation", agg_func="median"),
49
+ kpi_card(
50
+ data_frame=df_kpi,
51
+ value_column="Actual",
52
+ title="KPI with formatting",
53
+ value_format="${value:.2f}",
54
+ ),
55
+ kpi_card(
56
+ data_frame=df_kpi,
57
+ value_column="Actual",
58
+ title="KPI with icon",
59
+ icon="shopping_cart",
60
+ ),
61
+ ]
62
+
63
+ example_reference_cards = [
64
+ kpi_card_reference(
65
+ data_frame=df_kpi,
66
+ value_column="Actual",
67
+ reference_column="Reference",
68
+ title="KPI reference (pos)",
69
+ ),
70
+ kpi_card_reference(
71
+ data_frame=df_kpi,
72
+ value_column="Actual",
73
+ reference_column="Reference",
74
+ agg_func="median",
75
+ title="KPI reference (neg)",
76
+ ),
77
+ kpi_card_reference(
78
+ data_frame=df_kpi,
79
+ value_column="Actual",
80
+ reference_column="Reference",
81
+ title="KPI reference with formatting",
82
+ value_format="{value:.2f}$",
83
+ reference_format="{delta:.2f}$ vs. last year ({reference:.2f}$)",
84
+ ),
85
+ kpi_card_reference(
86
+ data_frame=df_kpi,
87
+ value_column="Actual",
88
+ reference_column="Reference",
89
+ title="KPI reference with icon",
90
+ icon="shopping_cart",
91
+ ),
92
+ ]
93
+
94
 
95
  # HOME ------------------------------------------------------------------------
96
  home = vm.Page(
 
103
 
104
  ### Components
105
 
106
+ Main components of Vizro include **charts**, **tables**, **cards**, **figures**, **containers**,
107
  **buttons** and **tabs**.
108
  """,
109
  href="/graphs",
 
156
  title="Graphs",
157
  components=[
158
  vm.Graph(
159
+ figure=px.scatter(iris, x="sepal_width", y="sepal_length", color="species"),
160
+ title="Relationships between Sepal Width and Sepal Length",
161
+ header="""
162
+ Each point in the scatter plot represents one of the 150 iris flowers, with colors indicating their
163
+ types. The Setosa type is easily identifiable by its short and wide sepals.
164
+
165
+ However, there is still overlap between the Versicolor and Virginica types when considering only sepal
166
+ width and length.
167
+ """,
168
+ footer="""SOURCE: **Plotly iris data set, 2024**""",
169
+ ),
170
  ],
 
171
  )
172
 
173
  ag_grid = vm.Page(
174
  title="AG Grid",
175
  components=[
176
  vm.AgGrid(
177
+ figure=dash_ag_grid(data_frame=gapminder_2007, dashGridOptions={"pagination": True}),
178
+ title="Gapminder Data Insights",
179
+ header="""#### An Interactive Exploration of Global Health, Wealth, and Population""",
180
+ footer="""SOURCE: **Plotly gapminder data set, 2024**""",
181
  )
182
  ],
 
183
  )
184
 
185
  table = vm.Page(
186
  title="Table",
187
  components=[
188
  vm.Table(
 
189
  figure=dash_data_table(data_frame=gapminder_2007),
190
+ title="Gapminder Data Insights",
191
+ header="""#### An Interactive Exploration of Global Health, Wealth, and Population""",
192
+ footer="""SOURCE: **Plotly gapminder data set, 2024**""",
193
  )
194
  ],
 
195
  )
196
 
197
  cards = vm.Page(
 
253
  ],
254
  )
255
 
256
+ figure = vm.Page(
257
+ title="Figure",
258
+ layout=vm.Layout(grid=[[0, 1, 2, 3], [4, 5, 6, 7], [-1, -1, -1, -1], [-1, -1, -1, -1]]),
259
+ components=[vm.Figure(figure=figure) for figure in example_cards + example_reference_cards],
260
+ controls=[vm.Filter(column="Category")],
261
+ )
262
+
263
+
264
  button = vm.Page(
265
  title="Button",
266
  layout=vm.Layout(grid=[[0], [0], [0], [0], [1]]),
 
545
 
546
 
547
  @capture("graph")
548
+ def waterfall(data_frame, measure, x, y, text, title=None):
549
  """Custom waterfall chart based on go."""
550
  fig = go.Figure()
551
  fig.add_traces(
 
601
 
602
  # CUSTOM TABLE ------------------------------------------------------------------
603
  @capture("table")
604
+ def my_custom_table(data_frame=None, chosen_columns: Optional[list[str]] = None):
605
  """Custom table with added logic to filter on chosen columns."""
606
  columns = [{"name": i, "id": i} for i in chosen_columns]
607
  defaults = {
608
  "style_as_list_view": True,
609
+ "style_data": {"border_bottom": "1px solid var(--border-subtleAlpha01)", "height": "40px"},
610
  "style_header": {
611
+ "border_bottom": "1px solid var(--stateOverlays-selectedHover)",
612
+ "border_top": "1px solid var(--right-side-bg)",
613
  "height": "32px",
614
  },
615
  }
 
734
  controls=[vm.Filter(column="species", selector=vm.Dropdown(title="Species"))],
735
  )
736
 
737
+
738
+ # CUSTOM FIGURE ----------------------------------------------------------------
739
+ @capture("figure") # (1)!
740
+ def multiple_cards(data_frame: pd.DataFrame, n_rows: Optional[int] = 1) -> html.Div:
741
+ """Creates a list with a variable number of `vm.Card` components from the provided data_frame.
742
+
743
+ Args:
744
+ data_frame: Data frame containing the data.
745
+ n_rows: Number of rows to use from the data_frame. Defaults to 1.
746
+
747
+ Returns:
748
+ html.Div with a list of dbc.Card objects generated from the data.
749
+
750
+ """
751
+ texts = data_frame.head(n_rows)["text"]
752
+ return html.Div(
753
+ [dbc.Card(dcc.Markdown(f"### Card #{i}\n{text}")) for i, text in enumerate(texts, 1)],
754
+ className="multiple-cards-container",
755
+ )
756
+
757
+
758
+ custom_figures = vm.Page(
759
+ title="Custom Figures",
760
+ components=[vm.Figure(id="my-figure", figure=multiple_cards(data_frame=custom_fig_df))],
761
+ controls=[
762
+ vm.Parameter(
763
+ targets=["my-figure.n_rows"],
764
+ selector=vm.Slider(min=2, max=12, step=2, value=8, title="Number of cards to display"),
765
+ ),
766
+ ],
767
+ )
768
+
769
+ kpi_indicators = vm.Page(
770
+ title="KPI Indicators",
771
+ layout=vm.Layout(grid=[[0, 1, 2, 3], [4, 5, 6, 7], [-1, -1, -1, -1], [-1, -1, -1, -1]]),
772
+ components=[vm.Figure(figure=figure) for figure in example_cards + example_reference_cards],
773
+ controls=[vm.Filter(column="Category")],
774
+ )
775
+
776
+
777
  # DASHBOARD -------------------------------------------------------------------
778
+ components = [graphs, ag_grid, table, cards, figure, button, containers, tabs]
779
  controls = [filters, parameters, selectors]
780
  actions = [export_data_action, chart_interaction]
781
+ extensions = [custom_charts, custom_tables, custom_components, custom_actions, custom_figures]
782
 
783
  dashboard = vm.Dashboard(
784
  title="Vizro Features",
 
790
  vm.NavLink(
791
  label="Features",
792
  pages={
793
+ "Components": [
794
+ "Graphs",
795
+ "AG Grid",
796
+ "Table",
797
+ "Cards",
798
+ "Figure",
799
+ "Button",
800
+ "Containers",
801
+ "Tabs",
802
+ ],
803
  "Controls": ["Filters", "Parameters", "Selectors"],
804
  "Actions": ["Export data", "Chart interaction"],
805
+ "Extensions": [
806
+ "Custom Charts",
807
+ "Custom Tables",
808
+ "Custom Components",
809
+ "Custom Actions",
810
+ "Custom Figures",
811
+ ],
812
  },
813
  icon="Library Add",
814
  ),
 
817
  ),
818
  )
819
 
 
 
 
 
 
 
 
 
 
 
 
820
 
821
+ if __name__ == "__main__":
822
+ app = Vizro().build(dashboard)
823
+ app.dash.layout.children.append(
824
+ dbc.NavLink(
825
+ ["Made with ", html.Img(src=get_asset_url("logo.svg"), id="banner", alt="Vizro logo"), "vizro"],
826
+ href="https://github.com/mckinsey/vizro",
827
+ target="_blank",
828
+ className="anchor-container",
829
+ )
830
+ )
831
+ server = app.dash.server
832
  app.run()
assets/{images/app.svg → app.svg} RENAMED
File without changes
assets/css/custom.css CHANGED
@@ -2,12 +2,28 @@
2
  padding-left: 8px;
3
  }
4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
  .anchor-container {
6
  align-items: center;
7
  background: var(--text-primary);
8
  border-top-left-radius: 8px;
9
  bottom: 0;
10
- color: var(--text-contrast-primary);
11
  display: flex;
12
  font-size: 0.8rem;
13
  font-weight: 500;
@@ -20,7 +36,7 @@
20
  .anchor-container:focus,
21
  .anchor-container:hover {
22
  background: var(--text-secondary);
23
- color: var(--text-contrast-primary);
24
  }
25
 
26
  img#banner {
 
2
  padding-left: 8px;
3
  }
4
 
5
+ #my-figure .multiple-cards-container {
6
+ display: flex;
7
+ flex-wrap: wrap;
8
+ gap: 12px;
9
+ }
10
+
11
+ #my-figure.figure-container {
12
+ height: unset;
13
+ width: unset;
14
+ }
15
+
16
+ #my-figure.figure-container .card {
17
+ height: 210px;
18
+ width: 240px;
19
+ }
20
+
21
  .anchor-container {
22
  align-items: center;
23
  background: var(--text-primary);
24
  border-top-left-radius: 8px;
25
  bottom: 0;
26
+ color: var(--text-primary-inverted);
27
  display: flex;
28
  font-size: 0.8rem;
29
  font-weight: 500;
 
36
  .anchor-container:focus,
37
  .anchor-container:hover {
38
  background: var(--text-secondary);
39
+ color: var(--text-primary-inverted);
40
  }
41
 
42
  img#banner {
assets/{images/logo.svg → logo.svg} RENAMED
File without changes
requirements.in CHANGED
@@ -1,3 +1,4 @@
 
1
  gunicorn
2
  openpyxl
3
- vizro
 
1
+ # This file is only used if you don't have hatch installed.
2
  gunicorn
3
  openpyxl
4
+ vizro==0.1.28
requirements.txt CHANGED
@@ -2,17 +2,23 @@
2
  # uv pip compile requirements.in -o requirements.txt
3
  annotated-types==0.7.0
4
  # via pydantic
 
 
 
 
5
  blinker==1.8.2
6
  # via flask
7
  cachelib==0.9.0
8
  # via flask-caching
9
- certifi==2024.7.4
10
  # via requests
11
- charset-normalizer==3.3.2
12
  # via requests
13
  click==8.1.7
14
- # via flask
15
- dash==2.17.1
 
 
16
  # via
17
  # dash-ag-grid
18
  # dash-bootstrap-components
@@ -29,7 +35,7 @@ dash-mantine-components==0.12.1
29
  # via vizro
30
  dash-table==5.0.0
31
  # via dash
32
- et-xmlfile==1.1.0
33
  # via openpyxl
34
  flask==3.0.3
35
  # via
@@ -39,75 +45,86 @@ flask-caching==2.3.0
39
  # via vizro
40
  gunicorn==23.0.0
41
  # via -r requirements.in
42
- idna==3.7
43
  # via requests
44
- importlib-metadata==8.4.0
45
- # via dash
 
 
46
  itsdangerous==2.2.0
47
  # via flask
48
  jinja2==3.1.4
49
  # via flask
50
- markupsafe==2.1.5
51
  # via
52
  # jinja2
53
  # werkzeug
 
 
54
  nest-asyncio==1.6.0
55
  # via dash
56
- numpy==2.1.0
57
- # via
58
- # pandas
59
- # vizro
60
  openpyxl==3.1.5
61
  # via -r requirements.in
62
  packaging==24.1
63
  # via
 
64
  # gunicorn
65
  # plotly
66
- pandas==2.2.2
67
  # via vizro
68
- plotly==5.23.0
69
- # via dash
70
- pydantic==2.8.2
 
 
 
 
 
 
71
  # via vizro
72
- pydantic-core==2.20.1
73
  # via pydantic
 
 
74
  python-dateutil==2.9.0.post0
75
  # via pandas
76
- pytz==2024.1
77
  # via pandas
78
  requests==2.32.3
79
  # via dash
80
  retrying==1.3.4
81
  # via dash
82
- ruff==0.6.1
83
- # via vizro
84
- setuptools==73.0.1
85
- # via
86
- # dash
87
- # vizro
88
  six==1.16.0
89
  # via
90
  # python-dateutil
91
  # retrying
92
  tenacity==9.0.0
93
  # via plotly
 
 
 
 
94
  typing-extensions==4.12.2
95
  # via
 
96
  # dash
97
  # pydantic
98
  # pydantic-core
99
- tzdata==2024.1
100
  # via pandas
101
- urllib3==2.2.2
102
  # via requests
103
- vizro==0.1.20
104
  # via -r requirements.in
105
- werkzeug==3.0.4
106
  # via
107
  # dash
108
  # flask
109
- # vizro
110
  wrapt==1.16.0
111
  # via vizro
112
- zipp==3.20.0
113
  # via importlib-metadata
 
2
  # uv pip compile requirements.in -o requirements.txt
3
  annotated-types==0.7.0
4
  # via pydantic
5
+ autoflake==2.3.1
6
+ # via vizro
7
+ black==24.4.2
8
+ # via vizro
9
  blinker==1.8.2
10
  # via flask
11
  cachelib==0.9.0
12
  # via flask-caching
13
+ certifi==2024.8.30
14
  # via requests
15
+ charset-normalizer==3.4.0
16
  # via requests
17
  click==8.1.7
18
+ # via
19
+ # black
20
+ # flask
21
+ dash==2.18.1
22
  # via
23
  # dash-ag-grid
24
  # dash-bootstrap-components
 
35
  # via vizro
36
  dash-table==5.0.0
37
  # via dash
38
+ et-xmlfile==2.0.0
39
  # via openpyxl
40
  flask==3.0.3
41
  # via
 
45
  # via vizro
46
  gunicorn==23.0.0
47
  # via -r requirements.in
48
+ idna==3.10
49
  # via requests
50
+ importlib-metadata==8.5.0
51
+ # via
52
+ # dash
53
+ # flask
54
  itsdangerous==2.2.0
55
  # via flask
56
  jinja2==3.1.4
57
  # via flask
58
+ markupsafe==3.0.2
59
  # via
60
  # jinja2
61
  # werkzeug
62
+ mypy-extensions==1.0.0
63
+ # via black
64
  nest-asyncio==1.6.0
65
  # via dash
66
+ numpy==2.0.2
67
+ # via pandas
 
 
68
  openpyxl==3.1.5
69
  # via -r requirements.in
70
  packaging==24.1
71
  # via
72
+ # black
73
  # gunicorn
74
  # plotly
75
+ pandas==2.2.3
76
  # via vizro
77
+ pathspec==0.12.1
78
+ # via black
79
+ platformdirs==4.2.2
80
+ # via black
81
+ plotly==5.24.1
82
+ # via
83
+ # dash
84
+ # vizro
85
+ pydantic==2.9.2
86
  # via vizro
87
+ pydantic-core==2.23.4
88
  # via pydantic
89
+ pyflakes==3.2.0
90
+ # via autoflake
91
  python-dateutil==2.9.0.post0
92
  # via pandas
93
+ pytz==2024.2
94
  # via pandas
95
  requests==2.32.3
96
  # via dash
97
  retrying==1.3.4
98
  # via dash
99
+ setuptools==75.3.0
100
+ # via dash
 
 
 
 
101
  six==1.16.0
102
  # via
103
  # python-dateutil
104
  # retrying
105
  tenacity==9.0.0
106
  # via plotly
107
+ tomli==2.1.0
108
+ # via
109
+ # autoflake
110
+ # black
111
  typing-extensions==4.12.2
112
  # via
113
+ # black
114
  # dash
115
  # pydantic
116
  # pydantic-core
117
+ tzdata==2024.2
118
  # via pandas
119
+ urllib3==2.2.3
120
  # via requests
121
+ vizro==0.1.28
122
  # via -r requirements.in
123
+ werkzeug==3.0.6
124
  # via
125
  # dash
126
  # flask
 
127
  wrapt==1.16.0
128
  # via vizro
129
+ zipp==3.20.2
130
  # via importlib-metadata