Skip to content

Commit 120446b

Browse files
author
Eric Smyth
committed
Modified callbacks to support dash_core_components.Store rather than storing state in hidden Div
Moved Body class from `components/__init__.py` to `pages/root.py` and renamed to Root Moved singleton decorator function from `components/__init__.py` to `utils/__init__.py` Removed hidden Div that stored Parameters JSON string
1 parent 2ec23ff commit 120446b

File tree

7 files changed

+113
-97
lines changed

7 files changed

+113
-97
lines changed
Lines changed: 9 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,75 +1,12 @@
1-
"""Combines all components
1+
"""app/components
22
3-
The `sidebar` component combines all the inputs while other components potentially
4-
have callbacks.
3+
reusable components that desc a bit of UI - nothing should be defined here only initialized
54
6-
To add or remove components, adjust the `setup`.
7-
"""
8-
from collections import OrderedDict
9-
10-
from dash_bootstrap_components import Container, Row
11-
from dash_bootstrap_components.themes import BOOTSTRAP
12-
from dash_html_components import Div
13-
14-
from chime_dash.app.components.base import Component
15-
from chime_dash.app.components.navbar import Navbar
16-
from chime_dash.app.pages.index import Index
17-
from chime_dash.app.pages.sidebar import Sidebar
18-
19-
20-
def singleton(class_):
21-
instances = {}
22-
23-
def get_instance(*args, **kwargs):
24-
if class_ not in instances:
25-
instances[class_] = class_(*args, **kwargs)
26-
return instances[class_]
27-
return get_instance
5+
for pages definitions see -> app/pages
6+
for core logic see --> app/services
7+
for utility classes and functions see --> utils
288
29-
30-
@singleton
31-
class Body(Component):
32-
"""
33-
"""
34-
external_stylesheets = [
35-
BOOTSTRAP,
36-
'https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,400;0,600;1,400;1,600&display=swap',
37-
]
38-
39-
def __init__(self, language, defaults):
40-
"""
41-
"""
42-
super().__init__(language, defaults)
43-
self.components = OrderedDict(
44-
navbar=Navbar(language, defaults),
45-
sidebar=Sidebar(language, defaults),
46-
# todo subscribe to changes to URL and select page appropriately
47-
index=Index(language, defaults),
48-
)
49-
50-
def get_html(self):
51-
"""Glues individual setup components together
52-
"""
53-
return Div(
54-
className="app",
55-
children=self.components["navbar"].html + [
56-
Div(
57-
className="app-content",
58-
children=self.components["sidebar"].html
59-
+ self.components["index"].html
60-
)
61-
]
62-
)
63-
64-
def get_html_old(self):
65-
"""Glues individual setup components together
66-
"""
67-
return Div(children=self.components["navbar"].html
68-
+ [Container(
69-
children=Row(self.components["sidebar"].html + [Div(
70-
id="page-wrapper",
71-
children=self.components["index"].html
72-
)]),
73-
fluid=True,
74-
className="mt-5",
75-
)])
9+
modules desc. route
10+
---- ---- ----
11+
index homepage /
12+
"""

src/chime_dash/app/pages/root.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
"""Combines all components
2+
3+
The `sidebar` component combines all the inputs while other components potentially
4+
have callbacks.
5+
6+
To add or remove components, adjust the `setup`.
7+
"""
8+
from collections import OrderedDict
9+
10+
from dash_bootstrap_components import Container, Row
11+
from dash_bootstrap_components.themes import BOOTSTRAP
12+
from dash_html_components import Div
13+
from dash_core_components import Store
14+
15+
from chime_dash.app.components.base import Page
16+
from chime_dash.app.components.navbar import Navbar
17+
from chime_dash.app.pages.index import Index
18+
from chime_dash.app.pages.sidebar import Sidebar
19+
from chime_dash.app.utils import singleton
20+
from chime_dash.app.services.callbacks import RootCallbacks
21+
22+
@singleton
23+
class Root(Page):
24+
"""
25+
"""
26+
external_stylesheets = [
27+
BOOTSTRAP,
28+
'https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,400;0,600;1,400;1,600&display=swap',
29+
]
30+
callbacks_cls = RootCallbacks
31+
32+
def __init__(self, language, defaults):
33+
"""
34+
"""
35+
super().__init__(language, defaults)
36+
self.components = OrderedDict(
37+
navbar=Navbar(language, defaults),
38+
sidebar=Sidebar(language, defaults),
39+
# todo subscribe to changes to URL and select page appropriately
40+
index=Index(language, defaults),
41+
)
42+
43+
def get_html(self):
44+
"""Glues individual setup components together
45+
"""
46+
return Div(
47+
className="app",
48+
children=self.components["navbar"].html + [
49+
Div(
50+
className="app-content",
51+
children=self.components["sidebar"].html
52+
+ self.components["index"].html
53+
),
54+
Store(id="root-store")
55+
]
56+
)

src/chime_dash/app/pages/sidebar.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -117,9 +117,7 @@ class Sidebar(Page):
117117
def get_html(self) -> List[ComponentMeta]:
118118
"""Initializes the view
119119
"""
120-
elements = [
121-
Div(id='pars', style={'display': 'none'})
122-
]
120+
elements = []
123121
for idx, data in _SIDEBAR_ELEMENTS.items():
124122
if data["type"] == "number":
125123
element = create_number_input(idx, data, self.content, self.defaults)

