1
1
from typing import List
2
- from datetime import date , datetime
2
+ from datetime import datetime
3
+ from collections import OrderedDict
4
+ from urllib .parse import parse_qsl , urlencode
5
+ from dash .exceptions import PreventUpdate
6
+ from dateutil .parser import parse as parse_date
3
7
4
8
from chime_dash .app .utils .callbacks import ChimeCallback , register_callbacks
5
9
from chime_dash .app .utils import (
@@ -30,13 +34,13 @@ def toggle_tables(switch_value):
30
34
return get_n_switch_values (switch_value , 3 )
31
35
32
36
@staticmethod
33
- def handle_model_change (i , pars_json ):
37
+ def handle_model_change (i , sidebar_data ):
34
38
model = {}
35
39
pars = None
36
40
result = []
37
41
viz_kwargs = {}
38
- if pars_json :
39
- pars = parameters_deserializer (pars_json )
42
+ if sidebar_data :
43
+ pars = parameters_deserializer (sidebar_data [ "parameters" ] )
40
44
model = SimSirModel (pars )
41
45
viz_kwargs = dict (
42
46
labels = pars .labels ,
@@ -53,9 +57,8 @@ def handle_model_change(i, pars_json):
53
57
return result
54
58
55
59
def __init__ (self , component_instance ):
56
- def handle_model_change_helper (* args , ** kwargs ):
57
- pars_json = args [1 ]
58
- return IndexCallbacks .handle_model_change (component_instance , pars_json )
60
+ def handle_model_change_helper (sidebar_mod , sidebar_data ):
61
+ return IndexCallbacks .handle_model_change (component_instance , sidebar_data )
59
62
60
63
super ().__init__ (
61
64
component_instance = component_instance ,
@@ -75,7 +78,7 @@ def handle_model_change_helper(*args, **kwargs):
75
78
callback_fn = IndexCallbacks .toggle_tables
76
79
),
77
80
ChimeCallback ( # If the parameters or model change, update the text
78
- changed_elements = {"root -store" : "modified_timestamp" },
81
+ changed_elements = {"sidebar -store" : "modified_timestamp" },
79
82
dom_updates = {
80
83
"intro" : "children" ,
81
84
"more_intro" : "children" ,
@@ -90,7 +93,7 @@ def handle_model_change_helper(*args, **kwargs):
90
93
"SIR_download" : "href" ,
91
94
},
92
95
callback_fn = handle_model_change_helper ,
93
- stores = ['root -store' ],
96
+ stores = ["sidebar -store" ],
94
97
)
95
98
]
96
99
)
@@ -107,11 +110,14 @@ def get_formated_values(i, input_values):
107
110
result [key ] = False if result [key ] == [True ] else True
108
111
elif input_type == "date" :
109
112
value = result [key ]
110
- result [key ] = datetime .strptime (value , "%Y-%m-%d" ).date () if value else value
113
+ try :
114
+ result [key ] = datetime .strptime (value , "%Y-%m-%d" ).date () if value else value
115
+ except ValueError :
116
+ pass
111
117
return result
112
118
113
119
@staticmethod
114
- def update_parameters (i , * input_values ) -> List [str ]:
120
+ def update_parameters (i , * input_values ) -> List [dict ]:
115
121
"""Reads html form outputs and converts them to a parameter instance
116
122
117
123
Returns Parameters
@@ -137,7 +143,7 @@ def update_parameters(i, *input_values) -> List[str]:
137
143
),
138
144
max_y_axis = inputs_dict .get ("max_y_axis_value" , None ),
139
145
)
140
- return [parameters_serializer (pars )]
146
+ return [{ "inputs_dict" : inputs_dict , "parameters" : parameters_serializer (pars )} ]
141
147
142
148
def __init__ (self , component_instance ):
143
149
def update_parameters_helper (* args , ** kwargs ):
@@ -148,17 +154,93 @@ def update_parameters_helper(*args, **kwargs):
148
154
callbacks = [
149
155
ChimeCallback (
150
156
changed_elements = component_instance .input_value_map ,
151
- dom_updates = {"root -store" : "data" },
157
+ dom_updates = {"sidebar -store" : "data" },
152
158
callback_fn = update_parameters_helper ,
153
- stores = ['root-store' ],
154
159
)
155
160
]
156
161
)
157
162
158
163
164
+ # todo Add tons of tests and validation because there be dragons
159
165
class RootCallbacks (ComponentCallbacks ):
166
+ @staticmethod
167
+ def try_parsing_number (v ):
168
+ try :
169
+ return int (v )
170
+ except ValueError :
171
+ try :
172
+ return float (v )
173
+ except ValueError :
174
+ return v
175
+
176
+ @staticmethod
177
+ def get_inputs (val_dict , inputs_keys ):
178
+ # todo handle versioning of inputs
179
+ return OrderedDict ((key , value ) for key , value in val_dict .items () if key in inputs_keys )
180
+
181
+ @staticmethod
182
+ def parse_hash (hash_str , sidebar_input_types ):
183
+ hash_dict = dict (parse_qsl (hash_str [1 :]))
184
+ for key , value in hash_dict .items ():
185
+ value_type = sidebar_input_types [key ]
186
+ if value_type == "number" :
187
+ parsed_value = RootCallbacks .try_parsing_number (value )
188
+ # elif value_type == "date":
189
+ # parsed_value = parse_date(value)
190
+ else :
191
+ parsed_value = value
192
+ hash_dict [key ] = parsed_value
193
+ return hash_dict
194
+
195
+ @staticmethod
196
+ def hash_changed (sidebar_input_types , hash_str = None , root_data = None ):
197
+ if hash_str :
198
+ hash_dict = RootCallbacks .parse_hash (hash_str , sidebar_input_types )
199
+ result = RootCallbacks .get_inputs (hash_dict , sidebar_input_types .keys ())
200
+ # Don't update the data store if it already contains the same data
201
+ if result == root_data :
202
+ raise PreventUpdate
203
+ else :
204
+ raise PreventUpdate
205
+ return [result ]
206
+
207
+ @staticmethod
208
+ def stores_changed (inputs_keys , root_mod , sidebar_mod , root_data , sidebar_data ):
209
+ root_modified = root_mod or 0
210
+ sidebar_modified = sidebar_mod or 0
211
+ if root_modified < sidebar_modified :
212
+ inputs_dict = sidebar_data ["inputs_dict" ]
213
+ new_val = RootCallbacks .get_inputs (inputs_dict , inputs_keys )
214
+ elif root_modified > sidebar_modified :
215
+ new_val = RootCallbacks .get_inputs (root_data , inputs_keys )
216
+ else :
217
+ raise PreventUpdate
218
+ return ["#{}" .format (urlencode (new_val ))] + list (new_val .values ())
219
+
160
220
def __init__ (self , component_instance ):
221
+ sidebar = component_instance .components ["sidebar" ]
222
+ sidebar_inputs = sidebar .input_value_map
223
+ sidebar_input_types = sidebar .input_type_map
224
+
225
+ def hash_changed_helper (hash_str = None , root_data = None ):
226
+ return RootCallbacks .hash_changed (sidebar_input_types , hash_str , root_data )
227
+
228
+ def stores_changed_helper (root_mod , sidebar_mod , root_data , sidebar_data ):
229
+ return RootCallbacks .stores_changed (sidebar_inputs .keys (), root_mod , sidebar_mod , root_data , sidebar_data )
161
230
super ().__init__ (
162
231
component_instance = component_instance ,
163
- callbacks = []
232
+ callbacks = [
233
+ ChimeCallback (
234
+ changed_elements = {"location" : "hash" },
235
+ dom_updates = {"root-store" : "data" },
236
+ callback_fn = hash_changed_helper ,
237
+ stores = ["root-store" ],
238
+ ),
239
+ ChimeCallback (
240
+ changed_elements = {"root-store" : "modified_timestamp" , "sidebar-store" : "modified_timestamp" },
241
+ dom_updates = {"location" : "hash" , ** sidebar_inputs },
242
+ callback_fn = stores_changed_helper ,
243
+ stores = ["root-store" , "sidebar-store" ],
244
+ ),
245
+ ]
164
246
)
0 commit comments