Skip to content

Commit

Permalink
uuid method (#183)
Browse files Browse the repository at this point in the history
  • Loading branch information
anders-kiaer authored Jan 23, 2020
1 parent 592f2a2 commit c4604bb
Show file tree
Hide file tree
Showing 7 changed files with 56 additions and 69 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ def test_example_plugin(dash_duo):
page = _example_plugin.ExamplePlugin(app, title)
app.layout = page.layout
dash_duo.start_server(app)
btn = dash_duo.find_element(f"#{page.button_id}")
btn = dash_duo.find_element("#" + page.uuid("submit-button"))
assert btn.text == "Submit"
text = dash_duo.find_element(f"#{page.div_id}")
text = dash_duo.find_element("#" + page.uuid("output-state"))
assert text.text == "Button has been pressed 0 times."
btn.click()
dash_duo.wait_for_contains_text(
f"#{page.div_id}", "Button has been pressed 1 times", timeout=2
"#" + page.uuid("output-state"), "Button has been pressed 1 times", timeout=2
)
assert text.text == "Button has been pressed 1 times."
24 changes: 8 additions & 16 deletions tests/test_table_plotter.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,27 +25,21 @@ def test_table_plotter(dash_duo):
# Check that filter is not active
assert not page.use_filter

# Checking that the selectors are not hidden
selector_row = dash_duo.find_element(f"#{page.selector_row}")
assert selector_row.get_attribute("style") == ""

# Checking that the correct plot type is initialized
plot_dd = dash_duo.find_element(f"#{page.plot_option_id}-plottype")
plot_dd = dash_duo.find_element("#" + page.uuid("plottype"))
assert plot_dd.text == "scatter"

# Checking that only the relevant options are shown
for plot_option in page.plot_args.keys():
plot_option_dd = dash_duo.find_element(
f"#{page.plot_option_id}-div-{plot_option}"
)
plot_option_dd = dash_duo.find_element("#" + page.uuid(f"div-{plot_option}"))
if plot_option not in page.plots["scatter"]:
assert plot_option_dd.get_attribute("style") == "display: none;"
else:
assert plot_option_dd.get_attribute("style") == "display: grid;"

# Checking that options are initialized correctly
for option in ["x", "y"]:
plot_option_dd = dash_duo.find_element(f"#{page.plot_option_id}-{option}")
plot_option_dd = dash_duo.find_element("#" + page.uuid(f"dropdown-{option}"))
assert plot_option_dd.text == "Well"


Expand All @@ -69,26 +63,24 @@ def test_table_plotter_filter(dash_duo):
assert page.use_filter
assert page.filter_cols == ["Well"]
# Checking that the selectors are not hidden
selector_row = dash_duo.find_element(f"#{page.selector_row}")
selector_row = dash_duo.find_element("#" + page.uuid("selector-row"))
assert selector_row.get_attribute("style") == ""

# Checking that the correct plot type is initialized
plot_dd = dash_duo.find_element(f"#{page.plot_option_id}-plottype")
plot_dd = dash_duo.find_element("#" + page.uuid("plottype"))
assert plot_dd.text == "scatter"

# Checking that only the relevant options are shown
for plot_option in page.plot_args.keys():
plot_option_dd = dash_duo.find_element(
f"#{page.plot_option_id}-div-{plot_option}"
)
plot_option_dd = dash_duo.find_element("#" + page.uuid(f"div-{plot_option}"))
if plot_option in page.plots["scatter"]:
assert plot_option_dd.get_attribute("style") == "display: grid;"
else:
assert plot_option_dd.get_attribute("style") == "display: none;"

# Checking that options are initialized correctly
for option in ["x", "y"]:
plot_option_dd = dash_duo.find_element(f"#{page.plot_option_id}-{option}")
plot_option_dd = dash_duo.find_element("#" + page.uuid(f"dropdown-{option}"))
assert plot_option_dd.text == "Well"


Expand Down Expand Up @@ -121,5 +113,5 @@ def test_initialized_table_plotter(dash_duo):
assert page.lock

# Checking that the selectors are hidden
selector_row = dash_duo.find_element(f"#{page.selector_row}")
selector_row = dash_duo.find_element("#" + page.uuid("selector-row"))
assert selector_row.get_attribute("style") == "display: none;"
30 changes: 23 additions & 7 deletions webviz_config/_plugin_abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,17 +53,33 @@ def layout(self):
ASSETS = []

def __init__(self):
"""This function will later be used for e.g. setting a unique ID at
initialization, which then subclasses can use further.
If a plugin/subclass defines its own `__init__` function
"""If a plugin/subclass defines its own `__init__` function
(which they usually do), they should remember to call
```python
super().__init__()
```
in its own `__init__` function in order to also run the parent `__init__`.
"""

self._plugin_uuid = uuid4()

def uuid(self, element: str):
"""Typically used to get a unique ID for some given element/component in
a plugins layout. If the element string is unique within the plugin, this
function returns a string which is guaranteed to be unique also across the
application (even when multiple instances of the same plugin is added).
Within the same plugin instance, the returned uuid is the same for the same
element string. I.e. storing the returned value in the plugin is not necessary.
Main benefit of using this function instead of creating a UUID directly,
is that the abstract base class can in the future provide IDs that
are consistent across application restarts (i.e. when the webviz configuration
file changes in a non-portable setting).
"""

return f"{element}-{self._plugin_uuid}"

@property
@abc.abstractmethod
def layout(self):
Expand All @@ -76,9 +92,9 @@ def layout(self):
def _plugin_wrapper_id(self):
# pylint: disable=attribute-defined-outside-init
# We do not have a __init__ method in this abstract base class
if not hasattr(self, "_plugin_wrapper_uuid"):
self._plugin_wrapper_uuid = uuid4()
return f"plugin-wrapper-{self._plugin_wrapper_uuid}"
if not hasattr(self, "_plugin_uuid"):
self._plugin_uuid = uuid4()
return f"plugin-wrapper-{self._plugin_uuid}"

@property
def plugin_data_output(self):
Expand Down
3 changes: 0 additions & 3 deletions webviz_config/plugins/_data_table.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
from uuid import uuid4
from pathlib import Path

import pandas as pd
Expand Down Expand Up @@ -40,15 +39,13 @@ def __init__(
self.sorting = sorting
self.filtering = filtering
self.pagination = pagination
self.data_table_id = f"data-table-{uuid4()}"

def add_webvizstore(self):
return [(get_data, [{"csv_file": self.csv_file}])]

@property
def layout(self):
return dash_table.DataTable(
id=self.data_table_id,
columns=[{"name": i, "id": i} for i in self.df.columns],
data=self.df.to_dict("records"),
sort_action="native" if self.sorting else "none",
Expand Down
14 changes: 6 additions & 8 deletions webviz_config/plugins/_example_plugin.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from uuid import uuid4

import dash_html_components as html
from dash.dependencies import Input, Output

Expand All @@ -12,24 +10,24 @@ def __init__(self, app, title: str):

self.title = title

self.button_id = f"submit-button-{uuid4()}"
self.div_id = f"output-state-{uuid4()}"

self.set_callbacks(app)

@property
def layout(self):
return html.Div(
[
html.H1(self.title),
html.Button(id=self.button_id, n_clicks=0, children="Submit"),
html.Div(id=self.div_id),
html.Button(
id=self.uuid("submit-button"), n_clicks=0, children="Submit"
),
html.Div(id=self.uuid("output-state")),
]
)

def set_callbacks(self, app):
@app.callback(
Output(self.div_id, "children"), [Input(self.button_id, "n_clicks")]
Output(self.uuid("output-state"), "children"),
[Input(self.uuid("submit-button"), "n_clicks")],
)
def _update_output(n_clicks):
return f"Button has been pressed {n_clicks} times."
16 changes: 4 additions & 12 deletions webviz_config/plugins/_example_tour.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,14 @@
from uuid import uuid4

import dash_html_components as html

from .. import WebvizPluginABC


class ExampleTour(WebvizPluginABC):
def __init__(self):
super().__init__()

self.blue_text_id = f"element-{uuid4()}"
self.red_text_id = f"element-{uuid4()}"

@property
def tour_steps(self):
return [
{"id": self.blue_text_id, "content": "This is the first step"},
{"id": self.red_text_id, "content": "This is the second step"},
{"id": self.uuid("blue_text"), "content": "This is the first step"},
{"id": self.uuid("red_text"), "content": "This is the second step"},
]

@property
Expand All @@ -25,12 +17,12 @@ def layout(self):
children=[
html.Span(
"Here is some blue text to explain... ",
id=self.blue_text_id,
id=self.uuid("blue_text"),
style={"color": "blue"},
),
html.Span(
" ...and here is some red text that also needs an explanation.",
id=self.red_text_id,
id=self.uuid("red_text"),
style={"color": "red"},
),
]
Expand Down
32 changes: 12 additions & 20 deletions webviz_config/plugins/_table_plotter.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
from uuid import uuid4
from pathlib import Path
from collections import OrderedDict

Expand Down Expand Up @@ -41,7 +40,6 @@ def __init__(
super().__init__()

self.plot_options = plot_options if plot_options else {}
self.graph_id = f"graph-id{uuid4()}"
self.lock = lock
self.csv_file = csv_file
self.data = get_data(self.csv_file)
Expand All @@ -50,14 +48,11 @@ def __init__(
self.numeric_columns = list(
self.data.select_dtypes(include=[np.number]).columns
)
self.selector_row = f"selector-row{uuid4()}"
self.plot_option_id = f"plot-option{uuid4()}"
self.plotly_theme = app.webviz_settings["theme"].plotly_theme
self.set_callbacks(app)

def set_filters(self, filter_cols):
self.filter_cols = []
self.filter_ids = {}
self.use_filter = False
if filter_cols:
for col in filter_cols:
Expand All @@ -66,9 +61,6 @@ def set_filters(self, filter_cols):
self.filter_cols.append(col)
if self.filter_cols:
self.use_filter = True
self.filter_ids = {
col: f"{col}-{str(uuid4())}" for col in self.filter_cols
}

def add_webvizstore(self):
return [(get_data, [{"csv_file": self.csv_file}])]
Expand Down Expand Up @@ -191,7 +183,7 @@ def filter_layout(self):
children=[
html.Summary(col.lower().capitalize()),
dcc.RangeSlider(
id=self.filter_ids[col],
id=self.uuid(f"filter-{col}"),
min=min_val,
max=max_val,
step=(max_val - min_val) / 10,
Expand All @@ -217,7 +209,7 @@ def filter_layout(self):
children=[
html.Summary(col.lower().capitalize()),
dcc.Dropdown(
id=self.filter_ids[col],
id=self.uuid(f"filter-{col}"),
options=[
{"label": i, "value": i} for i in elements
],
Expand All @@ -242,7 +234,7 @@ def plot_option_layout(self):
html.H4("Set plot options"),
html.P("Plot type"),
dcc.Dropdown(
id=f"{self.plot_option_id}-plottype",
id=self.uuid("plottype"),
clearable=False,
options=[{"label": i, "value": i} for i in self.plots],
value=self.plot_options.get("type", "scatter"),
Expand All @@ -256,11 +248,11 @@ def plot_option_layout(self):
divs.append(
html.Div(
style=self.style_options_div,
id=f"{self.plot_option_id}-div-{key}",
id=self.uuid(f"div-{key}"),
children=[
html.P(key),
dcc.Dropdown(
id=f"{self.plot_option_id}-{key}",
id=self.uuid(f"dropdown-{key}"),
clearable=False,
options=[{"label": i, "value": i} for i in arg["options"]],
value=arg["value"],
Expand Down Expand Up @@ -305,13 +297,13 @@ def layout(self):
style=self.style_page_layout,
children=[
html.Div(
id=self.selector_row,
id=self.uuid("selector-row"),
style=self.style_selectors,
children=self.plot_option_layout(),
),
html.Div(
style={"height": "100%"},
children=wcc.Graph(id=self.graph_id),
children=wcc.Graph(id=self.uuid("graph-id")),
),
html.Div(children=self.filter_layout()),
],
Expand All @@ -324,9 +316,9 @@ def plot_output_callbacks(self):
"""Creates list of output dependencies for callback
The outputs are the graph, and the style of the plot options"""
outputs = []
outputs.append(Output(self.graph_id, "figure"))
outputs.append(Output(self.uuid("graph-id"), "figure"))
for plot_arg in self.plot_args.keys():
outputs.append(Output(f"{self.plot_option_id}-div-{plot_arg}", "style"))
outputs.append(Output(self.uuid(f"div-{plot_arg}"), "style"))
return outputs

@property
Expand All @@ -336,11 +328,11 @@ def plot_input_callbacks(self):
for each plot option
"""
inputs = []
inputs.append(Input(f"{self.plot_option_id}-plottype", "value"))
inputs.append(Input(self.uuid("plottype"), "value"))
for plot_arg in self.plot_args.keys():
inputs.append(Input(f"{self.plot_option_id}-{plot_arg}", "value"))
inputs.append(Input(self.uuid(f"dropdown-{plot_arg}"), "value"))
for filtcol in self.filter_cols:
inputs.append(Input(self.filter_ids[filtcol], "value"))
inputs.append(Input(self.uuid(f"filter-{filtcol}"), "value"))
return inputs

def set_callbacks(self, app):
Expand Down

0 comments on commit c4604bb

Please sign in to comment.