.gitattributes CHANGED
@@ -34,4 +34,3 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
  assets/images/kpi_dashboard.gif filter=lfs diff=lfs merge=lfs -text
37
- kpi-dashboard.gif filter=lfs diff=lfs merge=lfs -text
 
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
  assets/images/kpi_dashboard.gif filter=lfs diff=lfs merge=lfs -text
 
Dockerfile CHANGED
@@ -1,9 +1,18 @@
1
- FROM ghcr.io/astral-sh/uv:python3.12-bookworm-slim
 
 
 
 
 
2
 
3
  WORKDIR /app
4
 
5
- COPY requirements.txt .
6
- RUN uv pip sync --system requirements.txt
7
- COPY . .
 
 
 
 
8
 
9
- ENTRYPOINT ["gunicorn", "app:app", "--workers", "4", "--bind", "0.0.0.0:7860"]
 
1
+ # read the doc: https://huggingface.co/docs/hub/spaces-sdks-docker
2
+ # you will also find guides on how best to write your Dockerfile
3
+
4
+ FROM python:3.12
5
+
6
+ RUN useradd -m -u 1000 user
7
 
8
  WORKDIR /app
9
 
10
+ COPY --chown=user ./requirements.txt requirements.txt
11
+
12
+ RUN pip install --no-cache-dir --upgrade -r requirements.txt
13
+
14
+ COPY --chown=user . /app
15
+
16
+ EXPOSE 7860
17
 
18
+ CMD ["gunicorn", "-w", "4", "-b", "0.0.0.0:7860", "app:server"]
README.md CHANGED
@@ -9,7 +9,7 @@ license: apache-2.0
9
  short_description: Example of a Key Performance Indicator (KPI) dashboard
10
  ---
11
 
12
- # KPI Dashboard
13
 
14
  This dashboard provides an example of a Key Performance Indicator (KPI) dashboard, designed to help users get started
15
  and extend further. It uses fictional budget data to demonstrate the capabilities of Vizro using real world applications.
 
9
  short_description: Example of a Key Performance Indicator (KPI) dashboard
10
  ---
11
 
12
+ # KPI dashboard
13
 
14
  This dashboard provides an example of a Key Performance Indicator (KPI) dashboard, designed to help users get started
15
  and extend further. It uses fictional budget data to demonstrate the capabilities of Vizro using real world applications.
app.py CHANGED
@@ -1,10 +1,8 @@
1
  """Example to show dashboard configuration."""
2
 
3
- from dash import html, get_asset_url
4
- import dash_bootstrap_components as dbc
5
  import pandas as pd
6
  import vizro.models as vm
7
- from utils._charts import COLUMN_DEFS, area, bar, choropleth, pie
8
  from utils._helper import clean_data_and_add_columns, create_data_for_kpi_cards
9
  from vizro import Vizro
10
  from vizro.actions import filter_interaction
@@ -15,11 +13,11 @@ from vizro.tables import dash_ag_grid
15
  df_complaints = pd.read_csv("https://query.data.world/s/glbdstahsuw3hjgunz3zssggk7dsfu?dws=00000")
16
  df_complaints = clean_data_and_add_columns(df_complaints)
17
  df_kpi_cards = create_data_for_kpi_cards(df_complaints)
 
18
 
19
 
20
  # SUB-SECTIONS ------------------------------------------------------------------------------------
21
- kpi_banner = vm.Container(
22
- layout=vm.Flex(direction="row"),
23
  components=[
24
  vm.Figure(
25
  id="kpi-reverse-coloring",
@@ -30,7 +28,6 @@ kpi_banner = vm.Container(
30
  title="Total Complaints",
31
  value_format="{value:.0f}",
32
  reference_format="{delta_relative:+.1%} vs. 2018 ({reference:.0f})",
33
- icon="person",
34
  ),
35
  ),
36
  vm.Figure(
@@ -41,7 +38,6 @@ kpi_banner = vm.Container(
41
  title="Closed Complaints",
42
  value_format="{value:.1f}%",
43
  reference_format="{delta:+.1f}pp vs. 2018 ({reference:.1f}%)",
44
- icon="inventory",
45
  )
46
  ),
47
  vm.Figure(
@@ -52,7 +48,6 @@ kpi_banner = vm.Container(
52
  title="Timely Response",
53
  value_format="{value:.1f}%",
54
  reference_format="{delta:+.1f}pp vs. 2018 ({reference:.1f}%)",
55
- icon="timer",
56
  )
57
  ),
58
  vm.Figure(
@@ -63,7 +58,6 @@ kpi_banner = vm.Container(
63
  title="Closed w/o cost",
64
  value_format="{value:.1f}%",
65
  reference_format="{delta:.1f}pp vs. 2018 ({reference:.1f}%)",
66
- icon="payments",
67
  )
68
  ),
69
  vm.Figure(
@@ -74,10 +68,10 @@ kpi_banner = vm.Container(
74
  title="Consumer disputed",
75
  value_format="{value:.1f}%",
76
  reference_format="{delta:+.1f}pp vs. 2018 ({reference:.1f}%)",
77
- icon="sentiment_dissatisfied",
78
  )
79
  ),
80
  ],
 
81
  )
