Falln87 ndrezner commited on
Commit
1950b48
·
verified ·
0 Parent(s):

Duplicate from plotly/dash-app-template

Browse files

Co-authored-by: Nathan Drezner <[email protected]>

Files changed (7) hide show
  1. .github/workflows/spaces_publish.yml +21 -0
  2. .gitignore +291 -0
  3. Dockerfile +16 -0
  4. README.md +93 -0
  5. app.py +332 -0
  6. assets/styles.css +107 -0
  7. requirements.txt +6 -0
.github/workflows/spaces_publish.yml ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: Sync to Hugging Face hub
2
+ on:
3
+ push:
4
+ branches: [main]
5
+
6
+ # to run this workflow manually from the Actions tab
7
+ workflow_dispatch:
8
+
9
+
10
+ jobs:
11
+ sync-to-hub:
12
+ runs-on: ubuntu-latest
13
+ steps:
14
+ - uses: actions/checkout@v3
15
+ with:
16
+ fetch-depth: 0
17
+ lfs: true
18
+ - name: Push to hub
19
+ env:
20
+ HF_TOKEN: ${{ secrets.HF_TOKEN }}
21
+ run: git push https://plotly:[email protected]/spaces/plotly/dash-app-template main
.gitignore ADDED
@@ -0,0 +1,291 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .DS_Store
2
+ .vscode/
3
+
4
+ # Build artifacts
5
+ dash_ag_grid/*
6
+ !dash_ag_grid/__init__.py
7
+ R/
8
+ deps/
9
+ man/
10
+ inst/
11
+ DESCRIPTION
12
+ NAMESPACE
13
+ Project.toml
14
+ src/*.jl
15
+ src/jl/*.jl
16
+ lib/
17
+
18
+ # Created by .ignore support plugin (hsz.mobi)
19
+ ### VisualStudioCode template
20
+ .vscode/*
21
+ !.vscode/settings.json
22
+ !.vscode/tasks.json
23
+ !.vscode/launch.json
24
+ !.vscode/extensions.json
25
+ ### JetBrains template
26
+ # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
27
+ # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
28
+
29
+ # User-specific stuff
30
+ .idea/**/workspace.xml
31
+ .idea/**/tasks.xml
32
+ .idea/**/usage.statistics.xml
33
+ .idea/**/dictionaries
34
+ .idea/**/shelf
35
+
36
+ # Sensitive or high-churn files
37
+ .idea/**/dataSources/
38
+ .idea/**/dataSources.ids
39
+ .idea/**/dataSources.local.xml
40
+ .idea/**/sqlDataSources.xml
41
+ .idea/**/dynamic.xml
42
+ .idea/**/uiDesigner.xml
43
+ .idea/**/dbnavigator.xml
44
+
45
+ # Gradle
46
+ .idea/**/gradle.xml
47
+ .idea/**/libraries
48
+
49
+ # Gradle and Maven with auto-import
50
+ # When using Gradle or Maven with auto-import, you should exclude module files,
51
+ # since they will be recreated, and may cause churn. Uncomment if using
52
+ # auto-import.
53
+ # .idea/modules.xml
54
+ # .idea/*.iml
55
+ # .idea/modules
56
+
57
+ # CMake
58
+ cmake-build-*/
59
+
60
+ # Mongo Explorer plugin
61
+ .idea/**/mongoSettings.xml
62
+
63
+ # File-based project format
64
+ *.iws
65
+
66
+ # IntelliJ
67
+ out/
68
+
69
+ # mpeltonen/sbt-idea plugin
70
+ .idea_modules/
71
+
72
+ # JIRA plugin
73
+ atlassian-ide-plugin.xml
74
+
75
+ # Cursive Clojure plugin
76
+ .idea/replstate.xml
77
+
78
+ # Crashlytics plugin (for Android Studio and IntelliJ)
79
+ com_crashlytics_export_strings.xml
80
+ crashlytics.properties
81
+ crashlytics-build.properties
82
+ fabric.properties
83
+
84
+ # Editor-based Rest Client
85
+ .idea/httpRequests
86
+ ### Node template
87
+ # Logs
88
+ logs
89
+ *.log
90
+ npm-debug.log*
91
+ yarn-debug.log*
92
+ yarn-error.log*
93
+
94
+ # Runtime data
95
+ pids
96
+ *.pid
97
+ *.seed
98
+ *.pid.lock
99
+
100
+ # Directory for instrumented libs generated by jscoverage/JSCover
101
+ lib-cov
102
+
103
+ # Coverage directory used by tools like istanbul
104
+ coverage
105
+
106
+ # nyc test coverage
107
+ .nyc_output
108
+
109
+ # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
110
+ .grunt
111
+
112
+ # Bower dependency directory (https://bower.io/)
113
+ bower_components
114
+
115
+ # node-waf configuration
116
+ .lock-wscript
117
+
118
+ # Compiled binary addons (https://nodejs.org/api/addons.html)
119
+ build/Release
120
+
121
+ # Dependency directories
122
+ node_modules/
123
+ jspm_packages/
124
+
125
+ # TypeScript v1 declaration files
126
+ typings/
127
+
128
+ # Optional npm cache directory
129
+ .npm
130
+
131
+ # Optional eslint cache
132
+ .eslintcache
133
+
134
+ # Optional REPL history
135
+ .node_repl_history
136
+
137
+ # Output of 'npm pack'
138
+ *.tgz
139
+
140
+ # Yarn Integrity file
141
+ .yarn-integrity
142
+
143
+ # dotenv environment variables file
144
+ .env
145
+
146
+ # parcel-bundler cache (https://parceljs.org/)
147
+ .cache
148
+
149
+ # next.js build output
150
+ .next
151
+
152
+ # nuxt.js build output
153
+ .nuxt
154
+
155
+ # vuepress build output
156
+ .vuepress/dist
157
+
158
+ # Serverless directories
159
+ .serverless
160
+ ### Python template
161
+ # Byte-compiled / optimized / DLL files
162
+ __pycache__/
163
+ *.py[cod]
164
+ *$py.class
165
+
166
+ # C extensions
167
+ *.so
168
+
169
+ # Distribution / packaging
170
+ .Python
171
+ build/
172
+ develop-eggs/
173
+ dist/
174
+ downloads/
175
+ eggs/
176
+ .eggs/
177
+ lib64/
178
+ parts/
179
+ sdist/
180
+ var/
181
+ wheels/
182
+ *.egg-info/
183
+ .installed.cfg
184
+ *.egg
185
+ MANIFEST
186
+
187
+ # PyInstaller
188
+ # Usually these files are written by a python script from a template
189
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
190
+ *.manifest
191
+ *.spec
192
+
193
+ # Installer logs
194
+ pip-log.txt
195
+ pip-delete-this-directory.txt
196
+
197
+ # Unit test / coverage reports
198
+ htmlcov/
199
+ .tox/
200
+ .coverage
201
+ .coverage.*
202
+ nosetests.xml
203
+ coverage.xml
204
+ *.cover
205
+ .hypothesis/
206
+ .pytest_cache/
207
+
208
+ # Translations
209
+ *.mo
210
+ *.pot
211
+
212
+ # Django stuff:
213
+ local_settings.py
214
+ db.sqlite3
215
+
216
+ # Flask stuff:
217
+ instance/
218
+ .webassets-cache
219
+
220
+ # Scrapy stuff:
221
+ .scrapy
222
+
223
+ # Sphinx documentation
224
+ docs/_build/
225
+
226
+ # PyBuilder
227
+ target/
228
+
229
+ # Jupyter Notebook
230
+ .ipynb_checkpoints
231
+
232
+ # pyenv
233
+ .python-version
234
+
235
+ # celery beat schedule file
236
+ celerybeat-schedule
237
+
238
+ # SageMath parsed files
239
+ *.sage.py
240
+
241
+ # Environments
242
+ .venv
243
+ env/
244
+ venv/
245
+ ENV/
246
+ env.bak/
247
+ venv.bak/
248
+
249
+ # Spyder project settings
250
+ .spyderproject
251
+ .spyproject
252
+
253
+ # Rope project settings
254
+ .ropeproject
255
+
256
+ # mkdocs documentation
257
+ /site
258
+
259
+ # mypy
260
+ .mypy_cache/
261
+ ### SublimeText template
262
+ # Cache files for Sublime Text
263
+ *.tmlanguage.cache
264
+ *.tmPreferences.cache
265
+ *.stTheme.cache
266
+
267
+ # Workspace files are user-specific
268
+ *.sublime-workspace
269
+
270
+ # Project files should be checked into the repository, unless a significant
271
+ # proportion of contributors will probably not be using Sublime Text
272
+ # *.sublime-project
273
+
274
+ # SFTP configuration file
275
+ sftp-config.json
276
+
277
+ # Package control specific files
278
+ Package Control.last-run
279
+ Package Control.ca-list
280
+ Package Control.ca-bundle
281
+ Package Control.system-ca-bundle
282
+ Package Control.cache/
283
+ Package Control.ca-certs/
284
+ Package Control.merged-ca-bundle
285
+ Package Control.user-ca-bundle
286
+ oscrypto-ca-bundle.crt
287
+ bh_unicode_properties.cache
288
+
289
+ # Sublime-github package stores a github token in this file
290
+ # https://packagecontrol.io/packages/sublime-github
291
+ GitHub.sublime-settings
Dockerfile ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.12
2
+ COPY --from=ghcr.io/astral-sh/uv:0.4.20 /uv /bin/uv
3
+
4
+ RUN useradd -m -u 1000 user
5
+ ENV PATH="/home/user/.local/bin:$PATH"
6
+ ENV UV_SYSTEM_PYTHON=1
7
+
8
+ WORKDIR /app
9
+
10
+ COPY --chown=user ./requirements.txt requirements.txt
11
+ RUN uv pip install -r requirements.txt
12
+
13
+ COPY --chown=user . /app
14
+ USER user
15
+
16
+ CMD ["gunicorn", "app:server", "--workers", "4", "--bind", "0.0.0.0:7860"]
README.md ADDED
@@ -0,0 +1,93 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: Dash App Template
3
+ emoji: 📊
4
+ sdk: docker
5
+ app_file: app.py
6
+ pinned: true
7
+ ---
8
+
9
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
10
+
11
+ # Dash on Spaces
12
+
13
+ ![Gapminder Dashboard Screenshot](screenshot.png)
14
+
15
+ With Dash Open Source, you can create data apps on your laptop in pure Python, no JavaScript required.
16
+
17
+ Get familiar with Dash by building a [sample app](https://dash.plotly.com/tutorial) with open source. Scale up with [Dash Enterprise](https://plotly.com/dash/) when your Dash app is ready for department or company-wide consumption. Or, launch your initiative with Dash Enterprise from the start to unlock developer productivity gains and hands-on acceleration from Plotly's team.
18
+
19
+ ## Deploy Dash on Spaces
20
+
21
+ To get started with Dash on Spaces, click the button below:
22
+
23
+ <a href="http://huggingface.co/new-space?template=plotly/dash-app-template" target="_blank">
24
+ <img src="https://huggingface.co/datasets/huggingface/badges/resolve/main/deploy-to-spaces-lg.svg" alt="">
25
+ </a>
26
+
27
+ This will start building your Space using Plotly's Dash Docker template. If successful, you should see a similar application to the [Dash template app](https://huggingface.co/spaces/dash/dash-app-template).
28
+
29
+ ## Customizing your Dash app
30
+
31
+ If you have never built with Dash before, we recommend getting started with our [Dash in 20 minutes tutorial](https://dash.plotly.com/tutorial).
32
+
33
+ When you create a Dash Space, you'll get a few key files to help you get started:
34
+
35
+ ### 1. app.py
36
+
37
+ This is the main app file that defines the core logic of your project. Dash apps are often structured as modules, and you can optionally seperate your layout, callbacks, and data into other files, like `layout.py`, etc.
38
+
39
+ Inside of `app.py` you will see:
40
+
41
+ 1. `from dash import Dash, html`
42
+ We import the `Dash` object to define our app, and the `html` library, which gives us building blocks to assemble our project.
43
+
44
+ 2. `app = Dash()`
45
+ Here, we define our app. Layout, server, and callbacks are _bound_ to the `app` object.
46
+
47
+ 3. `server = app.server`
48
+ Here, we define our server variable, which is used to run the app in production.
49
+
50
+ 4. `app.layout = `
51
+ The starter app layout is defined as a list of Dash components, an indivdual Dash component, or a function that returns either.
52
+
53
+ The `app.layout` is your initial layout that will be updated as a single-page application by callbacks and other logic in your project.
54
+
55
+ 5. `if __name__ == '__main__': app.run(debug=True)`
56
+ If you are running your project locally with `python app.py`, `app.run(...)` will execute and start up a development server to work on your project, with features including hot reloading, the callback graph, and more.
57
+
58
+ In production, we recommend `gunicorn`, which is a production-grade server. Debug features will not be enabled when running your project with `gunicorn`, so this line will never be reached.
59
+
60
+ ### 2. Dockerfile
61
+
62
+ The Dockerfile for a Dash app is minimal since Dash has few system dependencies. The key requirements are:
63
+
64
+ - It installs the dependencies listed in `requirements.txt` (using `uv`)
65
+ - It creates a non-root user for security
66
+ - It runs the app with `gunicorn` using `gunicorn app:server --workers 4`
67
+
68
+ You may need to modify this file if your application requires additional system dependencies, permissions, or other CLI flags.
69
+
70
+ ### 3. requirements.txt
71
+
72
+ The Space will automatically install dependencies listed in the `requirements.txt` file. At minimum, you must include `dash` and `gunicorn` in this file. You will want to add any other required packages your app needs.
73
+
74
+ The Dash Space template provides a basic setup that you can extend based on your needs.
75
+
76
+ ## Additional Resources and Support
77
+
78
+ - [Dash documentation](https://dash.plotly.com)
79
+ - [Dash GitHub repository](https://github.com/plotly/dash)
80
+ - [Dash Community Forums](https://community.plotly.com)
81
+ - [Dash Enterprise](https://plotly.com/dash)
82
+ - [Dash template Space](https://huggingface.co/spaces/plotly/dash-app-template)
83
+
84
+ ## Troubleshooting
85
+
86
+ If you encounter issues:
87
+
88
+ 1. Make sure your notebook runs locally in app mode using `python app.py`
89
+ 2. Check that all required packages are listed in `requirements.txt`
90
+ 3. Verify the port configuration matches (7860 is the default for Spaces)
91
+ 4. Check Space logs for any Python errors
92
+
93
+ For more help, visit the [Plotly Community Forums](https://community.plotly.com) or [open an issue](https://github.com/plotly/dash/issues).
app.py ADDED
@@ -0,0 +1,332 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import dash
2
+ import dash_mantine_components as dmc
3
+ import plotly.express as px
4
+ from dash import Input, Output, callback, dcc, html
5
+ from dash_iconify import DashIconify
6
+
7
+
8
+ app = dash.Dash(__name__)
9
+ server = app.server
10
+
11
+ df = px.data.gapminder()
12
+
13
+
14
+ def create_scatter_plot(selected_year, selected_continent=None):
15
+ filtered_df = df[df["year"] == selected_year]
16
+
17
+ if selected_continent and selected_continent != "All":
18
+ filtered_df = filtered_df[filtered_df["continent"] == selected_continent]
19
+
20
+ fig = px.scatter(
21
+ filtered_df,
22
+ x="gdpPercap",
23
+ y="lifeExp",
24
+ size="pop",
25
+ color="continent",
26
+ hover_name="country",
27
+ log_x=True,
28
+ size_max=60,
29
+ title=f"Life Expectancy vs GDP per Capita ({selected_year})",
30
+ )
31
+
32
+ fig.update_layout(
33
+ template="plotly_dark",
34
+ paper_bgcolor="rgba(0,0,0,0)",
35
+ plot_bgcolor="rgba(0,0,0,0)",
36
+ )
37
+
38
+ return fig
39
+
40
+
41
+ def create_line_chart(selected_country):
42
+ country_data = df[df["country"] == selected_country]
43
+ fig = px.line(
44
+ country_data,
45
+ x="year",
46
+ y="lifeExp",
47
+ title=f"{selected_country} - Life Expectancy",
48
+ )
49
+ fig.update_layout(
50
+ template="plotly_dark",
51
+ paper_bgcolor="rgba(0,0,0,0)",
52
+ plot_bgcolor="rgba(0,0,0,0)",
53
+ )
54
+ return fig
55
+
56
+
57
+ def create_bar_chart(selected_year):
58
+ year_data = df[df["year"] == selected_year]
59
+ continent_stats = year_data.groupby("continent")["lifeExp"].mean().reset_index()
60
+ fig = px.bar(
61
+ continent_stats,
62
+ x="continent",
63
+ y="lifeExp",
64
+ color="continent",
65
+ title=f"Average Life Expectancy by Continent ({selected_year})",
66
+ )
67
+ fig.update_layout(
68
+ template="plotly_dark",
69
+ paper_bgcolor="rgba(0,0,0,0)",
70
+ plot_bgcolor="rgba(0,0,0,0)",
71
+ showlegend=False,
72
+ )
73
+ return fig
74
+
75
+
76
+ def create_datacard(title, value, icon, color):
77
+ return dmc.Card(
78
+ [
79
+ dmc.Group(
80
+ [
81
+ DashIconify(icon=icon, width=30, color=color),
82
+ html.Div(
83
+ [
84
+ dmc.Text(value, size="xl", fw=700, c="white"),
85
+ dmc.Text(title, size="sm", c="dimmed"),
86
+ ]
87
+ ),
88
+ ],
89
+ align="center",
90
+ gap="md",
91
+ )
92
+ ],
93
+ p="md",
94
+ className="datacard",
95
+ )
96
+
97
+
98
+ app.layout = dmc.MantineProvider(
99
+ [
100
+ html.Link(
101
+ href="https://fonts.googleapis.com/css2?family=Outfit:[email protected]&display=swap",
102
+ rel="stylesheet",
103
+ ),
104
+ dmc.Group(
105
+ [
106
+ DashIconify(icon="twemoji:globe-with-meridians", width=45),
107
+ dmc.Text(
108
+ "Gapminder World Data Explorer", ml=10, size="xl", fw=900, c="white"
109
+ ),
110
+ ],
111
+ align="center",
112
+ className="header",
113
+ mb="md",
114
+ ),
115
+ dmc.Grid(
116
+ [
117
+ dmc.GridCol(
118
+ [
119
+ dmc.Stack(
120
+ [
121
+ dmc.Card(
122
+ [
123
+ dmc.Text("Controls", size="lg", mb="md"),
124
+ dmc.Stack(
125
+ [
126
+ html.Div(
127
+ [
128
+ dmc.Text(
129
+ "Year:", size="sm", mb=5
130
+ ),
131
+ dmc.Slider(
132
+ id="year-slider",
133
+ min=1952,
134
+ max=2007,
135
+ step=5,
136
+ value=2007,
137
+ marks=[
138
+ {
139
+ "value": year,
140
+ "label": str(year),
141
+ }
142
+ for year in [
143
+ 1952,
144
+ 1967,
145
+ 1982,
146
+ 1997,
147
+ 2007,
148
+ ]
149
+ ],
150
+ ),
151
+ ]
152
+ ),
153
+ html.Div(
154
+ [
155
+ dmc.Text(
156
+ "Continent Filter:",
157
+ size="sm",
158
+ mb=5,
159
+ ),
160
+ dmc.Select(
161
+ id="continent-dropdown",
162
+ data=[
163
+ {
164
+ "value": "All",
165
+ "label": "All Continents",
166
+ }
167
+ ]
168
+ + [
169
+ {
170
+ "value": cont,
171
+ "label": cont,
172
+ }
173
+ for cont in sorted(
174
+ df[
175
+ "continent"
176
+ ].unique()
177
+ )
178
+ ],
179
+ value="All",
180
+ ),
181
+ ]
182
+ ),
183
+ html.Div(
184
+ [
185
+ dmc.Text(
186
+ "Select Country:",
187
+ size="sm",
188
+ mb=5,
189
+ ),
190
+ dmc.Select(
191
+ id="country-dropdown",
192
+ data=[
193
+ {
194
+ "value": country,
195
+ "label": country,
196
+ }
197
+ for country in sorted(
198
+ df[
199
+ "country"
200
+ ].unique()
201
+ )
202
+ ],
203
+ value="United States",
204
+ searchable=True,
205
+ ),
206
+ ]
207
+ ),
208
+ ],
209
+ gap="lg",
210
+ ),
211
+ ],
212
+ p="md",
213
+ className="control-card",
214
+ )
215
+ ]
216
+ )
217
+ ],
218
+ span=3,
219
+ ),
220
+ dmc.GridCol(
221
+ [
222
+ dmc.Stack(
223
+ [
224
+ html.Div(id="stats-cards"),
225
+ dmc.Card(
226
+ [dcc.Graph(id="scatter-plot")],
227
+ p="sm",
228
+ className="chart-card",
229
+ ),
230
+ ],
231
+ gap="md",
232
+ )
233
+ ],
234
+ span=9,
235
+ ),
236
+ ],
237
+ gutter="md",
238
+ ),
239
+ dmc.Grid(
240
+ [
241
+ dmc.GridCol(
242
+ [
243
+ dmc.Card(
244
+ [dcc.Graph(id="line-chart")], p="sm", className="chart-card"
245
+ )
246
+ ],
247
+ span=6,
248
+ ),
249
+ dmc.GridCol(
250
+ [
251
+ dmc.Card(
252
+ [dcc.Graph(id="bar-chart")], p="sm", className="chart-card"
253
+ )
254
+ ],
255
+ span=6,
256
+ ),
257
+ ],
258
+ gutter="md",
259
+ mt="md",
260
+ ),
261
+ ],
262
+ forceColorScheme="dark",
263
+ theme={"colorScheme": "dark"},
264
+ )
265
+
266
+
267
+ @callback(
268
+ Output("scatter-plot", "figure"),
269
+ [Input("year-slider", "value"), Input("continent-dropdown", "value")],
270
+ )
271
+ def update_scatter_plot(selected_year, selected_continent):
272
+ return create_scatter_plot(selected_year, selected_continent)
273
+
274
+
275
+ @callback(Output("line-chart", "figure"), Input("country-dropdown", "value"))
276
+ def update_line_chart(selected_country):
277
+ return create_line_chart(selected_country)
278
+
279
+
280
+ @callback(Output("bar-chart", "figure"), Input("year-slider", "value"))
281
+ def update_bar_chart(selected_year):
282
+ return create_bar_chart(selected_year)
283
+
284
+
285
+ @callback(Output("stats-cards", "children"), Input("year-slider", "value"))
286
+ def update_stats(selected_year):
287
+ year_data = df[df["year"] == selected_year]
288
+
289
+ avg_life_exp = round(year_data["lifeExp"].mean(), 1)
290
+ total_pop = year_data["pop"].sum()
291
+ num_countries = len(year_data)
292
+ avg_gdp = round(year_data["gdpPercap"].mean(), 0)
293
+
294
+ return dmc.Grid(
295
+ [
296
+ dmc.GridCol(
297
+ create_datacard(
298
+ "Life Expectancy",
299
+ f"{avg_life_exp} years",
300
+ "mdi:heart-pulse",
301
+ "#ff6b35",
302
+ ),
303
+ span=3,
304
+ ),
305
+ dmc.GridCol(
306
+ create_datacard(
307
+ "Population",
308
+ f"{total_pop / 1e9:.1f}B",
309
+ "mdi:account-group",
310
+ "#1f77b4",
311
+ ),
312
+ span=3,
313
+ ),
314
+ dmc.GridCol(
315
+ create_datacard(
316
+ "Countries", str(num_countries), "mdi:earth", "#2ca02c"
317
+ ),
318
+ span=3,
319
+ ),
320
+ dmc.GridCol(
321
+ create_datacard(
322
+ "GDP per Capita", f"${avg_gdp:,.0f}", "mdi:currency-usd", "#d62728"
323
+ ),
324
+ span=3,
325
+ ),
326
+ ],
327
+ gutter="sm",
328
+ )
329
+
330
+
331
+ if __name__ == "__main__":
332
+ app.run(debug=True, port=8050)
assets/styles.css ADDED
@@ -0,0 +1,107 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ body {
2
+ font-family: 'Outfit', sans-serif;
3
+ background: linear-gradient(135deg, #1e1e2e 0%, #2a2a3e 100%);
4
+ margin: 0;
5
+ padding: 20px;
6
+ min-height: 100vh;
7
+ }
8
+
9
+ .header {
10
+ background: linear-gradient(135deg, #ff6b35, #d43425);
11
+ padding: 20px 30px;
12
+ border-radius: 15px;
13
+ box-shadow: 0 8px 25px rgba(255, 107, 53, 0.3);
14
+ margin-bottom: 20px;
15
+ }
16
+
17
+ .control-card {
18
+ background: rgba(15, 15, 20, 0.9);
19
+ border: 1px solid rgba(255, 255, 255, 0.1);
20
+ backdrop-filter: blur(10px);
21
+ border-radius: 15px;
22
+ box-shadow: 0 8px 25px rgba(0, 0, 0, 0.3);
23
+ }
24
+
25
+ .chart-card {
26
+ background: rgba(15, 15, 20, 0.9);
27
+ border: 1px solid rgba(255, 255, 255, 0.1);
28
+ backdrop-filter: blur(10px);
29
+ border-radius: 15px;
30
+ box-shadow: 0 8px 25px rgba(0, 0, 0, 0.3);
31
+ }
32
+
33
+ .datacard {
34
+ background: linear-gradient(135deg, rgba(255, 107, 53, 0.1), rgba(212, 52, 37, 0.1));
35
+ border: 1px solid rgba(255, 107, 53, 0.3);
36
+ border-radius: 12px;
37
+ transition: all 0.3s ease;
38
+ backdrop-filter: blur(10px);
39
+ }
40
+
41
+ .datacard:hover {
42
+ transform: translateY(-2px);
43
+ box-shadow: 0 12px 30px rgba(255, 107, 53, 0.2);
44
+ }
45
+
46
+ .year-slider .mantine-Slider-track {
47
+ background: rgba(255, 255, 255, 0.2);
48
+ }
49
+
50
+ .year-slider .mantine-Slider-bar {
51
+ background: linear-gradient(90deg, #ff6b35, #d43425);
52
+ }
53
+
54
+ .year-slider .mantine-Slider-thumb {
55
+ background: #ff6b35;
56
+ border: 2px solid white;
57
+ }
58
+
59
+ .continent-select .mantine-Select-input,
60
+ .country-select .mantine-Select-input {
61
+ background: rgba(255, 255, 255, 0.1);
62
+ border: 1px solid rgba(255, 255, 255, 0.2);
63
+ color: white;
64
+ }
65
+
66
+ .continent-select .mantine-Select-input:focus,
67
+ .country-select .mantine-Select-input:focus {
68
+ border-color: #ff6b35;
69
+ box-shadow: 0 0 10px rgba(255, 107, 53, 0.3);
70
+ }
71
+
72
+ /* Custom scrollbar for dropdowns */
73
+ .mantine-Select-dropdown {
74
+ background: rgba(15, 15, 20, 0.95);
75
+ border: 1px solid rgba(255, 255, 255, 0.1);
76
+ backdrop-filter: blur(10px);
77
+ }
78
+
79
+ .mantine-Select-item {
80
+ color: white;
81
+ }
82
+
83
+ .mantine-Select-item:hover {
84
+ background: rgba(255, 107, 53, 0.2);
85
+ }
86
+
87
+ /* Graph styling adjustments */
88
+ .js-plotly-plot {
89
+ border-radius: 10px;
90
+ overflow: hidden;
91
+ }
92
+
93
+ /* Responsive design */
94
+ @media (max-width: 768px) {
95
+ body {
96
+ padding: 10px;
97
+ }
98
+
99
+ .header {
100
+ padding: 15px 20px;
101
+ }
102
+
103
+ .control-card,
104
+ .chart-card {
105
+ margin: 10px 0;
106
+ }
107
+ }
requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ dash
2
+ dash-mantine-components
3
+ dash-iconify
4
+ plotly
5
+ pandas
6
+ gunicorn