├── .gitignore ├── LICENSE ├── README.md ├── dash_express ├── __init__.py ├── _app_shell.py ├── filters │ ├── __init__.py │ ├── autofilter.py │ └── filterfunc.py ├── kpi │ └── __init__.py ├── preview_chart.py └── version.py ├── docs ├── assets │ ├── css │ │ └── terminal.css │ ├── gifs │ │ ├── jupiter_preview.png │ │ └── min_app.gif │ └── js │ │ ├── custom.js │ │ └── terminal.js ├── authors.md ├── fundamentals │ ├── Add data.md │ ├── Create app.md │ ├── Visualization.md │ └── filters.md ├── index.md ├── installation.md ├── performance.md └── quickstart.md ├── mkdocs.yml ├── pyproject. toml ├── requirements.txt ├── requirements_docs.txt └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | __pycache__ 3 | .pytest_cache 4 | .venv 5 | build 6 | dash_express.egg-info 7 | dash_express.egg-info 8 | dist 9 | site 10 | test.py 11 | 12 | .cache -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Kirill Stepanov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Fast analytical web application with DashExpress 2 | 3 | [Documentation](https://stpnvkirill.github.io/dash-express/) 4 | 5 | Build your next dashboard even faster with premade responsive UI and automatic callback-function. DashExpress is a wrapper over the Plotly Dash web framework, which allows you to simplify and speed up the creation of multi-page analytical applications based on data from pd.DataFrame. 6 | 7 | ```console 8 | pip install dash-express 9 | ``` 10 | 11 | Currently supported: Charts, KPI, Geographical Maps 12 | 13 | The key features are: 14 | 15 | * **High Performance**: Provided by built-in optimization methods of Dash callback functions. 16 | * **Fast to code**: Using a pre-configured UI and automatically generated callback functions. 17 | * **Based on Pandas**: A library familiar to all analysts. 18 | * **Used Mantine UI**: Pretty UI by Mantine React Library. 19 | * **Include Dark Theme**: Use a dark theme for all components (including graphs and maps) without any additional actions. 20 | 21 | 22 | ## Minimal full-featured dashboard 23 | 24 | ![Image title](https://raw.githubusercontent.com/stpnvkirill/dash-express/main/docs/assets/gifs/min_app.gif) 25 | 26 | The first step is to import the necessary libraries 27 | 28 | ```python 29 | import pandas as pd 30 | import plotly.graph_objects as go 31 | import dash_mantine_components as dmc 32 | 33 | from dash_express import DashExpress, Page 34 | ``` 35 | 36 | Next, you need to initialize an instance of the Dash Express application. 37 | 38 | ```python 39 | app = DashExpress( 40 | logo='DashExpress', # navbar logo, string or dict: {'dark':'path/to/darklogo.svg', 'light':...} 41 | cache=True, # flask_caching.Cache instance, dict or True (default: True) 42 | default_cache_timeout=3600, # flask_caching.Cache timeout in seconds (default: 3600) 43 | app_shell=..., # Appshell class for customization UI your app (default: BaseAppShell()) 44 | # And standart Plotly Dash param 45 | ) 46 | ``` 47 | 48 | The Dash Express object implements a Dash application with a pre-configured interface and automatic callback generation for quickly creating interactive multi-page web analytics applications. 49 | 50 | ## Page definition 51 | 52 | Each application page is a separate object, an instance of the `dash_express' class.Page`. The page contains the source data for analysis, graph creation functions and a list of filters. 53 | 54 | 55 | ```python 56 | page = Page( 57 | app=app, # DashExpress app 58 | url_path='/', # page url 59 | name='Owerview', # page name in navigation buttons 60 | get_df=get_df, # function for getting pd.DataFrame 61 | title='Owerview', # page title 62 | ) 63 | ``` 64 | 65 | ## Getting data 66 | 67 | The 'get_df` function contains the logic of getting data: 68 | 69 | ```python 70 | get_df = lambda: pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/gapminder2007.csv') 71 | ``` 72 | 73 | ## Dashboard layout 74 | 75 | Next, you need to determine the layout of the dashboard. we recommend using dmc.Grid and dmc.SimpleGrid 76 | 77 | ```python 78 | page.layout = dmc.SimpleGrid( 79 | page.add_graph(h='100%',render_func=bar_func) 80 | ) 81 | ``` 82 | 83 | The render_func parameter of the page.add_graph method is a graph generation function based on data from a DataFrame 84 | 85 | ```python 86 | # The logic of drawing a graph 87 | def bar_func(df): 88 | pv = pd.pivot_table(df, index='continent', values='lifeExp').reset_index() 89 | fig = go.Figure([go.Bar(x=pv['continent'], y=pv['lifeExp'])]) 90 | return fig 91 | ``` 92 | 93 | The last action is to add filters, which is done by simply calling the page.add_filter method and specifying the filtering column. 94 | 95 | ```python 96 | page.add_autofilter('continent', multi=True) 97 | page.add_autofilter('country', multi=True) 98 | page.add_autofilter('lifeExp', multi=True) 99 | ``` 100 | 101 | ## App run 102 | 103 | These actions are enough to create a fully functional dashboard, so you can run the application. 104 | 105 | 106 | ```python 107 | app.run() 108 | ``` 109 | 110 | ## Full code of the minimal application 111 | 112 | ```python 113 | import pandas as pd 114 | import plotly.graph_objects as go 115 | import dash_mantine_components as dmc 116 | 117 | from dash_express import DashExpress, Page 118 | 119 | 120 | # Incorporate data 121 | get_df = lambda: pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/gapminder2007.csv') 122 | 123 | # Initialize the app 124 | app = DashExpress(logo='DashExpress') 125 | 126 | # Initialize the Page 127 | page = Page( 128 | app=app, # DashExpress app 129 | url_path='/', # page url 130 | name='Owerview', # page name in navigation buttons 131 | get_df=get_df, # function for getting pd.DataFrame 132 | title='Owerview', # page title 133 | ) 134 | 135 | # The logic of drawing a graph 136 | def bar_func(df): 137 | pv = pd.pivot_table(df, index='continent', values='lifeExp').reset_index() 138 | fig = go.Figure([go.Bar(x=pv['continent'], y=pv['lifeExp'])]) 139 | return fig 140 | 141 | # Dashboard layout 142 | page.layout = dmc.SimpleGrid( 143 | page.add_graph(h='calc(100vh - 138px)',render_func=bar_func) 144 | ) 145 | 146 | # By which columns to filter 147 | page.add_autofilter('continent', multi=True) 148 | page.add_autofilter('country', multi=True) 149 | page.add_autofilter('lifeExp', multi=True) 150 | 151 | app.run(debug=True) 152 | ``` 153 | 154 | ## Requirements 155 | 156 | Python 3.7+ 157 | 158 | DashExpress stands on the shoulders of giants: 159 | 160 | * Plotly Dash for the web parts. 161 | * Pandas DataFrame for the data store & compute measure. 162 | * Dash Mantine Components for the create pretty UI 163 | * Dash Leaflet for the create maps 164 | 165 | ## License 166 | 167 | This project is licensed under the terms of the MIT license. -------------------------------------------------------------------------------- /dash_express/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | import uuid 4 | import random 5 | import orjson 6 | 7 | import numpy as np 8 | import pandas as pd 9 | import dash_leaflet as dl 10 | import plotly.graph_objects as go 11 | import dash_mantine_components as dmc 12 | 13 | 14 | from .version import V 15 | from .kpi import KPI, FastKPI 16 | from .filters import autofilter 17 | from flask_caching import Cache 18 | from dash_iconify import DashIconify 19 | from .preview_chart import _render_wrapper 20 | from dash.exceptions import PreventUpdate 21 | from dash._jupyter import JupyterDisplayMode 22 | from ._app_shell import BaseAppShell, AsideAppShell 23 | from dash import Dash, Output, Input, State, ALL, dcc, html, Patch, MATCH 24 | 25 | 26 | _default_index = """ 27 | 28 | 29 | {%metas%} 30 | {%title%} 31 | {%favicon%} 32 | {%css%} 33 | 34 | 35 | 38 | {%app_entry%} 39 | 44 | 45 | """ 46 | 47 | 48 | class DashExpress(Dash): 49 | """The DashExpress object implements a Dash application with a pre-configured 50 | interface and automatic generation of callbacks to quickly create interactive 51 | multi-page web analytics applications. 52 | 53 | DashExpress uses the Mantine framework and supports dark theme out of the box. 54 | Additionally on the side of DashExpress dark is applied to the objects of PLotly figures and Leaflet maps. 55 | 56 | Dash is a framework for building analytic web applications without 57 | the use of JavaScript. 58 | 59 | :param logo: navbar logo, string or dict: {'dark':'path/to/darklogo.svg', 'light':...} 60 | :type logo: string or dict 61 | 62 | :param cache: flask_caching.Cache instance, dict or True (default: True) 63 | :type cache: flask_caching.Cache instance, dict or True 64 | 65 | :param default_cache_timeout: flask_caching.Cache timeout in seconds (default: 3600) 66 | :type default_cache_timeout: int 67 | 68 | :param app_shell: Appshell class for customization UI your app 69 | :type app_shell: AppShell instance 70 | 71 | ------------------------------- 72 | 73 | :param name: The name Flask should use for your app. Even if you provide 74 | your own ``server``, ``name`` will be used to help find assets. 75 | Typically ``__name__`` (the magic global var, not a string) is the 76 | best value to use. Default ``'__main__'``, env: ``DASH_APP_NAME`` 77 | :type name: string 78 | 79 | :param server: Sets the Flask server for your app. There are three options: 80 | ``True`` (default): Dash will create a new server 81 | ``False``: The server will be added later via ``app.init_app(server)`` 82 | where ``server`` is a ``flask.Flask`` instance. 83 | ``flask.Flask``: use this pre-existing Flask server. 84 | :type server: boolean or flask.Flask 85 | 86 | :param assets_folder: a path, relative to the current working directory, 87 | for extra files to be used in the browser. Default ``'assets'``. 88 | All .js and .css files will be loaded immediately unless excluded by 89 | ``assets_ignore``, and other files such as images will be served if 90 | requested. 91 | :type assets_folder: string 92 | 93 | :param pages_folder: a relative or absolute path for pages of a multi-page app. 94 | Default ``'pages'``. 95 | :type pages_folder: string or pathlib.Path 96 | 97 | :param use_pages: When True, the ``pages`` feature for multi-page apps is 98 | enabled. If you set a non-default ``pages_folder`` this will be inferred 99 | to be True. Default `None`. 100 | :type use_pages: boolean 101 | 102 | :param include_pages_meta: Include the page meta tags for twitter cards. 103 | :type include_pages_meta: bool 104 | 105 | :param assets_url_path: The local urls for assets will be: 106 | ``requests_pathname_prefix + assets_url_path + '/' + asset_path`` 107 | where ``asset_path`` is the path to a file inside ``assets_folder``. 108 | Default ``'assets'``. 109 | :type asset_url_path: string 110 | 111 | :param assets_ignore: A regex, as a string to pass to ``re.compile``, for 112 | assets to omit from immediate loading. Ignored files will still be 113 | served if specifically requested. You cannot use this to prevent access 114 | to sensitive files. 115 | :type assets_ignore: string 116 | 117 | :param assets_external_path: an absolute URL from which to load assets. 118 | Use with ``serve_locally=False``. assets_external_path is joined 119 | with assets_url_path to determine the absolute url to the 120 | asset folder. Dash can still find js and css to automatically load 121 | if you also keep local copies in your assets folder that Dash can index, 122 | but external serving can improve performance and reduce load on 123 | the Dash server. 124 | env: ``DASH_ASSETS_EXTERNAL_PATH`` 125 | :type assets_external_path: string 126 | 127 | :param include_assets_files: Default ``True``, set to ``False`` to prevent 128 | immediate loading of any assets. Assets will still be served if 129 | specifically requested. You cannot use this to prevent access 130 | to sensitive files. env: ``DASH_INCLUDE_ASSETS_FILES`` 131 | :type include_assets_files: boolean 132 | 133 | :param url_base_pathname: A local URL prefix to use app-wide. 134 | Default ``'/'``. Both `requests_pathname_prefix` and 135 | `routes_pathname_prefix` default to `url_base_pathname`. 136 | env: ``DASH_URL_BASE_PATHNAME`` 137 | :type url_base_pathname: string 138 | 139 | :param requests_pathname_prefix: A local URL prefix for file requests. 140 | Defaults to `url_base_pathname`, and must end with 141 | `routes_pathname_prefix`. env: ``DASH_REQUESTS_PATHNAME_PREFIX`` 142 | :type requests_pathname_prefix: string 143 | 144 | :param routes_pathname_prefix: A local URL prefix for JSON requests. 145 | Defaults to ``url_base_pathname``, and must start and end 146 | with ``'/'``. env: ``DASH_ROUTES_PATHNAME_PREFIX`` 147 | :type routes_pathname_prefix: string 148 | 149 | :param serve_locally: If ``True`` (default), assets and dependencies 150 | (Dash and Component js and css) will be served from local URLs. 151 | If ``False`` we will use CDN links where available. 152 | :type serve_locally: boolean 153 | 154 | :param compress: Use gzip to compress files and data served by Flask. 155 | To use this option, you need to install dash[compress] 156 | Default ``False`` 157 | :type compress: boolean 158 | 159 | :param meta_tags: html tags to be added to the index page. 160 | Each dict should have the attributes and values for one tag, eg: 161 | ``{'name': 'description', 'content': 'My App'}`` 162 | :type meta_tags: list of dicts 163 | 164 | :param index_string: Override the standard Dash index page. 165 | Must contain the correct insertion markers to interpolate various 166 | content into it depending on the app config and components used. 167 | See https://dash.plotly.com/external-resources for details. 168 | :type index_string: string 169 | 170 | :param external_scripts: Additional JS files to load with the page. 171 | Each entry can be a string (the URL) or a dict with ``src`` (the URL) 172 | and optionally other ``