82
 
83
  bar_charts_tabbed = vm.Tabs(
@@ -136,7 +130,7 @@ bar_charts_tabbed = vm.Tabs(
136
  # PAGES --------------------------------------------------------------------------------------
137
  page_exec = vm.Page(
138
  title="Executive View",
139
- layout=vm.Grid(
140
  grid=[
141
  [0, 0],
142
  [0, 0],
@@ -165,7 +159,7 @@ page_exec = vm.Page(
165
 
166
  page_region = vm.Page(
167
  title="Regional View",
168
- layout=vm.Grid(grid=[[0, 1]]),
169
  components=[
170
  vm.Graph(
171
  figure=choropleth(
@@ -249,15 +243,7 @@ dashboard = vm.Dashboard(
249
  )
250
 
251
  app = Vizro().build(dashboard)
252
- app.dash.layout.children.append(
253
- dbc.NavLink(
254
- ["Made with ", html.Img(src=get_asset_url("images/logo.svg"), id="banner", alt="Vizro logo"), "vizro"],
255
- href="https://github.com/mckinsey/vizro",
256
- target="_blank",
257
- external_link=True,
258
- className="anchor-container",
259
- )
260
- )
261
 
262
  if __name__ == "__main__":
263
  app.run()
 
1
  """Example to show dashboard configuration."""
2
 
 
 
3
  import pandas as pd
4
  import vizro.models as vm
5
+ from utils._charts import COLUMN_DEFS, FlexContainer, area, bar, choropleth, pie
6
  from utils._helper import clean_data_and_add_columns, create_data_for_kpi_cards
7
  from vizro import Vizro
8
  from vizro.actions import filter_interaction
 
13
  df_complaints = pd.read_csv("https://query.data.world/s/glbdstahsuw3hjgunz3zssggk7dsfu?dws=00000")
14
  df_complaints = clean_data_and_add_columns(df_complaints)
15
  df_kpi_cards = create_data_for_kpi_cards(df_complaints)
16
+ vm.Page.add_type("components", FlexContainer)
17
 
18
 
19
  # SUB-SECTIONS ------------------------------------------------------------------------------------
20
+ kpi_banner = FlexContainer(
 
21
  components=[
22
  vm.Figure(
23
  id="kpi-reverse-coloring",
 
28
  title="Total Complaints",
29
  value_format="{value:.0f}",
30
  reference_format="{delta_relative:+.1%} vs. 2018 ({reference:.0f})",
 
31
  ),
32
  ),
33
  vm.Figure(
 
38
  title="Closed Complaints",
39
  value_format="{value:.1f}%",
40
  reference_format="{delta:+.1f}pp vs. 2018 ({reference:.1f}%)",
 
41
  )
42
  ),
43
  vm.Figure(
 
48
  title="Timely Response",
49
  value_format="{value:.1f}%",
50
  reference_format="{delta:+.1f}pp vs. 2018 ({reference:.1f}%)",
 
51
  )
52
  ),
53
  vm.Figure(
 
58
  title="Closed w/o cost",
59
  value_format="{value:.1f}%",
60
  reference_format="{delta:.1f}pp vs. 2018 ({reference:.1f}%)",
 
61
  )
62
  ),
63
  vm.Figure(
 
68
  title="Consumer disputed",
69
  value_format="{value:.1f}%",
70
  reference_format="{delta:+.1f}pp vs. 2018 ({reference:.1f}%)",
 
71
  )
72
  ),
73
  ],
74
+ classname="kpi-banner",
75
  )
76
 
77
  bar_charts_tabbed = vm.Tabs(
 
130
  # PAGES --------------------------------------------------------------------------------------
131
  page_exec = vm.Page(
132
  title="Executive View",
133
+ layout=vm.Layout(
134
  grid=[
135
  [0, 0],
136
  [0, 0],
 
159
 
160
  page_region = vm.Page(
161
  title="Regional View",
162
+ layout=vm.Layout(grid=[[0, 1]]),
163
  components=[
164
  vm.Graph(
165
  figure=choropleth(
 
243
  )
244
 
245
  app = Vizro().build(dashboard)
246
+ server = app.dash.server
 
 
 
 
 
 
 
 
247
 
248
  if __name__ == "__main__":
249
  app.run()
assets/css/custom.css CHANGED
@@ -1,11 +1,25 @@
1
- #header {
2
- padding-left: 0;
3
  }
4
 
5
  .card-kpi {
6
- min-width: 240px;
7
  padding: 0.75rem;
8
- height: 150px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
  }
10
 
11
  /* Apply reverse color coding for one KPI card */
@@ -24,28 +38,3 @@
24
  #kpi-reverse-coloring .card-kpi:has(.color-neg) {
25
  border-left: 4px solid var(--bs-blue);
26
  }
27
-
28
- .anchor-container {
29
- align-items: center;
30
- background: var(--text-primary);
31
- border-top-left-radius: 8px;
32
- bottom: 0;
33
- color: var(--text-primary-inverted) !important;
34
- display: flex;
35
- font-size: 0.8rem;
36
- font-weight: 500;
37
- height: 24px;
38
- padding: 0 12px;
39
- position: fixed;
40
- right: 0;
41
- }
42
-
43
- .anchor-container:focus,
44
- .anchor-container:hover {
45
- background: var(--text-secondary);
46
- color: var(--text-primary-inverted);
47
- }
48
-
49
- img#banner {
50
- height: 16px;
51
- }
 
1
+ #page-header {
2
+ padding-left: 4px;
3
  }
4
 
5
  .card-kpi {
6
+ min-width: 220px;
7
  padding: 0.75rem;
8
+ }
9
+
10
+ .kpi-banner {
11
+ display: flex;
12
+ gap: 1rem;
13
+ height: 100%;
14
+ overflow: scroll;
15
+ }
16
+
17
+ .kpi-banner .figure-container {
18
+ height: unset;
19
+ }
20
+
21
+ .kpi-banner::-webkit-scrollbar-thumb {
22
+ border: 5px solid var(--main-container-bg-color);
23
  }
24
 
25
  /* Apply reverse color coding for one KPI card */
 
38
  #kpi-reverse-coloring .card-kpi:has(.color-neg) {
39
  border-left: 4px solid var(--bs-blue);
40
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
images/kpi-dashboard.gif DELETED

Git LFS Details

  • SHA256: de57229ea8570c9038cb6c4b0217dee896fac4b7bcde452b5ed7afc74cfbcacf
  • Pointer size: 132 Bytes
  • Size of remote file: 1.85 MB
requirements.in CHANGED
@@ -1,3 +1,3 @@
 
1
  gunicorn
2
- vizro==0.1.44
3
- numpy!=2.0.2 # there is an issue with this specific version that the whl can't be built
 
1
+
2
  gunicorn
3
+ vizro
 
requirements.txt CHANGED
@@ -2,34 +2,33 @@
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.10.0
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.3.2
16
  # via requests
17
  click==8.1.7
18
- # via
19
- # black
20
- # flask
21
- dash==3.1.1
22
  # via
23
  # dash-ag-grid
24
  # dash-bootstrap-components
25
- # dash-mantine-components
26
  # vizro
27
- dash-ag-grid==32.3.0
28
  # via vizro
29
- dash-bootstrap-components==2.0.3
30
  # via vizro
31
- dash-mantine-components==2.1.0
 
 
 
 
32
  # via vizro
 
 
33
  flask==3.0.3
34
  # via
35
  # dash
@@ -38,7 +37,7 @@ flask-caching==2.3.0
38
  # via vizro
39
  gunicorn==23.0.0
40
  # via -r requirements.in
41
- idna==3.8
42
  # via requests
43
  importlib-metadata==8.4.0
44
  # via dash
@@ -50,36 +49,24 @@ markupsafe==2.1.5
50
  # via
51
  # jinja2
52
  # werkzeug
53
- mypy-extensions==1.0.0
54
- # via black
55
  nest-asyncio==1.6.0
56
  # via dash
57
- numpy==2.3.2
58
  # via
59
- # -r requirements.in
60
  # pandas
 
61
  packaging==24.1
62
  # via
63
- # black
64
  # gunicorn
65
  # plotly
66
- # vizro
67
  pandas==2.2.2
68
  # via vizro
69
- pathspec==0.12.1
70
- # via black
71
- platformdirs==4.3.6
72
- # via black
73
- plotly==5.24.0
74
- # via
75
- # dash
76
- # vizro
77
  pydantic==2.8.2
78
  # via vizro
79
  pydantic-core==2.20.1
80
  # via pydantic
81
- pyflakes==3.2.0
82
- # via autoflake
83
  python-dateutil==2.9.0.post0
84
  # via pandas
85
  pytz==2024.1
@@ -88,8 +75,12 @@ requests==2.32.3
88
  # via dash
89
  retrying==1.3.4
90
  # via dash
91
- setuptools==74.0.0
92
- # via dash
 
 
 
 
93
  six==1.16.0
94
  # via
95
  # python-dateutil
@@ -105,13 +96,14 @@ tzdata==2024.1
105
  # via pandas
106
  urllib3==2.2.2
107
  # via requests
108
- vizro==0.1.44
109
  # via -r requirements.in
110
  werkzeug==3.0.4
111
  # via
112
  # dash
113
  # flask
 
114
  wrapt==1.16.0
115
  # via vizro
116
- zipp==3.20.1
117
  # via importlib-metadata
 
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
 
19
  # vizro
20
+ dash-ag-grid==31.2.0
21
  # via vizro
22
+ dash-bootstrap-components==1.6.0
23
  # via vizro
24
+ dash-core-components==2.0.0
25
+ # via dash
26
+ dash-html-components==2.0.0
27
+ # via dash
28
+ dash-mantine-components==0.12.1
29
  # via vizro
30
+ dash-table==5.0.0
31
+ # via dash
32
  flask==3.0.3
33
  # via
34
  # dash
 
37
  # via vizro
38
  gunicorn==23.0.0
39
  # via -r requirements.in
40
+ idna==3.7
41
  # via requests
42
  importlib-metadata==8.4.0
43
  # via dash
 
49
  # via
50
  # jinja2
51
  # werkzeug
 
 
52
  nest-asyncio==1.6.0
53
  # via dash
54
+ numpy==2.1.0
55
  # via
 
56
  # pandas
57
+ # vizro
58
  packaging==24.1
59
  # via
 
60
  # gunicorn
61
  # plotly
 
62
  pandas==2.2.2
63
  # via vizro
64
+ plotly==5.23.0
65
+ # via dash
 
 
 
 
 
 
66
  pydantic==2.8.2
67
  # via vizro
68
  pydantic-core==2.20.1
69
  # via pydantic
 
 
70
  python-dateutil==2.9.0.post0
71
  # via pandas
72
  pytz==2024.1
 
75
  # via dash
76
  retrying==1.3.4
77
  # via dash
78
+ ruff==0.6.1
79
+ # via vizro
80
+ setuptools==73.0.1
81
+ # via
82
+ # dash
83
+ # vizro
84
  six==1.16.0
85
  # via
86
  # python-dateutil
 
96
  # via pandas
97
  urllib3==2.2.2
98
  # via requests
99
+ vizro==0.1.20
100
  # via -r requirements.in
101
  werkzeug==3.0.4
102
  # via
103
  # dash
104
  # flask
105
+ # vizro
106
  wrapt==1.16.0
107
  # via vizro
108
+ zipp==3.20.0
109
  # via importlib-metadata
utils/_charts.py CHANGED
@@ -1,13 +1,33 @@
1
  """Contains custom components and charts used inside the dashboard."""
2
 
3
- from typing import List, Optional
4
 
5
  import pandas as pd
6
  import plotly.graph_objects as go
 
7
  import vizro.plotly.express as px
 
8
  from vizro.models.types import capture
9
 
10
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  # CUSTOM CHARTS ----------------------------------------------------------------
12
  @capture("graph")
13
  def bar(
 
1
  """Contains custom components and charts used inside the dashboard."""
2
 
3
+ from typing import List, Literal, Optional
4
 
5
  import pandas as pd
6
  import plotly.graph_objects as go
7
+ import vizro.models as vm
8
  import vizro.plotly.express as px
9
+ from dash import html
10
  from vizro.models.types import capture
11
 
12
 
13
+ # CUSTOM COMPONENTS -------------------------------------------------------------
14
+ class FlexContainer(vm.Container):
15
+ """Custom flex `Container`."""
16
+
17
+ type: Literal["flex_container"] = "flex_container"
18
+ title: str = None # Title exists in vm.Container but we don't want to use it here.
19
+ classname: str = "d-flex"
20
+
21
+ def build(self):
22
+ """Returns a flex container."""
23
+ return html.Div(
24
+ id=self.id, children=[component.build() for component in self.components], className=self.classname
25
+ )
26
+
27
+
28
+ vm.Container.add_type("components", FlexContainer)
29
+
30
+
31
  # CUSTOM CHARTS ----------------------------------------------------------------
32
  @capture("graph")
33
  def bar(