src/chime_dash/app/run.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@
55

66
from dash import Dash
77
from penn_chime.settings import DEFAULTS
8-
from chime_dash.app.components import Body
8+
from chime_dash.app.pages.root import Root
99
from chime_dash.app.utils.callbacks import wrap_callbacks
1010

1111
LANGUAGE = "en"
1212

13-
body = Body(LANGUAGE, DEFAULTS)
13+
body = Root(LANGUAGE, DEFAULTS)
1414

1515
DASH = Dash(
1616
__name__,

src/chime_dash/app/services/callbacks.py

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@ def handle_model_change(i, pars_json):
5353
return result
5454

5555
def __init__(self, component_instance):
56-
def handle_model_change_helper(pars_json):
56+
def handle_model_change_helper(*args, **kwargs):
57+
pars_json = args[1]
5758
return IndexCallbacks.handle_model_change(component_instance, pars_json)
5859

5960
super().__init__(
@@ -74,7 +75,7 @@ def handle_model_change_helper(pars_json):
7475
callback_fn=IndexCallbacks.toggle_tables
7576
),
7677
ChimeCallback( # If the parameters or model change, update the text
77-
changed_elements={"pars": "children"},
78+
changed_elements={"root-store": "modified_timestamp"},
7879
dom_updates={
7980
"intro": "children",
8081
"more_intro": "children",
@@ -88,7 +89,8 @@ def handle_model_change_helper(pars_json):
8889
"SIR_table": "children",
8990
"SIR_download": "href",
9091
},
91-
callback_fn=handle_model_change_helper
92+
callback_fn=handle_model_change_helper,
93+
stores=['root-store'],
9294
)
9395
]
9496
)
@@ -146,8 +148,17 @@ def update_parameters_helper(*args, **kwargs):
146148
callbacks=[
147149
ChimeCallback(
148150
changed_elements=component_instance.input_value_map,
149-
dom_updates={"pars": "children"},
150-
callback_fn=update_parameters_helper
151+
dom_updates={"root-store": "data"},
152+
callback_fn=update_parameters_helper,
153+
stores=['root-store'],
151154
)
152155
]
153156
)
157+
158+
159+
class RootCallbacks(ComponentCallbacks):
160+
def __init__(self, component_instance):
161+
super().__init__(
162+
component_instance=component_instance,
163+
callbacks=[]
164+
)

src/chime_dash/app/utils/__init__.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,3 +137,13 @@ def prepare_visualization_group(df: DataFrame = None, **kwargs) -> List[Any]:
137137
result = [plot_data, table, csv]
138138

139139
return result
140+
141+
142+
def singleton(class_):
143+
instances = {}
144+
145+
def get_instance(*args, **kwargs):
146+
if class_ not in instances:
147+
instances[class_] = class_(*args, **kwargs)
148+
return instances[class_]
149+
return get_instance

src/chime_dash/app/utils/callbacks.py

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,45 @@
11
from dash import Dash
2-
from dash.exceptions import DuplicateCallbackOutput
3-
# todo Change to `from collections.abc import Iterable`. Using OrderedDict prevents multiple outputs to same DOM element
4-
# todo e.g., update children and toggle hidden
5-
from collections import OrderedDict
2+
from dash.dependencies import Input, Output, State
3+
from collections.abc import Iterable, Mapping
64
from typing import Callable, List
75
from functools import lru_cache
86

9-
from dash.dependencies import Input, Output
10-
117

128
class ChimeCallback:
139
def __init__(self,
14-
changed_elements: OrderedDict,
15-
dom_updates: OrderedDict,
10+
changed_elements: Mapping,
1611
callback_fn: Callable,
12+
dom_updates: Mapping = None,
13+
stores: Iterable = None,
1714
memoize: bool = True
1815
):
19-
pass
2016
self.inputs = [
2117
Input(component_id=component_id, component_property=component_property)
2218
for component_id, component_property in changed_elements.items()
2319
]
24-
self.outputs = [
25-
Output(component_id=component_id, component_property=component_property)
26-
for component_id, component_property in dom_updates.items()
27-
]
20+
self.outputs = []
21+
self.stores = []
2822
self.callback_fn = callback_fn
2923
self.memoize = memoize
24+
if dom_updates:
25+
self.outputs.extend(
26+
Output(component_id=component_id, component_property=component_property)
27+
for component_id, component_property in dom_updates.items()
28+
)
29+
if stores:
30+
self.stores.extend(
31+
State(component_id=component_id, component_property="data")
32+
for component_id in stores
33+
)
3034

3135
def wrap(self, app: Dash):
3236
if self.memoize:
3337
@lru_cache(maxsize=32)
34-
@app.callback(self.outputs, self.inputs)
38+
@app.callback(self.outputs, self.inputs, self.stores)
3539
def callback_wrapper(*args, **kwargs):
3640
return self.callback_fn(*args, **kwargs)
3741
else:
38-
@app.callback(self.outputs, self.inputs)
42+
@app.callback(self.outputs, self.inputs, self.stores)
3943
def callback_wrapper(*args, **kwargs):
4044
return self.callback_fn(*args, **kwargs)
4145

0 commit comments

Comments
 (0)