Skip to content

Commit da286b1

Browse files
author
Andrew DiBiasio
committed
[Dash] Lock Zoom
Zoom Persists on parameter updates when the "Lock Zoom" button is in the on position Removed "max_y_axis" feature
1 parent a8c21bc commit da286b1

File tree

7 files changed

+115
-33
lines changed

7 files changed

+115
-33
lines changed

src/chime_dash/app/components/visualizations.py

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from dash.development.base_component import ComponentMeta
1111
from dash_html_components import H2, Div, A
1212
from dash_core_components import Markdown, Graph
13-
from dash_bootstrap_components import Table, Container
13+
from dash_bootstrap_components import Table, Container, Button
1414

1515
from chime_dash.app.components.base import Component
1616

@@ -50,6 +50,16 @@ def get_html(self) -> List[ComponentMeta]:
5050
target="_blank",
5151
className="btn btn-sm btn-info",
5252
),
53+
Div(
54+
children=Button(
55+
"Lock Zoom",
56+
id="new_admissions_lock_zoom",
57+
color="info",
58+
outline=False,
59+
className="btn btn-sm"
60+
),
61+
style={"display": "inline-block", "padding": 10}
62+
),
5363
Div(
5464
className="row justify-content-center",
5565
children=Div(
@@ -79,6 +89,16 @@ def get_html(self) -> List[ComponentMeta]:
7989
target="_blank",
8090
className="btn btn-sm btn-info",
8191
),
92+
Div(
93+
children=Button(
94+
"Lock Zoom",
95+
id="admitted_patients_lock_zoom",
96+
color="info",
97+
outline=False,
98+
className="btn btn-sm"
99+
),
100+
style={"display": "inline-block", "padding": 10}
101+
),
82102
Div(
83103
className="row justify-content-center",
84104
children=Div(
@@ -108,6 +128,16 @@ def get_html(self) -> List[ComponentMeta]:
108128
target="_blank",
109129
className="btn btn-sm btn-info my-4",
110130
),
131+
Div(
132+
children=Button(
133+
"Lock Zoom",
134+
id="SIR_lock_zoom",
135+
color="info",
136+
outline=False,
137+
className="btn btn-sm"
138+
),
139+
style={"display": "inline-block", "padding": 10}
140+
),
111141
Div(
112142
className="row justify-content-center",
113143
children=Div(

src/chime_dash/app/pages/sidebar.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,7 @@
8888
"initial_visible_month": date.today(),
8989
"date": date.today(),
9090
},
91-
max_y_axis_value={"type": "number", "min": 10, "step": 10, "value": None},
92-
show_tables={"type": "switch", "value": False},
91+
show_tables={"type": "switch", "value": False}
9392
))
9493

9594

src/chime_dash/app/services/callbacks.py

Lines changed: 69 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,14 @@ def toggle_tables(switch_value):
3030
return get_n_switch_values(switch_value, 3)
3131

3232
@staticmethod
33-
def handle_model_change(i, sidebar_data):
33+
def change_btn_color(n_clicks):
34+
try:
35+
return [False] if n_clicks % 2 == 0 else [True]
36+
except:
37+
return [False]
38+
39+
@staticmethod
40+
def handle_model_change(i, sidebar_data, lock_zoom_clicks, graphs_relayout_data):
3441
model = {}
3542
pars = None
3643
result = []
@@ -44,7 +51,6 @@ def handle_model_change(i, sidebar_data):
4451
viz_kwargs = dict(
4552
labels=pars.labels,
4653
table_mod=7,
47-
max_y_axis=pars.max_y_axis,
4854
content=vis_content
4955
)
5056
result.extend(i.components["intro"].build(model, pars))
@@ -53,11 +59,42 @@ def handle_model_change(i, sidebar_data):
5359
if model:
5460
df = model.__dict__.get(df_key, None)
5561
result.extend(prepare_visualization_group(df, **viz_kwargs))
62+
63+
figures = [result[1], result[4], result[7]]
64+
65+
for n_clicks, relayout_data, figure in zip(lock_zoom_clicks, graphs_relayout_data, figures):
66+
if relayout_data:
67+
if n_clicks == None or n_clicks % 2 == 0:
68+
# Set plot_data figure coordinates
69+
if "xaxis.range[0]" in relayout_data:
70+
figure["layout"]["xaxis"]["range"] = [
71+
relayout_data["xaxis.range[0]"],
72+
relayout_data["xaxis.range[1]"]
73+
]
74+
if "yaxis.range[0]" in relayout_data:
75+
figure["layout"]["yaxis"]["range"] = [
76+
relayout_data["yaxis.range[0]"],
77+
relayout_data["yaxis.range[1]"]
78+
]
79+
5680
return result
5781

5882
def __init__(self, component_instance):
59-
def handle_model_change_helper(sidebar_mod, sidebar_data):
60-
return IndexCallbacks.handle_model_change(component_instance, sidebar_data)
83+
def handle_model_change_helper(
84+
sidebar_mod,
85+
new_admissions_lock_zoom,
86+
admitted_patients_lock_zoom,
87+
SIR_lock_zoom,
88+
sidebar_data,
89+
new_admissions_relayout_data,
90+
admitted_patients_relayout_data,
91+
SIR_relayout_data
92+
):
93+
# parameter order: Inputs (sidebar_mod and all lock_zooms) followed by States (sidebar_data and all relayout_datas)
94+
# Order matters; callback_wrapper passes in Inputs before States
95+
lock_zoom_clicks = [new_admissions_lock_zoom, admitted_patients_lock_zoom, SIR_lock_zoom]
96+
graphs_relayout_data = [new_admissions_relayout_data, admitted_patients_relayout_data, SIR_relayout_data]
97+
return IndexCallbacks.handle_model_change(component_instance, sidebar_data, lock_zoom_clicks, graphs_relayout_data)
6198

6299
super().__init__(
63100
component_instance=component_instance,
@@ -72,7 +109,12 @@ def handle_model_change_helper(sidebar_mod, sidebar_data):
72109
callback_fn=IndexCallbacks.toggle_tables
73110
),
74111
ChimeCallback( # If the parameters or model change, update the text
75-
changed_elements={"sidebar-store": "modified_timestamp"},
112+
changed_elements={
113+
"sidebar-store": "modified_timestamp",
114+
"new_admissions_lock_zoom": "n_clicks",
115+
"admitted_patients_lock_zoom": "n_clicks",
116+
"SIR_lock_zoom": "n_clicks"
117+
},
76118
dom_updates={
77119
"intro": "children",
78120
"new_admissions_graph": "figure",
@@ -85,8 +127,28 @@ def handle_model_change_helper(sidebar_mod, sidebar_data):
85127
"SIR_table": "children",
86128
"SIR_download": "href",
87129
},
88-
callback_fn=handle_model_change_helper,
130+
states={
131+
"new_admissions_graph": "relayoutData",
132+
"admitted_patients_graph": "relayoutData",
133+
"SIR_graph": "relayoutData"
134+
},
89135
stores=["sidebar-store"],
136+
callback_fn=handle_model_change_helper
137+
),
138+
ChimeCallback( # If user presses the Lock Zoom Button, update outline / solid color
139+
changed_elements={"new_admissions_lock_zoom": "n_clicks"},
140+
dom_updates={"new_admissions_lock_zoom": "outline"},
141+
callback_fn=IndexCallbacks.change_btn_color
142+
),
143+
ChimeCallback(
144+
changed_elements={"admitted_patients_lock_zoom": "n_clicks"},
145+
dom_updates={"admitted_patients_lock_zoom": "outline"},
146+
callback_fn=IndexCallbacks.change_btn_color
147+
),
148+
ChimeCallback(
149+
changed_elements={"SIR_lock_zoom": "n_clicks"},
150+
dom_updates={"SIR_lock_zoom": "outline"},
151+
callback_fn=IndexCallbacks.change_btn_color
90152
)
91153
]
92154
)
@@ -139,8 +201,7 @@ def update_parameters(i, *input_values) -> List[dict]:
139201
ventilated=Disposition.create(
140202
days=inputs_dict["ventilated_los"],
141203
rate=inputs_dict["ventilated_rate"] / 100,
142-
),
143-
max_y_axis=inputs_dict.get("max_y_axis_value", None),
204+
)
144205
)
145206
return [{"inputs_dict": inputs_dict, "parameters": parameters_serializer(pars)}]
146207

