Skip to content

Commit 5860483

Browse files
committed
Suggestions after review
1 parent 342f640 commit 5860483

7 files changed

+63
-52
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ now has search functionality (using [`docsify` full text search](https://docsify
1414
enforced by `webviz-config` (inline script hashes are added automatically).
1515

1616
### Changed
17+
- [#294](https://github.com/equinor/webviz-config/pull/294) - Plugin authors can now define file type to download
18+
(including specifying MIME type). Before only `.zip` archives were supported.
1719
- [#281](https://github.com/equinor/webviz-config/pull/281) - Now uses `importlib` instead of `pkg_resources` for
1820
detecting plugin entry points and package versions.
1921

CONTRIBUTING.md

+20-16
Original file line numberDiff line numberDiff line change
@@ -156,28 +156,32 @@ callback is set. A typical compressed data download callback will look like
156156
@app.callback(self.plugin_data_output,
157157
self.plugin_data_requested)
158158
def _user_download_data(data_requested):
159-
return WebvizPluginABC.plugin_compressed_data(
160-
file_name="webviz-data.zip",
161-
content=[{'filename': 'some_file.txt',
162-
'content': 'Some download data'}]
163-
) if data_requested else {}
159+
return (
160+
WebvizPluginABC.plugin_compressed_data(
161+
filename="webviz-data.zip",
162+
content=[{"filename": "some_file.txt", "content": "Some download data"}],
163+
)
164+
if data_requested
165+
else None
166+
)
164167
```
165168

166-
A typical raw CSV data download will look like:
169+
A typical CSV data download from e.g. a `pandas.DataFrame` will look like:
167170
```python
168171
@app.callback(self.plugin_data_output,
169172
self.plugin_data_requested)
170173
def _user_download_data(data_requested):
171-
if data_requested:
172-
byte_io = io.BytesIO()
173-
byte_io.write(get_data(self.csv_file).to_csv().encode('ascii'))
174-
byte_io.seek(0)
175-
return {
176-
"file_name": "file_name.csv",
177-
"content": base64.b64encode(byte_io.read()).decode("ascii"),
178-
"mime_type": "text/csv"
179-
}
180-
return {}
174+
return (
175+
{
176+
"filename": "some-file.csv",
177+
"content": base64.b64encode(
178+
some_pandas_dataframe.to_csv().encode()
179+
).decode("ascii"),
180+
"mime_type": "text/csv",
181+
}
182+
if data_requested
183+
else None
184+
)
181185
```
182186

183187
By letting the plugin define the callback, the plugin author is able

setup.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848
"tqdm>=4.8",
4949
"importlib-metadata>=1.7; python_version<'3.8'",
5050
"typing-extensions>=3.7; python_version<'3.8'",
51-
"webviz-core-components>=0.0.19",
51+
"webviz-core-components>=0.0.19", # bump before merge
5252
],
5353
tests_require=TESTS_REQUIRES,
5454
extras_require={"tests": TESTS_REQUIRES},

webviz_config/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from ._theme_class import WebvizConfigTheme
99
from ._localhost_token import LocalhostToken
1010
from ._is_reload_process import is_reload_process
11-
from ._plugin_abc import WebvizPluginABC
11+
from ._plugin_abc import WebvizPluginABC, EncodedFile, ZipFileMember
1212
from ._shared_settings_subscriptions import SHARED_SETTINGS_SUBSCRIPTIONS
1313

1414
try:

webviz_config/_plugin_abc.py

+19-17
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,12 @@
2222
import webviz_core_components as wcc
2323

2424

25-
class FileDefinition(TypedDict, total=False):
26-
file_name: str
25+
class ZipFileMember(TypedDict):
26+
filename: str
2727
content: str
28+
29+
30+
class EncodedFile(ZipFileMember):
2831
mime_type: str
2932

3033

@@ -126,23 +129,22 @@ def _reformat_tour_steps(steps: List[dict]) -> List[dict]:
126129

127130
@staticmethod
128131
def plugin_compressed_data(
129-
file_name: str, content: List[FileDefinition]
130-
) -> FileDefinition:
131-
byte_io = io.BytesIO()
132-
133-
with zipfile.ZipFile(byte_io, "w") as zipped_data:
134-
for data in content:
135-
zipped_data.writestr(data["file_name"], data["content"])
136-
137-
byte_io.seek(0)
138-
return {
139-
"file_name": file_name,
140-
"content": base64.b64encode(byte_io.read()).decode("ascii"),
141-
"mime_type": "application/zip",
142-
}
132+
filename: str, content: List[ZipFileMember]
133+
) -> EncodedFile:
134+
with io.BytesIO() as bytes_io:
135+
with zipfile.ZipFile(bytes_io, "w") as zipped_data:
136+
for data in content:
137+
zipped_data.writestr(data["filename"], data["content"])
138+
139+
bytes_io.seek(0)
140+
return EncodedFile(
141+
filename=filename,
142+
content=base64.b64encode(bytes_io.read()).decode("ascii"),
143+
mime_type="application/zip",
144+
)
143145

144146
@staticmethod
145-
def plugin_data_compress(content: List[dict]) -> FileDefinition:
147+
def plugin_data_compress(content: List[ZipFileMember]) -> EncodedFile:
146148
warnings.warn(
147149
"Use 'plugin_compressed_data' instead of 'plugin_data_compress'",
148150
DeprecationWarning,

webviz_config/plugins/_example_data_download.py

+9-5
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1+
from typing import Optional
2+
13
import dash_html_components as html
24
from dash import Dash
35

4-
from .. import WebvizPluginABC
6+
from .. import WebvizPluginABC, EncodedFile, ZipFileMember
57

68

79
class ExampleDataDownload(WebvizPluginABC):
@@ -17,14 +19,16 @@ def layout(self) -> html.H1:
1719

1820
def set_callbacks(self, app: Dash) -> None:
1921
@app.callback(self.plugin_data_output, self.plugin_data_requested)
20-
def _user_download_data(data_requested: bool) -> dict:
22+
def _user_download_data(data_requested: bool) -> Optional[EncodedFile]:
2123
return (
2224
WebvizPluginABC.plugin_compressed_data(
23-
file_name="webviz-data.zip",
25+
filename="webviz-data.zip",
2426
content=[
25-
{"filename": "some_file.txt", "content": "Some download data"}
27+
ZipFileMember(
28+
filename="some_file.txt", content="Some download data"
29+
)
2630
],
2731
)
2832
if data_requested
29-
else {}
33+
else None
3034
)

webviz_config/plugins/_table_plotter.py

+11-12
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1+
import base64
2+
import inspect
13
from pathlib import Path
24
from collections import OrderedDict
35
from typing import Optional, List, Dict, Any
4-
import inspect
56

67
import numpy as np
78
import pandas as pd
@@ -12,7 +13,7 @@
1213
from dash import Dash
1314
import webviz_core_components as wcc
1415

15-
from .. import WebvizPluginABC
16+
from .. import WebvizPluginABC, EncodedFile
1617
from ..webviz_store import webvizstore
1718
from ..common_cache import CACHE
1819

@@ -357,19 +358,17 @@ def plot_input_callbacks(self) -> List[Input]:
357358

358359
def set_callbacks(self, app: Dash) -> None:
359360
@app.callback(self.plugin_data_output, self.plugin_data_requested)
360-
def _user_download_data(data_requested: Optional[int]) -> dict:
361+
def _user_download_data(data_requested: Optional[int]) -> Optional[EncodedFile]:
361362
return (
362-
WebvizPluginABC.plugin_compressed_data(
363-
file_name="webviz-data.zip",
364-
content=[
365-
{
366-
"filename": "table_plotter.csv",
367-
"content": get_data(self.csv_file).to_csv(),
368-
}
369-
],
363+
EncodedFile(
364+
filename="table-plotter.csv",
365+
content=base64.b64encode(
366+
get_data(self.csv_file).to_csv().encode()
367+
).decode("ascii"),
368+
mime_type="text/csv",
370369
)
371370
if data_requested
372-
else {}
371+
else None
373372
)
374373

375374
@app.callback(self.plot_output_callbacks, self.plot_input_callbacks)

0 commit comments

Comments
 (0)