Files changed (6) hide show
  1. Dockerfile +14 -5
  2. app.py +7 -16
  3. assets/css/custom.css +18 -29
  4. requirements.in +2 -2
  5. requirements.txt +26 -34
  6. utils/_charts.py +18 -1
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"]
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",
@@ -78,6 +76,7 @@ kpi_banner = vm.Container(
78
  )
79
  ),
80
  ],
 
81
  )
82
 
83
  bar_charts_tabbed = vm.Tabs(
@@ -136,7 +135,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 +164,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 +248,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",
 
76
  )
77
  ),
78
  ],
79
+ classname="kpi-banner",
80
  )
81
 
82
  bar_charts_tabbed = vm.Tabs(
 
135
  # PAGES --------------------------------------------------------------------------------------
136
  page_exec = vm.Page(
137
  title="Executive View",
138
+ layout=vm.Layout(
139
  grid=[
140
  [0, 0],
141
  [0, 0],
 
164
 
165
  page_region = vm.Page(
166
  title="Regional View",
167
+ layout=vm.Layout(grid=[[0, 1]]),
168
  components=[
169
  vm.Graph(
170
  figure=choropleth(
 
248
  )
249
 
250
  app = Vizro().build(dashboard)
251
+ server = app.dash.server
 
 
 
 
 
 
 
 
252
 
253
  if __name__ == "__main__":
254
  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
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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,30 @@
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
  # CUSTOM CHARTS ----------------------------------------------------------------
29
  @capture("graph")
30
  def bar(