Skip to content

Commit

Permalink
Load css and js assets automatically in non-portable mode (#208)
Browse files Browse the repository at this point in the history
  • Loading branch information
anders-kiaer authored Mar 9, 2020
1 parent 930435b commit 406e8bc
Show file tree
Hide file tree
Showing 6 changed files with 46 additions and 3 deletions.
2 changes: 2 additions & 0 deletions examples/demo_portable.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,5 @@ pages:
some_number: 42
- ExampleAssets:
picture_path: ./example_banner.png
css_path: ./example_stylesheet.css
js_path: ./example_javascript.js
1 change: 1 addition & 0 deletions examples/example_javascript.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
alert("This loaded JavaScript file comes from a plugin.");
3 changes: 3 additions & 0 deletions examples/example_stylesheet.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
body {
background-color: green;
}
8 changes: 7 additions & 1 deletion webviz_config/plugins/_example_assets.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,17 @@


class ExampleAssets(WebvizPluginABC):
def __init__(self, picture_path: Path):
def __init__(self, picture_path: Path, css_path: Path = None, js_path: Path = None):
super().__init__()

self.asset_url = WEBVIZ_ASSETS.add(picture_path)

if css_path is not None:
WEBVIZ_ASSETS.add(css_path)

if js_path is not None:
WEBVIZ_ASSETS.add(js_path)

@property
def layout(self) -> html.Img:
return html.Img(src=self.asset_url)
3 changes: 2 additions & 1 deletion webviz_config/templates/webviz_template.py.jinja2
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ WEBVIZ_STORAGE.storage_folder = path.join(
)

WEBVIZ_ASSETS.portable = {{ portable }}
{{ "WEBVIZ_ASSETS.register_app(app)" if not portable else ""}}

if {{ not portable }} and not webviz_config.is_reload_process():
# When Dash/Flask is started on localhost with hot module reload activated,
Expand Down Expand Up @@ -106,6 +105,8 @@ else:
{% endfor %}],
)

{{ "WEBVIZ_ASSETS.directly_host_assets(app)" if not portable else ""}}

if __name__ == "__main__":
# This part is ignored when the webviz app is started
# using Docker container and uwsgi (e.g. when hosted on Azure).
Expand Down
32 changes: 31 additions & 1 deletion webviz_config/webviz_assets.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,19 @@ def _base_folder(self) -> str:
return "assets" if self.portable else "temp"

def add(self, filename: pathlib.Path) -> str:
"""Calling this function makes the filename given as input
available as a hosted asset when the application is running.
The returned string is a URI which the plugin optionally
can use internally (e.g. as "src" in image elements).
Calling this function with the same input path
multiple times will return the same URI.
Filenames added to WebvizAssets that ends with .css or .js
are loaded automatically in the browser by Dash,
both in non-portable and portable mode.
"""

path = pathlib.Path(filename)

if filename not in self._assets.values():
Expand All @@ -60,20 +73,37 @@ def add(self, filename: pathlib.Path) -> str:

return os.path.normcase(os.path.join(self._base_folder(), assigned_id))

def register_app(self, app: Dash) -> None:
def directly_host_assets(self, app: Dash) -> None:
"""In non-portable mode, this function can be called by the
application. It routes the Dash application to the added assets on
disk, making hot reloading and more interactive development of the
application possible.
"""

if self._portable:
raise RuntimeError(
"The function WebvizAssets.directly_host_assets() "
"method is only meaningful in a non-portable settings."
)

@app.server.route(f"/{self._base_folder()}/<path:asset_id>")
def _send_file(asset_id: str) -> Optional[flask.wrappers.Response]:
if asset_id in self._assets: # Only serve white listed resources
path = pathlib.Path(self._assets[asset_id])
return flask.send_from_directory(path.parent, path.name)
return None

# Add .css and .js files to auto-loaded Dash assets
for asset_id, asset_path in self._assets.items():
if asset_path.suffix == ".css":
app.config.external_stylesheets.append(
f"./{self._base_folder()}/{asset_id}"
)
elif asset_path.suffix == ".js":
app.config.external_scripts.append(
f"./{self._base_folder()}/{asset_id}"
)

def make_portable(self, asset_folder: str) -> None:
"""Copy over all added assets to the given folder (asset_folder).
"""
Expand Down

0 comments on commit 406e8bc

Please sign in to comment.