From 4fab8433a3d3d7ff3a48cb08e87d240ae7047247 Mon Sep 17 00:00:00 2001 From: Antony Milne Date: Tue, 7 Nov 2023 16:21:45 +0000 Subject: [PATCH 1/9] Platform McK fix? --- vizro-core/examples/default/app.py | 19 ++++++++- vizro-core/src/vizro/__init__.py | 2 +- vizro-core/src/vizro/_vizro.py | 50 +++++++++++++++++------ vizro-core/src/vizro/models/_dashboard.py | 4 +- 4 files changed, 58 insertions(+), 17 deletions(-) diff --git a/vizro-core/examples/default/app.py b/vizro-core/examples/default/app.py index 3f7878595..d56e101d6 100644 --- a/vizro-core/examples/default/app.py +++ b/vizro-core/examples/default/app.py @@ -1,6 +1,8 @@ """Example to show dashboard configuration.""" import os +import dash +import flask import pandas as pd import vizro.models as vm @@ -530,7 +532,20 @@ def create_home_page(): } ), ) +# Vizro._user_assets_folder = os.path.abspath("../assets") +vizro_app = Vizro(assets_folder="../assets/").build(dashboard) +# url_base_pathname="/dashboard/" + +app = vizro_app.dash.server +# debug=False, +# use_reloader=False) if __name__ == "__main__": - Vizro._user_assets_folder = os.path.abspath("../assets") - Vizro().build(dashboard).run() + vizro_app.run() +# server = flask.Flask(__name__) +# +# @server.route("/") +# def home(): +# return "Hello, Flask!" + +# app = dash.Dash(name=__name__, server=server, url_base_pathname="/dash/") diff --git a/vizro-core/src/vizro/__init__.py b/vizro-core/src/vizro/__init__.py index 081b799c9..c865f9274 100644 --- a/vizro-core/src/vizro/__init__.py +++ b/vizro-core/src/vizro/__init__.py @@ -5,6 +5,6 @@ __all__ = ["Vizro"] -__version__ = "0.1.6.dev0" +__version__ = "0.1.6.dev1" logging.basicConfig(level=os.getenv("VIZRO_LOG_LEVEL", "WARNING")) diff --git a/vizro-core/src/vizro/_vizro.py b/vizro-core/src/vizro/_vizro.py index 8ed00b326..ff450ff76 100644 --- a/vizro-core/src/vizro/_vizro.py +++ b/vizro-core/src/vizro/_vizro.py @@ -16,24 +16,50 @@ class Vizro: """The main class of the `vizro` package.""" - _user_assets_folder = Path.cwd() / "assets" + # _user_assets_folder = Path.cwd() / "assets" _lib_assets_folder = os.path.join(os.path.dirname(os.path.abspath(__file__)), "static") - def __init__(self): + def __init__(self, **kwargs): """Initializes Dash.""" - _js, _css = _append_styles(self._lib_assets_folder, STATIC_URL_PREFIX) + + # assets_url_path: The local urls for assets will be: + # ``requests_pathname_prefix + assets_url_path + '/' + asset_path`` + # where ``asset_path`` is the path to a file inside ``assets_folder``. + # Both `requests_pathname_prefix` and + # `routes_pathname_prefix` default to `url_base_pathname`. + # self.dash.config.requests_pathname_prefix self.dash = dash.Dash( + **kwargs, use_pages=True, pages_folder="", - external_scripts=_js, - external_stylesheets=_css, - assets_folder=self._user_assets_folder, + # external_scripts=_js, + # external_stylesheets=_css, + # assets_folder=self._user_assets_folder, + ) + + _js, _css = _append_styles(self._lib_assets_folder, self.dash.config.requests_pathname_prefix + "vizro") + self.dash.config.external_scripts.extend(_js) + self.dash.config.external_stylesheets.extend(_css) + + config = self.dash.config + bp_prefix = config.routes_pathname_prefix.replace("/", "_").replace(".", "_") + assets_blueprint_name = f"{bp_prefix}vizro_assets" + + self.dash.server.register_blueprint( + flask.Blueprint( + assets_blueprint_name, + config.name, + static_folder=self._lib_assets_folder, + static_url_path=config.routes_pathname_prefix + "vizro", + ) ) - @self.dash.server.route("//") - def serve_static(filepath, url_prefix=STATIC_URL_PREFIX): - """Serve vizro static contents.""" - return flask.send_from_directory(self._lib_assets_folder, filepath) + # @self.dash.server.route("//") + # def serve_static(filepath, url_prefix=STATIC_URL_PREFIX): + # """Serve vizro static contents.""" + # # Directs requests to /vizro/X to vizro/src/static/X + # # But does not include url_base_pathname + # return flask.send_from_directory(self._lib_assets_folder, filepath) def build(self, dashboard: Dashboard): """Builds the dashboard. @@ -97,10 +123,10 @@ def _append_styles(walk_dir: str, url_prefix: str) -> Tuple[List[Dict[str, str]] for current_dir, _, files in sorted(os.walk(walk_dir)): base = "" if current_dir == walk_dir else os.path.relpath(current_dir, walk_dir).replace("\\", "/") for f in sorted(files): - path = os.path.join("/" + url_prefix, base, f) if base else os.path.join("/" + url_prefix, f) + path = os.path.join(url_prefix, base, f) if base else os.path.join(url_prefix, f) extension = os.path.splitext(f)[1] if extension == ".js": - _vizro_js.append({"src": path, "type": "module"}) + _vizro_js.append({"src": path, "type": "module"}) # what is this? elif extension == ".css": _vizro_css.append(path) return _vizro_js, _vizro_css diff --git a/vizro-core/src/vizro/models/_dashboard.py b/vizro-core/src/vizro/models/_dashboard.py index cf6fbab63..a213c5be5 100644 --- a/vizro-core/src/vizro/models/_dashboard.py +++ b/vizro-core/src/vizro/models/_dashboard.py @@ -8,7 +8,7 @@ import dash_bootstrap_components as dbc import dash_daq as daq import plotly.io as pio -from dash import ClientsideFunction, Input, Output, clientside_callback, html +from dash import ClientsideFunction, Input, Output, clientside_callback, html, get_relative_path from pydantic import Field, validator import vizro @@ -139,7 +139,7 @@ def _update_theme(): def _make_page_404_layout(): return html.Div( [ - html.Img(src=STATIC_URL_PREFIX + "/images/errors/error_404.svg"), + html.Img(src=get_relative_path("/" + STATIC_URL_PREFIX + "/images/errors/error_404.svg")), html.Div( [ html.Div( From 0323a5d51bd81ec12287d88e465644d4f6af6e17 Mon Sep 17 00:00:00 2001 From: Antony Milne Date: Wed, 8 Nov 2023 17:46:17 +0000 Subject: [PATCH 2/9] Tidy time --- vizro-core/examples/default/app.py | 19 +---- vizro-core/examples/from_dict/app.py | 5 +- vizro-core/examples/from_json/app.py | 4 +- vizro-core/examples/from_yaml/app.py | 4 +- vizro-core/src/vizro/__init__.py | 2 +- vizro-core/src/vizro/_vizro.py | 85 +++++++------------ .../src/vizro/models/_components/card.py | 6 +- vizro-core/src/vizro/models/_dashboard.py | 4 +- 8 files changed, 40 insertions(+), 89 deletions(-) diff --git a/vizro-core/examples/default/app.py b/vizro-core/examples/default/app.py index d56e101d6..acc1d7870 100644 --- a/vizro-core/examples/default/app.py +++ b/vizro-core/examples/default/app.py @@ -532,20 +532,5 @@ def create_home_page(): } ), ) -# Vizro._user_assets_folder = os.path.abspath("../assets") -vizro_app = Vizro(assets_folder="../assets/").build(dashboard) -# url_base_pathname="/dashboard/" - -app = vizro_app.dash.server -# debug=False, -# use_reloader=False) - -if __name__ == "__main__": - vizro_app.run() -# server = flask.Flask(__name__) -# -# @server.route("/") -# def home(): -# return "Hello, Flask!" - -# app = dash.Dash(name=__name__, server=server, url_base_pathname="/dash/") + +app = Vizro(assets_folder="../assets").build(dashboard).run() diff --git a/vizro-core/examples/from_dict/app.py b/vizro-core/examples/from_dict/app.py index 804610ecc..156afb345 100644 --- a/vizro-core/examples/from_dict/app.py +++ b/vizro-core/examples/from_dict/app.py @@ -580,8 +580,7 @@ def retrieve_avg_gapminder_year(year: int): } }, } + dashboard = Dashboard(**dashboard) -if __name__ == "__main__": - Vizro._user_assets_folder = os.path.abspath("../assets") - Vizro().build(dashboard).run() +app = Vizro(assets_folder="../assets").build(dashboard).run() diff --git a/vizro-core/examples/from_json/app.py b/vizro-core/examples/from_json/app.py index bedaf58ca..e8e04045f 100644 --- a/vizro-core/examples/from_json/app.py +++ b/vizro-core/examples/from_json/app.py @@ -62,6 +62,4 @@ def retrieve_avg_gapminder_year(year: int): dashboard = json.loads(Path("dashboard.json").read_text(encoding="utf-8")) dashboard = Dashboard(**dashboard) -if __name__ == "__main__": - Vizro._user_assets_folder = os.path.abspath("../assets") - Vizro().build(dashboard).run() +app = Vizro(assets_folder="../assets").build(dashboard).run() diff --git a/vizro-core/examples/from_yaml/app.py b/vizro-core/examples/from_yaml/app.py index 8e53c3f65..3775533aa 100644 --- a/vizro-core/examples/from_yaml/app.py +++ b/vizro-core/examples/from_yaml/app.py @@ -62,6 +62,4 @@ def retrieve_avg_gapminder_year(year: int): dashboard = yaml.safe_load(Path("dashboard.yaml").read_text(encoding="utf-8")) dashboard = Dashboard(**dashboard) -if __name__ == "__main__": - Vizro._user_assets_folder = os.path.abspath("../assets") - Vizro().build(dashboard).run() +app = Vizro(assets_folder="../assets").build(dashboard).run() diff --git a/vizro-core/src/vizro/__init__.py b/vizro-core/src/vizro/__init__.py index c865f9274..a6efaeb42 100644 --- a/vizro-core/src/vizro/__init__.py +++ b/vizro-core/src/vizro/__init__.py @@ -5,6 +5,6 @@ __all__ = ["Vizro"] -__version__ = "0.1.6.dev1" +__version__ = "0.1.6.dev2" logging.basicConfig(level=os.getenv("VIZRO_LOG_LEVEL", "WARNING")) diff --git a/vizro-core/src/vizro/_vizro.py b/vizro-core/src/vizro/_vizro.py index ff450ff76..7f2fb6b15 100644 --- a/vizro-core/src/vizro/_vizro.py +++ b/vizro-core/src/vizro/_vizro.py @@ -1,6 +1,6 @@ import logging import os -from pathlib import Path +from pathlib import Path, PosixPath from typing import Dict, List, Tuple import dash @@ -16,51 +16,35 @@ class Vizro: """The main class of the `vizro` package.""" - # _user_assets_folder = Path.cwd() / "assets" - _lib_assets_folder = os.path.join(os.path.dirname(os.path.abspath(__file__)), "static") - def __init__(self, **kwargs): """Initializes Dash.""" - - # assets_url_path: The local urls for assets will be: - # ``requests_pathname_prefix + assets_url_path + '/' + asset_path`` - # where ``asset_path`` is the path to a file inside ``assets_folder``. - # Both `requests_pathname_prefix` and - # `routes_pathname_prefix` default to `url_base_pathname`. - # self.dash.config.requests_pathname_prefix - self.dash = dash.Dash( - **kwargs, - use_pages=True, - pages_folder="", - # external_scripts=_js, - # external_stylesheets=_css, - # assets_folder=self._user_assets_folder, - ) - - _js, _css = _append_styles(self._lib_assets_folder, self.dash.config.requests_pathname_prefix + "vizro") - self.dash.config.external_scripts.extend(_js) - self.dash.config.external_stylesheets.extend(_css) - - config = self.dash.config - bp_prefix = config.routes_pathname_prefix.replace("/", "_").replace(".", "_") - assets_blueprint_name = f"{bp_prefix}vizro_assets" - + self.dash = dash.Dash(**kwargs, use_pages=True, pages_folder="") + + # Include Vizro assets (in the static folder) as external scripts and stylesheets. We extend self.dash.config + # objects so the user can specify additional external_scripts and externaL_stylesheets via kwargs. + vizro_assets_folder = Path(__file__).with_name("static") + static_url_path = self.dash.config.requests_pathname_prefix + STATIC_URL_PREFIX + vizro_css = self._get_external_assets(static_url_path, vizro_assets_folder, "css") + vizro_js = [ + {"src": path, "type": "module"} + for path in self._get_external_assets(static_url_path, vizro_assets_folder, "js") + ] + self.dash.config.external_stylesheets.extend(vizro_css) + self.dash.config.external_scripts.extend(vizro_js) + + # Serve all assets (including files other than css and js) that live in vizro_assets_folder at the + # route /vizro. Based on code in Dash.init_app that serves assets_folder. This respects the case that the + # dashboard is not hosted at the root of the server, e.g. http://www.example.com/dashboard/vizro. + blueprint_prefix = self.dash.config.routes_pathname_prefix.replace("/", "_").replace(".", "_") self.dash.server.register_blueprint( flask.Blueprint( - assets_blueprint_name, - config.name, - static_folder=self._lib_assets_folder, - static_url_path=config.routes_pathname_prefix + "vizro", + f"{blueprint_prefix}vizro_assets", + self.dash.config.name, + static_folder=vizro_assets_folder, + static_url_path=static_url_path, ) ) - # @self.dash.server.route("//") - # def serve_static(filepath, url_prefix=STATIC_URL_PREFIX): - # """Serve vizro static contents.""" - # # Directs requests to /vizro/X to vizro/src/static/X - # # But does not include url_base_pathname - # return flask.send_from_directory(self._lib_assets_folder, filepath) - def build(self, dashboard: Dashboard): """Builds the dashboard. @@ -114,19 +98,10 @@ def _reset(): dash._pages.CONFIG.clear() dash._pages.CONFIG.__dict__.clear() - -def _append_styles(walk_dir: str, url_prefix: str) -> Tuple[List[Dict[str, str]], List[str]]: - """Append vizro css and js resources.""" - _vizro_css = [] - _vizro_js = [] - - for current_dir, _, files in sorted(os.walk(walk_dir)): - base = "" if current_dir == walk_dir else os.path.relpath(current_dir, walk_dir).replace("\\", "/") - for f in sorted(files): - path = os.path.join(url_prefix, base, f) if base else os.path.join(url_prefix, f) - extension = os.path.splitext(f)[1] - if extension == ".js": - _vizro_js.append({"src": path, "type": "module"}) # what is this? - elif extension == ".css": - _vizro_css.append(path) - return _vizro_js, _vizro_css + @staticmethod + def _get_external_assets(new_path: str, folder: Path, extension: str) -> List[str]: + """Returns a list of paths to assets with given extension in folder, prefixed with new_path. + e.g. with new_path="/vizro", extension="css", folder="/path/to/vizro/vizro-core/src/vizro/static", + we will get ["/vizro/css/accordion.css", "/vizro/css/button.css", ...] + """ + return sorted((new_path / path.relative_to(folder)).as_posix() for path in folder.rglob(f"*.{extension}")) diff --git a/vizro-core/src/vizro/models/_components/card.py b/vizro-core/src/vizro/models/_components/card.py index 01e219854..05600253d 100644 --- a/vizro-core/src/vizro/models/_components/card.py +++ b/vizro-core/src/vizro/models/_components/card.py @@ -43,8 +43,4 @@ def build(self): ) card_container = "nav_card_container" if self.href else "card_container" - return html.Div( - [text, button], - className=card_container, - id=f"{self.id}_outer", - ) + return html.Div([text, button], className=card_container, id=f"{self.id}_outer") diff --git a/vizro-core/src/vizro/models/_dashboard.py b/vizro-core/src/vizro/models/_dashboard.py index a213c5be5..c9a2025c6 100644 --- a/vizro-core/src/vizro/models/_dashboard.py +++ b/vizro-core/src/vizro/models/_dashboard.py @@ -139,7 +139,7 @@ def _update_theme(): def _make_page_404_layout(): return html.Div( [ - html.Img(src=get_relative_path("/" + STATIC_URL_PREFIX + "/images/errors/error_404.svg")), + html.Img(src=get_relative_path(f"/{STATIC_URL_PREFIX}/images/errors/error_404.svg")), html.Div( [ html.Div( @@ -149,7 +149,7 @@ def _make_page_404_layout(): ], className="error_text_container", ), - dbc.Button("Take me home", href="/", className="button_primary"), + dbc.Button("Take me home", href=get_relative_path("/"), className="button_primary"), ], className="error_content_container", ), From 462457c25ea0d52f6332670344a24ddeee83300b Mon Sep 17 00:00:00 2001 From: Antony Milne Date: Wed, 8 Nov 2023 17:48:11 +0000 Subject: [PATCH 3/9] Tidy docs --- vizro-core/docs/pages/user_guides/actions.md | 12 ++++-------- vizro-core/docs/pages/user_guides/custom_charts.md | 6 ++---- .../docs/pages/user_guides/custom_components.md | 6 ++---- vizro-core/docs/pages/user_guides/dashboard.md | 6 ++---- vizro-core/examples/default/app.py | 2 +- vizro-core/examples/from_dict/app.py | 2 +- vizro-core/examples/from_json/app.py | 2 +- vizro-core/examples/from_yaml/app.py | 2 +- 8 files changed, 14 insertions(+), 24 deletions(-) diff --git a/vizro-core/docs/pages/user_guides/actions.md b/vizro-core/docs/pages/user_guides/actions.md index 855402219..d76b3ea77 100644 --- a/vizro-core/docs/pages/user_guides/actions.md +++ b/vizro-core/docs/pages/user_guides/actions.md @@ -63,8 +63,7 @@ a result, when a dashboard user now clicks the button, all data on the page will dashboard = vm.Dashboard(pages=[page]) - if __name__ == "__main__": - Vizro().build(dashboard).run() + Vizro().build(dashboard).run() ``` === "app.yaml" ```yaml @@ -167,8 +166,7 @@ Here is an example of how to configure a chart interaction when the source is a ] ) - if __name__ == "__main__": - Vizro().build(dashboard).run() + Vizro().build(dashboard).run() ``` === "app.yaml" ```yaml @@ -250,8 +248,7 @@ Here is an example of how to configure a chart interaction when the source is a ] ) - if __name__ == "__main__": - Vizro().build(dashboard).run() + Vizro().build(dashboard).run() ``` === "app.yaml" ```yaml @@ -343,8 +340,7 @@ The order of action execution is guaranteed, and the next action in the list wil dashboard = vm.Dashboard(pages=[page]) - if __name__ == "__main__": - Vizro().build(dashboard).run() + Vizro().build(dashboard).run() ``` === "app.yaml" ```yaml diff --git a/vizro-core/docs/pages/user_guides/custom_charts.md b/vizro-core/docs/pages/user_guides/custom_charts.md index 0c8219be6..3dda3f720 100644 --- a/vizro-core/docs/pages/user_guides/custom_charts.md +++ b/vizro-core/docs/pages/user_guides/custom_charts.md @@ -70,8 +70,7 @@ Building on the above, there are several routes one can take. The following exam ) dashboard = vm.Dashboard(pages=[page_0]) - if __name__ == "__main__": - Vizro().build(dashboard).run() + Vizro().build(dashboard).run() ``` === "app.yaml" ```yaml @@ -142,8 +141,7 @@ The below examples shows a more involved use-case. We create and style a waterfa ) dashboard = vm.Dashboard(pages=[page_0]) - if __name__ == "__main__": - Vizro().build(dashboard).run() + Vizro().build(dashboard).run() ``` === "app.yaml" ```yaml diff --git a/vizro-core/docs/pages/user_guides/custom_components.md b/vizro-core/docs/pages/user_guides/custom_components.md index bc61f9e13..f3872edfd 100644 --- a/vizro-core/docs/pages/user_guides/custom_components.md +++ b/vizro-core/docs/pages/user_guides/custom_components.md @@ -207,8 +207,7 @@ vm.Parameter.add_type("selector", TooltipNonCrossRangeSlider) dashboard = vm.Dashboard(pages=[page]) - if __name__ == "__main__": - Vizro().build(dashboard).run() + Vizro().build(dashboard).run() ``` 1. Here we provide a new type for the new component, so it can be distinguished in the discriminated union. @@ -330,8 +329,7 @@ vm.Page.add_type("components", Jumbotron) dashboard = vm.Dashboard(pages=[page]) - if __name__ == "__main__": - Vizro().build(dashboard).run() + Vizro().build(dashboard).run() ``` diff --git a/vizro-core/docs/pages/user_guides/dashboard.md b/vizro-core/docs/pages/user_guides/dashboard.md index c17b3ae51..0088aa2c5 100644 --- a/vizro-core/docs/pages/user_guides/dashboard.md +++ b/vizro-core/docs/pages/user_guides/dashboard.md @@ -168,8 +168,7 @@ To create a dashboard, do the following steps: dashboard = yaml.safe_load(Path("dashboard.yaml").read_text(encoding="utf-8")) dashboard = Dashboard(**dashboard) - if __name__ == "__main__": - Vizro().build(dashboard).run() + Vizro().build(dashboard).run() ``` === "app.py for json" ```py @@ -185,8 +184,7 @@ To create a dashboard, do the following steps: dashboard = json.loads(Path("dashboard.json").read_text(encoding="utf-8")) dashboard = Dashboard(**dashboard) - if __name__ == "__main__": - Vizro().build(dashboard).run() + Vizro().build(dashboard).run() ``` After running the dashboard, you can access the dashboard via `localhost:8050`. diff --git a/vizro-core/examples/default/app.py b/vizro-core/examples/default/app.py index acc1d7870..707013397 100644 --- a/vizro-core/examples/default/app.py +++ b/vizro-core/examples/default/app.py @@ -533,4 +533,4 @@ def create_home_page(): ), ) -app = Vizro(assets_folder="../assets").build(dashboard).run() +Vizro(assets_folder="../assets").build(dashboard).run() diff --git a/vizro-core/examples/from_dict/app.py b/vizro-core/examples/from_dict/app.py index 156afb345..b6f434257 100644 --- a/vizro-core/examples/from_dict/app.py +++ b/vizro-core/examples/from_dict/app.py @@ -583,4 +583,4 @@ def retrieve_avg_gapminder_year(year: int): dashboard = Dashboard(**dashboard) -app = Vizro(assets_folder="../assets").build(dashboard).run() +Vizro(assets_folder="../assets").build(dashboard).run() diff --git a/vizro-core/examples/from_json/app.py b/vizro-core/examples/from_json/app.py index e8e04045f..a4db04352 100644 --- a/vizro-core/examples/from_json/app.py +++ b/vizro-core/examples/from_json/app.py @@ -62,4 +62,4 @@ def retrieve_avg_gapminder_year(year: int): dashboard = json.loads(Path("dashboard.json").read_text(encoding="utf-8")) dashboard = Dashboard(**dashboard) -app = Vizro(assets_folder="../assets").build(dashboard).run() +Vizro(assets_folder="../assets").build(dashboard).run() diff --git a/vizro-core/examples/from_yaml/app.py b/vizro-core/examples/from_yaml/app.py index 3775533aa..2703dace5 100644 --- a/vizro-core/examples/from_yaml/app.py +++ b/vizro-core/examples/from_yaml/app.py @@ -62,4 +62,4 @@ def retrieve_avg_gapminder_year(year: int): dashboard = yaml.safe_load(Path("dashboard.yaml").read_text(encoding="utf-8")) dashboard = Dashboard(**dashboard) -app = Vizro(assets_folder="../assets").build(dashboard).run() +Vizro(assets_folder="../assets").build(dashboard).run() From 6faa194517215c6df9c44d8aa938b9f457e6a549 Mon Sep 17 00:00:00 2001 From: Antony Milne Date: Wed, 8 Nov 2023 22:41:11 +0000 Subject: [PATCH 4/9] Use chromedriver_autoinstaller_fix --- .github/requirements.txt | 2 +- vizro-core/hatch.toml | 2 +- vizro-core/tests/integration/test_examples.py | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/requirements.txt b/.github/requirements.txt index d410eb7ed..47df11979 100644 --- a/.github/requirements.txt +++ b/.github/requirements.txt @@ -3,7 +3,7 @@ pre-commit coverage[toml]>=6.5 pytest dash[testing] -chromedriver-autoinstaller +chromedriver-autoinstaller-fix toml pyyaml openpyxl diff --git a/vizro-core/hatch.toml b/vizro-core/hatch.toml index cec84e1da..615f9c0ab 100644 --- a/vizro-core/hatch.toml +++ b/vizro-core/hatch.toml @@ -20,7 +20,7 @@ dependencies = [ "pytest", "pytest-mock", "dash[testing]", - "chromedriver-autoinstaller", + "chromedriver-autoinstaller-fix", "toml", "pyyaml", "openpyxl" diff --git a/vizro-core/tests/integration/test_examples.py b/vizro-core/tests/integration/test_examples.py index 727a9ed55..85885b373 100644 --- a/vizro-core/tests/integration/test_examples.py +++ b/vizro-core/tests/integration/test_examples.py @@ -2,7 +2,7 @@ import os from pathlib import Path -import chromedriver_autoinstaller +import chromedriver_autoinstaller_fix import pytest from vizro import Vizro @@ -22,7 +22,7 @@ def setup_integration_test_environment(monkeypatch_session): monkeypatch_session.setenv("DASH_DEBUG", "false") # We only need to install chromedriver outside CI. if not os.getenv("CI"): - chromedriver_autoinstaller.install() + chromedriver_autoinstaller_fix.install() @pytest.fixture From 2af705d236a389ab8d3190c6bf6ff83627a5ee68 Mon Sep 17 00:00:00 2001 From: Antony Milne Date: Wed, 8 Nov 2023 22:48:12 +0000 Subject: [PATCH 5/9] Revert changes to examples so that integration tests still pass --- vizro-core/examples/default/app.py | 3 ++- vizro-core/examples/from_dict/app.py | 3 ++- vizro-core/examples/from_json/app.py | 3 ++- vizro-core/examples/from_yaml/app.py | 3 ++- vizro-core/tests/integration/test_examples.py | 1 + 5 files changed, 9 insertions(+), 4 deletions(-) diff --git a/vizro-core/examples/default/app.py b/vizro-core/examples/default/app.py index 707013397..65764fcd8 100644 --- a/vizro-core/examples/default/app.py +++ b/vizro-core/examples/default/app.py @@ -533,4 +533,5 @@ def create_home_page(): ), ) -Vizro(assets_folder="../assets").build(dashboard).run() +if __name__ == "__main__": + Vizro(assets_folder="../assets").build(dashboard).run() diff --git a/vizro-core/examples/from_dict/app.py b/vizro-core/examples/from_dict/app.py index b6f434257..6e41bbe53 100644 --- a/vizro-core/examples/from_dict/app.py +++ b/vizro-core/examples/from_dict/app.py @@ -583,4 +583,5 @@ def retrieve_avg_gapminder_year(year: int): dashboard = Dashboard(**dashboard) -Vizro(assets_folder="../assets").build(dashboard).run() +if __name__ == "__main__": + Vizro(assets_folder="../assets").build(dashboard).run() diff --git a/vizro-core/examples/from_json/app.py b/vizro-core/examples/from_json/app.py index a4db04352..282034888 100644 --- a/vizro-core/examples/from_json/app.py +++ b/vizro-core/examples/from_json/app.py @@ -62,4 +62,5 @@ def retrieve_avg_gapminder_year(year: int): dashboard = json.loads(Path("dashboard.json").read_text(encoding="utf-8")) dashboard = Dashboard(**dashboard) -Vizro(assets_folder="../assets").build(dashboard).run() +if __name__ == "__main__": + Vizro(assets_folder="../assets").build(dashboard).run() diff --git a/vizro-core/examples/from_yaml/app.py b/vizro-core/examples/from_yaml/app.py index 2703dace5..eb74a043f 100644 --- a/vizro-core/examples/from_yaml/app.py +++ b/vizro-core/examples/from_yaml/app.py @@ -62,4 +62,5 @@ def retrieve_avg_gapminder_year(year: int): dashboard = yaml.safe_load(Path("dashboard.yaml").read_text(encoding="utf-8")) dashboard = Dashboard(**dashboard) -Vizro(assets_folder="../assets").build(dashboard).run() +if __name__ == "__main__": + Vizro(assets_folder="../assets").build(dashboard).run() diff --git a/vizro-core/tests/integration/test_examples.py b/vizro-core/tests/integration/test_examples.py index 85885b373..f543e0928 100644 --- a/vizro-core/tests/integration/test_examples.py +++ b/vizro-core/tests/integration/test_examples.py @@ -23,6 +23,7 @@ def setup_integration_test_environment(monkeypatch_session): # We only need to install chromedriver outside CI. if not os.getenv("CI"): chromedriver_autoinstaller_fix.install() + print("Done setting up integration test environment.") @pytest.fixture From 9110267582b55631ef9b68592e39bb339c85aa11 Mon Sep 17 00:00:00 2001 From: Antony Milne Date: Wed, 8 Nov 2023 23:08:29 +0000 Subject: [PATCH 6/9] Revert changes to examples so that integration tests still pass --- ...230816_antony.milne_fix_assets_pathname.md | 48 +++++++++++++++++++ vizro-core/examples/default/app.py | 3 -- vizro-core/examples/from_dict/app.py | 1 - vizro-core/examples/from_json/app.py | 1 - vizro-core/examples/from_yaml/app.py | 1 - vizro-core/src/vizro/_vizro.py | 8 ++-- vizro-core/src/vizro/models/_dashboard.py | 2 +- vizro-core/tests/integration/test_examples.py | 1 - .../tests/unit/vizro/models/test_dashboard.py | 3 +- 9 files changed, 55 insertions(+), 13 deletions(-) create mode 100644 vizro-core/changelog.d/20231108_230816_antony.milne_fix_assets_pathname.md diff --git a/vizro-core/changelog.d/20231108_230816_antony.milne_fix_assets_pathname.md b/vizro-core/changelog.d/20231108_230816_antony.milne_fix_assets_pathname.md new file mode 100644 index 000000000..f1f65e73c --- /dev/null +++ b/vizro-core/changelog.d/20231108_230816_antony.milne_fix_assets_pathname.md @@ -0,0 +1,48 @@ + + + + + + + + + diff --git a/vizro-core/examples/default/app.py b/vizro-core/examples/default/app.py index 65764fcd8..3bcce552b 100644 --- a/vizro-core/examples/default/app.py +++ b/vizro-core/examples/default/app.py @@ -1,8 +1,5 @@ """Example to show dashboard configuration.""" -import os -import dash -import flask import pandas as pd import vizro.models as vm diff --git a/vizro-core/examples/from_dict/app.py b/vizro-core/examples/from_dict/app.py index 6e41bbe53..f36641ef6 100644 --- a/vizro-core/examples/from_dict/app.py +++ b/vizro-core/examples/from_dict/app.py @@ -1,5 +1,4 @@ """Example to show dashboard configuration specified as a dictionary.""" -import os import pandas as pd diff --git a/vizro-core/examples/from_json/app.py b/vizro-core/examples/from_json/app.py index 282034888..900914bf3 100644 --- a/vizro-core/examples/from_json/app.py +++ b/vizro-core/examples/from_json/app.py @@ -1,6 +1,5 @@ """Example to show dashboard configuration specified as a JSON file.""" import json -import os from pathlib import Path import pandas as pd diff --git a/vizro-core/examples/from_yaml/app.py b/vizro-core/examples/from_yaml/app.py index eb74a043f..4ab29d514 100644 --- a/vizro-core/examples/from_yaml/app.py +++ b/vizro-core/examples/from_yaml/app.py @@ -1,5 +1,4 @@ """Example to show dashboard configuration specified as a YAML file.""" -import os from pathlib import Path import pandas as pd diff --git a/vizro-core/src/vizro/_vizro.py b/vizro-core/src/vizro/_vizro.py index 7f2fb6b15..907197449 100644 --- a/vizro-core/src/vizro/_vizro.py +++ b/vizro-core/src/vizro/_vizro.py @@ -1,7 +1,6 @@ import logging -import os -from pathlib import Path, PosixPath -from typing import Dict, List, Tuple +from pathlib import Path +from typing import List import dash import flask @@ -101,7 +100,8 @@ def _reset(): @staticmethod def _get_external_assets(new_path: str, folder: Path, extension: str) -> List[str]: """Returns a list of paths to assets with given extension in folder, prefixed with new_path. + e.g. with new_path="/vizro", extension="css", folder="/path/to/vizro/vizro-core/src/vizro/static", - we will get ["/vizro/css/accordion.css", "/vizro/css/button.css", ...] + we will get ["/vizro/css/accordion.css", "/vizro/css/button.css", ...]. """ return sorted((new_path / path.relative_to(folder)).as_posix() for path in folder.rglob(f"*.{extension}")) diff --git a/vizro-core/src/vizro/models/_dashboard.py b/vizro-core/src/vizro/models/_dashboard.py index c9a2025c6..11d482706 100644 --- a/vizro-core/src/vizro/models/_dashboard.py +++ b/vizro-core/src/vizro/models/_dashboard.py @@ -8,7 +8,7 @@ import dash_bootstrap_components as dbc import dash_daq as daq import plotly.io as pio -from dash import ClientsideFunction, Input, Output, clientside_callback, html, get_relative_path +from dash import ClientsideFunction, Input, Output, clientside_callback, get_relative_path, html from pydantic import Field, validator import vizro diff --git a/vizro-core/tests/integration/test_examples.py b/vizro-core/tests/integration/test_examples.py index f543e0928..85885b373 100644 --- a/vizro-core/tests/integration/test_examples.py +++ b/vizro-core/tests/integration/test_examples.py @@ -23,7 +23,6 @@ def setup_integration_test_environment(monkeypatch_session): # We only need to install chromedriver outside CI. if not os.getenv("CI"): chromedriver_autoinstaller_fix.install() - print("Done setting up integration test environment.") @pytest.fixture diff --git a/vizro-core/tests/unit/vizro/models/test_dashboard.py b/vizro-core/tests/unit/vizro/models/test_dashboard.py index 49ea53cdf..43353b85d 100644 --- a/vizro-core/tests/unit/vizro/models/test_dashboard.py +++ b/vizro-core/tests/unit/vizro/models/test_dashboard.py @@ -142,7 +142,8 @@ def test_dashboard_page_registry(self, dashboard, mock_page_registry): # Str conversion required as comparison of OrderedDict values result in False otherwise assert str(result.items()) == str(expected.items()) - def test_create_layout_page_404(self, dashboard): + def test_create_layout_page_404(self, dashboard, mocker): + mocker.patch("vizro.models._dashboard.get_relative_path") result = dashboard._make_page_404_layout() result_image = result.children[0] result_div = result.children[1] From 0f761e507aaf4b3b347aa026055ddf7720dcabff Mon Sep 17 00:00:00 2001 From: Antony Milne Date: Wed, 8 Nov 2023 23:09:40 +0000 Subject: [PATCH 7/9] Add schema --- vizro-core/schemas/0.1.6.dev2.json | 932 +++++++++++++++++++++++++++++ 1 file changed, 932 insertions(+) create mode 100644 vizro-core/schemas/0.1.6.dev2.json diff --git a/vizro-core/schemas/0.1.6.dev2.json b/vizro-core/schemas/0.1.6.dev2.json new file mode 100644 index 000000000..95b345bcb --- /dev/null +++ b/vizro-core/schemas/0.1.6.dev2.json @@ -0,0 +1,932 @@ +{ + "title": "Dashboard", + "description": "Vizro Dashboard to be used within [`Vizro`][vizro._vizro.Vizro.build].\n\nArgs:\n pages (List[Page]): See [`Page`][vizro.models.Page].\n theme (Literal[\"vizro_dark\", \"vizro_light\"]): Layout theme to be applied across dashboard.\n Defaults to `vizro_dark`.\n navigation (Optional[Navigation]): See [`Navigation`][vizro.models.Navigation]. Defaults to `None`.\n title (Optional[str]): Dashboard title to appear on every page on top left-side. Defaults to `None`.", + "type": "object", + "properties": { + "id": { + "title": "Id", + "description": "ID to identify model. Must be unique throughout the whole dashboard.When no ID is chosen, ID will be automatically generated.", + "type": "string" + }, + "pages": { + "title": "Pages", + "type": "array", + "items": { + "$ref": "#/definitions/Page" + } + }, + "theme": { + "title": "Theme", + "description": "Layout theme to be applied across dashboard. Defaults to `vizro_dark`", + "default": "vizro_dark", + "enum": ["vizro_dark", "vizro_light"], + "type": "string" + }, + "navigation": { + "$ref": "#/definitions/Navigation" + }, + "title": { + "title": "Title", + "description": "Dashboard title to appear on every page on top left-side.", + "type": "string" + } + }, + "required": ["pages"], + "additionalProperties": false, + "definitions": { + "Action": { + "title": "Action", + "description": "Action to be inserted into `actions` of relevant component.\n\nArgs:\n function (CapturedCallable): See [`CapturedCallable`][vizro.models.types.CapturedCallable].\n inputs (List[str]): Inputs in the form `.` passed to the action function.\n Defaults to `[]`.\n outputs (List[str]): Outputs in the form `.` changed by the action function.\n Defaults to `[]`.", + "type": "object", + "properties": { + "id": { + "title": "Id", + "description": "ID to identify model. Must be unique throughout the whole dashboard.When no ID is chosen, ID will be automatically generated.", + "type": "string" + }, + "inputs": { + "title": "Inputs", + "description": "Inputs in the form `.` passed to the action function.", + "default": [], + "pattern": "^[a-zA-Z0-9_]+[.][a-zA-Z_]+$", + "type": "array", + "items": { + "type": "string", + "pattern": "^[a-zA-Z0-9_]+[.][a-zA-Z_]+$" + } + }, + "outputs": { + "title": "Outputs", + "description": "Outputs in the form `.` changed by the action function.", + "default": [], + "pattern": "^[a-zA-Z0-9_]+[.][a-zA-Z_]+$", + "type": "array", + "items": { + "type": "string", + "pattern": "^[a-zA-Z0-9_]+[.][a-zA-Z_]+$" + } + } + }, + "additionalProperties": false + }, + "Button": { + "title": "Button", + "description": "Component provided to `Page` to trigger any defined `action` in `Page`.\n\nArgs:\n type (Literal[\"button\"]): Defaults to `\"button\"`.\n text (str): Text to be displayed on button. Defaults to `\"Click me!\"`.\n actions (List[Action]): See [`Action`][vizro.models.Action]. Defaults to `[]`.", + "type": "object", + "properties": { + "id": { + "title": "Id", + "description": "ID to identify model. Must be unique throughout the whole dashboard.When no ID is chosen, ID will be automatically generated.", + "type": "string" + }, + "type": { + "title": "Type", + "default": "button", + "enum": ["button"], + "type": "string" + }, + "text": { + "title": "Text", + "description": "Text to be displayed on button.", + "default": "Click me!", + "type": "string" + }, + "actions": { + "title": "Actions", + "default": [], + "type": "array", + "items": { + "$ref": "#/definitions/Action" + } + } + }, + "additionalProperties": false + }, + "Card": { + "title": "Card", + "description": "Creates a card utilizing `dcc.Markdown` as title and text component.\n\nArgs:\n type (Literal[\"card\"]): Defaults to `\"card\"`.\n text (str): Markdown string to create card title/text that should adhere to the CommonMark Spec.\n href (Optional[str]): URL (relative or absolute) to navigate to. If not provided the Card serves as a text card\n only. Defaults to None.", + "type": "object", + "properties": { + "id": { + "title": "Id", + "description": "ID to identify model. Must be unique throughout the whole dashboard.When no ID is chosen, ID will be automatically generated.", + "type": "string" + }, + "type": { + "title": "Type", + "default": "card", + "enum": ["card"], + "type": "string" + }, + "text": { + "title": "Text", + "description": "Markdown string to create card title/text that should adhere to the CommonMark Spec.", + "type": "string" + }, + "href": { + "title": "Href", + "description": "URL (relative or absolute) to navigate to. If not provided the Card serves as a text card only.", + "type": "string" + } + }, + "required": ["text"], + "additionalProperties": false + }, + "Graph": { + "title": "Graph", + "description": "Wrapper for `dcc.Graph` to visualize charts in dashboard.\n\nArgs:\n type (Literal[\"graph\"]): Defaults to `\"graph\"`.\n figure (CapturedCallable): See [`CapturedCallable`][vizro.models.types.CapturedCallable].\n actions (List[Action]): See [`Action`][vizro.models.Action]. Defaults to `[]`.", + "type": "object", + "properties": { + "id": { + "title": "Id", + "description": "ID to identify model. Must be unique throughout the whole dashboard.When no ID is chosen, ID will be automatically generated.", + "type": "string" + }, + "type": { + "title": "Type", + "default": "graph", + "enum": ["graph"], + "type": "string" + }, + "actions": { + "title": "Actions", + "default": [], + "type": "array", + "items": { + "$ref": "#/definitions/Action" + } + } + }, + "additionalProperties": false + }, + "Table": { + "title": "Table", + "description": "Wrapper for table components to visualize in dashboard.\n\nArgs:\n type (Literal[\"table\"]): Defaults to `\"table\"`.\n figure (CapturedCallable): Table like object to be displayed. Current choices include:\n [`dash_table.DataTable`](https://dash.plotly.com/datatable).\n title (str): Title of the table. Defaults to `None`.\n actions (List[Action]): See [`Action`][vizro.models.Action]. Defaults to `[]`.", + "type": "object", + "properties": { + "id": { + "title": "Id", + "description": "ID to identify model. Must be unique throughout the whole dashboard.When no ID is chosen, ID will be automatically generated.", + "type": "string" + }, + "type": { + "title": "Type", + "default": "table", + "enum": ["table"], + "type": "string" + }, + "title": { + "title": "Title", + "description": "Title of the table", + "type": "string" + }, + "actions": { + "title": "Actions", + "default": [], + "type": "array", + "items": { + "$ref": "#/definitions/Action" + } + } + }, + "additionalProperties": false + }, + "Layout": { + "title": "Layout", + "description": "Grid specification to place chart/components on the [`Page`][vizro.models.Page].\n\nArgs:\n grid (List[List[int]]): Grid specification to arrange components on screen.\n row_gap (str): Gap between rows in px. Defaults to `\"12px\"`.\n col_gap (str): Gap between columns in px. Defaults to `\"12px\"`.\n row_min_height (str): Minimum row height in px. Defaults to `\"0px\"`.\n col_min_width (str): Minimum column width in px. Defaults to `\"0px\"`.", + "type": "object", + "properties": { + "id": { + "title": "Id", + "description": "ID to identify model. Must be unique throughout the whole dashboard.When no ID is chosen, ID will be automatically generated.", + "type": "string" + }, + "grid": { + "title": "Grid", + "description": "Grid specification to arrange components on screen.", + "type": "array", + "items": { + "type": "array", + "items": { + "type": "integer" + } + } + }, + "row_gap": { + "title": "Row Gap", + "description": "Gap between rows in px. Defaults to 12px.", + "default": "12px", + "pattern": "[0-9]+px", + "type": "string" + }, + "col_gap": { + "title": "Col Gap", + "description": "Gap between columns in px. Defaults to 12px.", + "default": "12px", + "pattern": "[0-9]+px", + "type": "string" + }, + "row_min_height": { + "title": "Row Min Height", + "description": "Minimum row height in px. Defaults to 0px.", + "default": "0px", + "pattern": "[0-9]+px", + "type": "string" + }, + "col_min_width": { + "title": "Col Min Width", + "description": "Minimum column width in px. Defaults to 0px.", + "default": "0px", + "pattern": "[0-9]+px", + "type": "string" + } + }, + "required": ["grid"], + "additionalProperties": false + }, + "OptionsDictType": { + "title": "OptionsDictType", + "type": "object", + "properties": { + "label": { + "title": "Label", + "type": "string" + }, + "value": { + "title": "Value", + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "number" + }, + { + "type": "string" + } + ] + } + }, + "required": ["label", "value"], + "additionalProperties": false + }, + "Checklist": { + "title": "Checklist", + "description": "Categorical multi-selector `Checklist` to be provided to [`Filter`][vizro.models.Filter].\n\nArgs:\n type (Literal[\"checklist\"]): Defaults to `\"checklist\"`.\n options (OptionsType): See [`OptionsType`][vizro.models.types.OptionsType]. Defaults to `[]`.\n value (Optional[MultiValueType]): See [`MultiValueType`][vizro.models.types.MultiValueType]. Defaults to `None`.\n title (Optional[str]): Title to be displayed. Defaults to `None`.\n actions (List[Action]): See [`Action`][vizro.models.Action]. Defaults to `[]`.", + "type": "object", + "properties": { + "id": { + "title": "Id", + "description": "ID to identify model. Must be unique throughout the whole dashboard.When no ID is chosen, ID will be automatically generated.", + "type": "string" + }, + "type": { + "title": "Type", + "default": "checklist", + "enum": ["checklist"], + "type": "string" + }, + "options": { + "title": "Options", + "default": [], + "anyOf": [ + { + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "type": "array", + "items": { + "type": "number" + } + }, + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "array", + "items": { + "$ref": "#/definitions/OptionsDictType" + } + } + ] + }, + "value": { + "title": "Value", + "anyOf": [ + { + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "type": "array", + "items": { + "type": "number" + } + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + }, + "title": { + "title": "Title", + "description": "Title to be displayed", + "type": "string" + }, + "actions": { + "title": "Actions", + "default": [], + "type": "array", + "items": { + "$ref": "#/definitions/Action" + } + } + }, + "additionalProperties": false + }, + "Dropdown": { + "title": "Dropdown", + "description": "Categorical multi-selector `Dropdown` to be provided to [`Filter`][vizro.models.Filter].\n\nArgs:\n type (Literal[\"dropdown\"]): Defaults to `\"dropdown\"`.\n options (OptionsType): See [`OptionsType`][vizro.models.types.OptionsType]. Defaults to `[]`.\n value (Optional[Union[SingleValueType, MultiValueType]]): See\n [`SingleValueType`][vizro.models.types.SingleValueType] and\n [`MultiValueType`][vizro.models.types.MultiValueType]. Defaults to `None`.\n multi (bool): Whether to allow selection of multiple values. Defaults to `True`.\n title (Optional[str]): Title to be displayed. Defaults to `None`.\n actions (List[Action]): See [`Action`][vizro.models.Action]. Defaults to `[]`.", + "type": "object", + "properties": { + "id": { + "title": "Id", + "description": "ID to identify model. Must be unique throughout the whole dashboard.When no ID is chosen, ID will be automatically generated.", + "type": "string" + }, + "type": { + "title": "Type", + "default": "dropdown", + "enum": ["dropdown"], + "type": "string" + }, + "options": { + "title": "Options", + "default": [], + "anyOf": [ + { + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "type": "array", + "items": { + "type": "number" + } + }, + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "array", + "items": { + "$ref": "#/definitions/OptionsDictType" + } + } + ] + }, + "value": { + "title": "Value", + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "number" + }, + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "type": "array", + "items": { + "type": "number" + } + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + }, + "multi": { + "title": "Multi", + "description": "Whether to allow selection of multiple values", + "default": true, + "type": "boolean" + }, + "title": { + "title": "Title", + "description": "Title to be displayed", + "type": "string" + }, + "actions": { + "title": "Actions", + "default": [], + "type": "array", + "items": { + "$ref": "#/definitions/Action" + } + } + }, + "additionalProperties": false + }, + "RadioItems": { + "title": "RadioItems", + "description": "Categorical single-selector `RadioItems` to be provided to `Filter`.\n\nArgs:\n type (Literal[\"radio_items\"]): Defaults to `\"radio_items\"`.\n options (OptionsType): See [`OptionsType`][vizro.models.types.OptionsType]. Defaults to `[]`.\n value (Optional[SingleValueType]): See [`SingleValueType`][vizro.models.types.SingleValueType].\n Defaults to `None`.\n title (Optional[str]): Title to be displayed. Defaults to `None`.\n actions (List[Action]): See [`Action`][vizro.models.Action]. Defaults to `[]`.", + "type": "object", + "properties": { + "id": { + "title": "Id", + "description": "ID to identify model. Must be unique throughout the whole dashboard.When no ID is chosen, ID will be automatically generated.", + "type": "string" + }, + "type": { + "title": "Type", + "default": "radio_items", + "enum": ["radio_items"], + "type": "string" + }, + "options": { + "title": "Options", + "default": [], + "anyOf": [ + { + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "type": "array", + "items": { + "type": "number" + } + }, + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "array", + "items": { + "$ref": "#/definitions/OptionsDictType" + } + } + ] + }, + "value": { + "title": "Value", + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "number" + }, + { + "type": "string" + } + ] + }, + "title": { + "title": "Title", + "description": "Title to be displayed", + "type": "string" + }, + "actions": { + "title": "Actions", + "default": [], + "type": "array", + "items": { + "$ref": "#/definitions/Action" + } + } + }, + "additionalProperties": false + }, + "RangeSlider": { + "title": "RangeSlider", + "description": "Numeric multi-selector `RangeSlider`.\n\nCan be provided to [`Filter`][vizro.models.Filter] or\n[`Parameter`][vizro.models.Parameter]. Based on the underlying\n[`dcc.RangeSlider`](https://dash.plotly.com/dash-core-components/rangeslider).\n\nArgs:\n type (Literal[\"range_slider\"]): Defaults to `\"range_slider\"`.\n min (Optional[float]): Start value for slider. Defaults to `None`.\n max (Optional[float]): End value for slider. Defaults to `None`.\n step (Optional[float]): Step-size for marks on slider. Defaults to `None`.\n marks (Optional[Dict[float, str]]): Marks to be displayed on slider. Defaults to `{}`.\n value (Optional[List[float]]): Default start and end value for slider. Must be 2 items. Defaults to `None`.\n title (Optional[str]): Title to be displayed. Defaults to `None`.\n actions (List[Action]): See [`Action`][vizro.models.Action]. Defaults to `[]`.", + "type": "object", + "properties": { + "id": { + "title": "Id", + "description": "ID to identify model. Must be unique throughout the whole dashboard.When no ID is chosen, ID will be automatically generated.", + "type": "string" + }, + "type": { + "title": "Type", + "default": "range_slider", + "enum": ["range_slider"], + "type": "string" + }, + "min": { + "title": "Min", + "description": "Start value for slider.", + "type": "number" + }, + "max": { + "title": "Max", + "description": "End value for slider.", + "type": "number" + }, + "step": { + "title": "Step", + "description": "Step-size for marks on slider.", + "type": "number" + }, + "marks": { + "title": "Marks", + "description": "Marks to be displayed on slider.", + "default": {}, + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "value": { + "title": "Value", + "description": "Default start and end value for slider", + "minItems": 2, + "maxItems": 2, + "type": "array", + "items": { + "type": "number" + } + }, + "title": { + "title": "Title", + "description": "Title to be displayed.", + "type": "string" + }, + "actions": { + "title": "Actions", + "default": [], + "type": "array", + "items": { + "$ref": "#/definitions/Action" + } + } + }, + "additionalProperties": false + }, + "Slider": { + "title": "Slider", + "description": "Numeric single-selector `Slider`.\n\nCan be provided to [`Filter`][vizro.models.Filter] or\n[`Parameter`][vizro.models.Parameter]. Based on the underlying\n[`dcc.Slider`](https://dash.plotly.com/dash-core-components/slider).\n\nArgs:\n type (Literal[\"range_slider\"]): Defaults to `\"range_slider\"`.\n min (Optional[float]): Start value for slider. Defaults to `None`.\n max (Optional[float]): End value for slider. Defaults to `None`.\n step (Optional[float]): Step-size for marks on slider. Defaults to `None`.\n marks (Optional[Dict[float, str]]): Marks to be displayed on slider. Defaults to `{}`.\n value (Optional[float]): Default value for slider. Defaults to `None`.\n title (Optional[str]): Title to be displayed. Defaults to `None`.\n actions (List[Action]): See [`Action`][vizro.models.Action]. Defaults to `[]`.", + "type": "object", + "properties": { + "id": { + "title": "Id", + "description": "ID to identify model. Must be unique throughout the whole dashboard.When no ID is chosen, ID will be automatically generated.", + "type": "string" + }, + "type": { + "title": "Type", + "default": "slider", + "enum": ["slider"], + "type": "string" + }, + "min": { + "title": "Min", + "description": "Start value for slider.", + "type": "number" + }, + "max": { + "title": "Max", + "description": "End value for slider.", + "type": "number" + }, + "step": { + "title": "Step", + "description": "Step-size for marks on slider.", + "type": "number" + }, + "marks": { + "title": "Marks", + "description": "Marks to be displayed on slider.", + "default": {}, + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "value": { + "title": "Value", + "description": "Default value for slider.", + "type": "number" + }, + "title": { + "title": "Title", + "description": "Title to be displayed.", + "type": "string" + }, + "actions": { + "title": "Actions", + "default": [], + "type": "array", + "items": { + "$ref": "#/definitions/Action" + } + } + }, + "additionalProperties": false + }, + "Filter": { + "title": "Filter", + "description": "Filter the data supplied to `targets` on the [`Page`][vizro.models.Page].\n\nExamples:\n >>> print(repr(Filter(column=\"species\")))\n\nArgs:\n type (Literal[\"filter\"]): Defaults to `\"filter\"`.\n column (str): Column of `DataFrame` to filter.\n targets (List[ModelID]): Target component to be affected by filter. If none are given then target all components\n on the page that use `column`.\n selector (Optional[SelectorType]): See [SelectorType][vizro.models.types.SelectorType]. Defaults to `None`.", + "type": "object", + "properties": { + "id": { + "title": "Id", + "description": "ID to identify model. Must be unique throughout the whole dashboard.When no ID is chosen, ID will be automatically generated.", + "type": "string" + }, + "type": { + "title": "Type", + "default": "filter", + "enum": ["filter"], + "type": "string" + }, + "column": { + "title": "Column", + "description": "Column of DataFrame to filter.", + "type": "string" + }, + "targets": { + "title": "Targets", + "description": "Target component to be affected by filter. If none are given then target all components on the page that use `column`.", + "default": [], + "type": "array", + "items": { + "type": "string" + } + }, + "selector": { + "title": "Selector", + "anyOf": [ + { + "$ref": "#/definitions/Checklist" + }, + { + "$ref": "#/definitions/Dropdown" + }, + { + "$ref": "#/definitions/RadioItems" + }, + { + "$ref": "#/definitions/RangeSlider" + }, + { + "$ref": "#/definitions/Slider" + } + ] + } + }, + "required": ["column"], + "additionalProperties": false + }, + "Parameter": { + "title": "Parameter", + "description": "Alter the arguments supplied to any `targets` on the [`Page`][vizro.models.Page].\n\nExamples:\n >>> print(repr(Parameter(\n >>> targets=[\"scatter.x\"], selector=Slider(min=0, max=1, default=0.8, title=\"Bubble opacity\"))))\n\nArgs:\n type (Literal[\"parameter\"]): Defaults to `\"parameter\"`.\n targets (List[str]): Targets in the form of `.`.\n selector (SelectorType): See [SelectorType][vizro.models.types.SelectorType]. Converts selector value\n `\"NONE\"` into `None` to allow optional parameters.\n\nRaises:\n ValueError: If targets are invalid and not of the form `.`.", + "type": "object", + "properties": { + "id": { + "title": "Id", + "description": "ID to identify model. Must be unique throughout the whole dashboard.When no ID is chosen, ID will be automatically generated.", + "type": "string" + }, + "type": { + "title": "Type", + "default": "parameter", + "enum": ["parameter"], + "type": "string" + }, + "targets": { + "title": "Targets", + "description": "Targets in the form of `.`.", + "type": "array", + "items": { + "type": "string" + } + }, + "selector": { + "title": "Selector", + "description": "Selectors to be used inside a control.", + "discriminator": { + "propertyName": "type", + "mapping": { + "checklist": "#/definitions/Checklist", + "dropdown": "#/definitions/Dropdown", + "radio_items": "#/definitions/RadioItems", + "range_slider": "#/definitions/RangeSlider", + "slider": "#/definitions/Slider" + } + }, + "oneOf": [ + { + "$ref": "#/definitions/Checklist" + }, + { + "$ref": "#/definitions/Dropdown" + }, + { + "$ref": "#/definitions/RadioItems" + }, + { + "$ref": "#/definitions/RangeSlider" + }, + { + "$ref": "#/definitions/Slider" + } + ] + } + }, + "required": ["targets", "selector"], + "additionalProperties": false + }, + "ActionsChain": { + "title": "ActionsChain", + "description": "All models that are registered to the model manager should inherit from this class.\n\nArgs:\n id (Optional[str]): ID to identify model. Must be unique throughout the whole dashboard. Defaults to `None`.\n When no ID is chosen, ID will be automatically generated.", + "type": "object", + "properties": { + "id": { + "title": "Id", + "description": "ID to identify model. Must be unique throughout the whole dashboard.When no ID is chosen, ID will be automatically generated.", + "type": "string" + }, + "trigger": { + "title": "Trigger", + "type": "array", + "items": [ + { + "title": "Component Id", + "type": "string" + }, + { + "title": "Component Property", + "type": "string" + } + ], + "minItems": 2, + "maxItems": 2 + }, + "actions": { + "title": "Actions", + "default": [], + "type": "array", + "items": { + "$ref": "#/definitions/Action" + } + } + }, + "required": ["trigger"], + "additionalProperties": false + }, + "Page": { + "title": "Page", + "description": "A page in [`Dashboard`][vizro.models.Dashboard] with its own URL path and place in the `Navigation`.\n\nArgs:\n components (List[ComponentType]): See [ComponentType][vizro.models.types.ComponentType]. At least one component\n has to be provided.\n title (str): Title to be displayed.\n layout (Optional[Layout]): Layout to place components in. Defaults to `None`.\n controls (List[ControlType]): See [ControlType][vizro.models.types.ControlType]. Defaults to `[]`.\n path (Optional[str]): Path to navigate to page. Defaults to `None`.\n\nRaises:\n ValueError: If number of page and grid components is not the same", + "type": "object", + "properties": { + "id": { + "title": "Id", + "description": "ID to identify model. Must be unique throughout the whole dashboard.When no ID is chosen, ID will be automatically generated.", + "type": "string" + }, + "components": { + "title": "Components", + "type": "array", + "items": { + "discriminator": { + "propertyName": "type", + "mapping": { + "button": "#/definitions/Button", + "card": "#/definitions/Card", + "graph": "#/definitions/Graph", + "table": "#/definitions/Table" + } + }, + "oneOf": [ + { + "$ref": "#/definitions/Button" + }, + { + "$ref": "#/definitions/Card" + }, + { + "$ref": "#/definitions/Graph" + }, + { + "$ref": "#/definitions/Table" + } + ] + } + }, + "title": { + "title": "Title", + "description": "Title to be displayed.", + "type": "string" + }, + "layout": { + "$ref": "#/definitions/Layout" + }, + "controls": { + "title": "Controls", + "default": [], + "type": "array", + "items": { + "discriminator": { + "propertyName": "type", + "mapping": { + "filter": "#/definitions/Filter", + "parameter": "#/definitions/Parameter" + } + }, + "oneOf": [ + { + "$ref": "#/definitions/Filter" + }, + { + "$ref": "#/definitions/Parameter" + } + ] + } + }, + "path": { + "title": "Path", + "description": "Path to navigate to page.", + "type": "string" + }, + "actions": { + "title": "Actions", + "default": [], + "type": "array", + "items": { + "$ref": "#/definitions/ActionsChain" + } + } + }, + "required": ["components", "title"], + "additionalProperties": false + }, + "Navigation": { + "title": "Navigation", + "description": "Navigation in [`Dashboard`][vizro.models.Dashboard] to structure [`Pages`][vizro.models.Page].\n\nArgs:\n pages (Optional[NavigationPagesType]): See [`NavigationPagesType`][vizro.models.types.NavigationPagesType].\n Defaults to `None`.", + "type": "object", + "properties": { + "id": { + "title": "Id", + "description": "ID to identify model. Must be unique throughout the whole dashboard.When no ID is chosen, ID will be automatically generated.", + "type": "string" + }, + "pages": { + "title": "Pages", + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } + } + ] + } + }, + "additionalProperties": false + } + } +} From 529aca0a409177827e338cff1adb66d98a544317 Mon Sep 17 00:00:00 2001 From: Antony Milne Date: Thu, 9 Nov 2023 12:46:20 +0000 Subject: [PATCH 8/9] Update docs --- ...230816_antony.milne_fix_assets_pathname.md | 12 +++------ vizro-core/docs/pages/API_reference/vizro.md | 4 +++ vizro-core/docs/pages/user_guides/assets.md | 25 +++++-------------- vizro-core/docs/pages/user_guides/run.md | 8 +++++- vizro-core/examples/jupyter/app.ipynb | 3 +-- vizro-core/src/vizro/_vizro.py | 13 +++++++--- 6 files changed, 30 insertions(+), 35 deletions(-) diff --git a/vizro-core/changelog.d/20231108_230816_antony.milne_fix_assets_pathname.md b/vizro-core/changelog.d/20231108_230816_antony.milne_fix_assets_pathname.md index f1f65e73c..b9c7af32f 100644 --- a/vizro-core/changelog.d/20231108_230816_antony.milne_fix_assets_pathname.md +++ b/vizro-core/changelog.d/20231108_230816_antony.milne_fix_assets_pathname.md @@ -16,30 +16,24 @@ Uncomment the section that is right (remove the HTML comment wrapper). - A bullet item for the Removed category with a link to the relevant PR at the end of your entry, e.g. Enable feature XXX ([#1](https://github.com/mckinsey/vizro/pull/1)) --> - - - + ### Added - `Vizro` takes `**kwargs` that are passed through to `Dash` ([#151](https://github.com/mckinsey/vizro/pull/151)) @@ -30,6 +31,7 @@ Uncomment the section that is right (remove the HTML comment wrapper). - A bullet item for the Deprecated category with a link to the relevant PR at the end of your entry, e.g. Enable feature XXX ([#1](https://github.com/mckinsey/vizro/pull/1)) --> + ### Fixed - Assets are now routed correctly when hosting the dashboard in a subdirectory ([#151](https://github.com/mckinsey/vizro/pull/151)) diff --git a/vizro-core/docs/pages/API_reference/vizro.md b/vizro-core/docs/pages/API_reference/vizro.md index 449711fad..182ba849a 100644 --- a/vizro-core/docs/pages/API_reference/vizro.md +++ b/vizro-core/docs/pages/API_reference/vizro.md @@ -4,4 +4,4 @@ options: merge_init_into_class: false docstring_options: - ignore_init_summary: false \ No newline at end of file + ignore_init_summary: false diff --git a/vizro-core/docs/pages/user_guides/assets.md b/vizro-core/docs/pages/user_guides/assets.md index e6b21e14f..dced19937 100644 --- a/vizro-core/docs/pages/user_guides/assets.md +++ b/vizro-core/docs/pages/user_guides/assets.md @@ -18,9 +18,6 @@ The user-provided `assets` folder thus always takes precedence. ├── favicon.ico ``` -## Adding static images -We use [Dash's underlying functionalities](https://dash.plotly.com/dash-enterprise/static-assets#embedding-images-in-your-dash-apps) to embed images into the app). You should always use `dash.get_asset_url` to refer to files inside your assets folder to ensure that the correct URL is generated in both development and production environments. For example, the file `collections.svg` in the above example would be referenced as `dash.get_asset_url("images/icons/collections.csv")`. - ## Changing the favicon To change the default favicon (website icon appearing in the browser tab), add a file named `favicon.ico` to your `assets` folder. For more information, see [here](https://dash.plotly.com/external-resources#changing-the-favicon). @@ -148,17 +145,13 @@ To achieve this, do the following: [CardCSS]: ../../assets/user_guides/assets/css_change_card.png -???+ note - - CSS properties will be applied with the last served file taking precedence. - Files are served in alphanumerical order. - - **Order of CSS being served to app** +CSS properties will be applied with the last served file taking precedence. The order of serving is: - 1. Dash built-in stylesheets - 2. Vizro built-in stylesheets - 3. User assets folder stylesheets +1. Dash built-in stylesheets +2. Vizro built-in stylesheets +3. User assets folder stylesheets +Within each of these categories, individual files are served in alphanumerical order. ## Changing the `assets` folder path If you do not want to place your `assets` folder in the root directory of your app, you can diff --git a/vizro-core/src/vizro/_vizro.py b/vizro-core/src/vizro/_vizro.py index 94b38910b..f0f075f3c 100644 --- a/vizro-core/src/vizro/_vizro.py +++ b/vizro-core/src/vizro/_vizro.py @@ -20,7 +20,7 @@ def __init__(self, **kwargs): Args: kwargs: Passed through to `Dash.__init__`, e.g. `assets_folder`, `url_base_pathname`. See - [Dash documentation](https://dash.plotly.com/reference#dash.dash) for possible arguments. + [Dash documentation](https://dash.plotly.com/reference#dash.dash) for possible arguments. """ self.dash = dash.Dash(**kwargs, use_pages=True, pages_folder="")