src/chime_dash/app/services/plotting.py

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,13 @@
77
from pandas import DataFrame
88

99

10-
def plot_dataframe(
11-
dataframe: DataFrame,
12-
max_y_axis: int = None,
13-
) -> Dict[str, Any]:
10+
def plot_dataframe(dataframe: DataFrame) -> Dict[str, Any]:
1411
"""Returns dictionary used for plotly graphs
1512
1613
Arguments:
1714
dataframe: The dataframe to plot. Plots all columns as y, index is x.
18-
max_y_axis: Maximal value on y-axis.
1915
"""
2016

21-
if max_y_axis is None:
22-
yaxis = {}
23-
else:
24-
yaxis = {"range": (0, max_y_axis), "autorange": False}
25-
2617
return {
2718
"data": [
2819
{
@@ -34,7 +25,8 @@ def plot_dataframe(
3425
for col in dataframe.columns
3526
],
3627
"layout": {
37-
"yaxis": yaxis,
28+
"xaxis": {},
29+
"yaxis": {},
3830
"legend": {"orientation": "h"},
3931
},
4032
}

src/chime_dash/app/templates/en/sidebar.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@ ventilated_los: Average days on ventilator
2020
display_parameters: Display Parameters
2121
n_days: Number of days to project
2222
current_date: Current date (default is today)
23-
max_y_axis: Set the Y-axis on graphs to a static
24-
max_y_axis_value: Maximal y-axis value (leave blank for no max value)
2523
show_tables: Show tables with data
2624
show_tool_details: Show more info about this tool
2725
show_additional_projections: Show additional projections

src/chime_dash/app/utils/__init__.py

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,6 @@ def parameters_deserializer(p_json: str):
8282
date_first_hospitalized=dates["date_first_hospitalized"],
8383
doubling_time=values["doubling_time"],
8484
market_share=values["market_share"],
85-
max_y_axis=values["max_y_axis"],
8685
mitigation_date=dates["mitigation_date"],
8786
n_days=values["n_days"],
8887
population=values["population"],
@@ -120,8 +119,6 @@ def prepare_visualization_group(df: DataFrame = None, **kwargs) -> List[Any]:
120119
df: The Dataframe to plot
121120
content: Dict[str, str]
122121
Mapping for translating columns and index.
123-
max_y_axis: int
124-
Maximal value on y-axis
125122
labels: List[str]
126123
Columns to display
127124
table_mod: int
@@ -148,11 +145,9 @@ def prepare_visualization_group(df: DataFrame = None, **kwargs) -> List[Any]:
148145
day_column = content.get(day_column, day_column)
149146

150147
plot_data = plot_dataframe(
151-
df.dropna().set_index(date_column).drop(columns=[day_column]),
152-
max_y_axis=kwargs.get("max_y_axis", None),
148+
df.dropna().set_index(date_column).drop(columns=[day_column])
153149
)
154150

155-
156151
# translate back for backwards compability of build_table
157152
column_map = {day_column: "day", date_column: "date"}
158153
table = (

src/chime_dash/app/utils/callbacks.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ def __init__(self,
1111
callback_fn: Callable,
1212
dom_updates: Mapping = None,
1313
stores: Iterable = None,
14+
states: Mapping = None,
1415
memoize: bool = True
1516
):
1617
self.inputs = [
@@ -19,6 +20,7 @@ def __init__(self,
1920
]
2021
self.outputs = []
2122
self.stores = []
23+
self.states = []
2224
self.callback_fn = callback_fn
2325
self.memoize = memoize
2426
if dom_updates:
@@ -27,20 +29,25 @@ def __init__(self,
2729
for component_id, component_property in dom_updates.items()
2830
)
2931
if stores:
30-
self.stores.extend(
32+
self.states.extend(
3133
State(component_id=component_id, component_property="data")
3234
for component_id in stores
3335
)
36+
if states:
37+
self.states.extend(
38+
State(component_id=component_id, component_property=component_property)
39+
for component_id, component_property in states.items()
40+
)
3441

3542
def wrap(self, app: Dash):
3643
if self.memoize:
3744
@lru_cache(maxsize=32)
38-
@app.callback(self.outputs, self.inputs, self.stores)
45+
@app.callback(self.outputs, self.inputs, self.states)
3946
def callback_wrapper(*args, **kwargs):
4047
print(str(self.callback_fn))
4148
return self.callback_fn(*args, **kwargs)
4249
else:
43-
@app.callback(self.outputs, self.inputs, self.stores)
50+
@app.callback(self.outputs, self.inputs, self.states)
4451
def callback_wrapper(*args, **kwargs):
4552
return self.callback_fn(*args, **kwargs)
4653

0 commit comments

Comments
 (0)