|
1 | 1 | """components/sidebar
|
2 | 2 | Initializes the side bar containing the various inputs for the model
|
3 | 3 |
|
4 |
| -#! _INPUTS should be considered for moving else where |
| 4 | +#! _SIDEBAR_ELEMENTS should be considered for moving else where |
5 | 5 | """
|
6 |
| -from dash_html_components import Nav, Div |
7 |
| - |
8 |
| -from typing import List, Dict, Any, Tuple |
| 6 | +from typing import List |
9 | 7 | from collections import OrderedDict
|
10 | 8 | from datetime import date, datetime
|
11 | 9 |
|
12 | 10 | from dash.development.base_component import ComponentMeta
|
13 |
| -from dash_html_components import Nav, Div, Hr |
14 |
| - |
15 |
| -from penn_chime.parameters import Parameters, Disposition |
| 11 | +from dash_html_components import Nav, Div |
16 | 12 |
|
17 |
| -from chime_dash.app.components.base import Component |
18 |
| -from chime_dash.app.utils import parameters_serializer |
19 |
| -from chime_dash.app.utils.callbacks import ChimeCallback |
| 13 | +from chime_dash.app.components.base import Page |
| 14 | +from chime_dash.app.utils import ReadOnlyDict |
20 | 15 | from chime_dash.app.utils.templates import (
|
21 | 16 | create_switch_input,
|
22 | 17 | create_number_input,
|
23 | 18 | create_date_input,
|
24 | 19 | create_header,
|
25 | 20 | )
|
| 21 | +from chime_dash.app.services.callbacks import SidebarCallbacks |
26 | 22 |
|
27 | 23 | FLOAT_INPUT_MIN = 0.001
|
28 | 24 | FLOAT_INPUT_STEP = "any"
|
29 | 25 |
|
30 |
| -_INPUTS = OrderedDict( |
| 26 | +_SIDEBAR_ELEMENTS = ReadOnlyDict(OrderedDict( |
31 | 27 | ###
|
32 | 28 | hospital_parameters={"type": "header", "size": "h3"},
|
33 | 29 | population={"type": "number", "min": 1, "step": 1},
|
|
94 | 90 | max_y_axis_value={"type": "number", "min": 10, "step": 10, "value": None},
|
95 | 91 | show_tables={"type": "switch", "value": False},
|
96 | 92 | show_tool_details={"type": "switch", "value": False},
|
97 |
| -) |
| 93 | +)) |
98 | 94 |
|
99 |
| -# Different kind of inputs store different kind of "values" |
100 |
| -# This tells the callback output for which field to look |
101 |
| -_PROPERTY_OUTPUT_MAP = { |
102 |
| - "number": "value", |
103 |
| - "date": "date", |
104 |
| -} |
105 | 95 |
|
106 |
| - |
107 |
| -class Sidebar(Component): |
| 96 | +class Sidebar(Page): |
108 | 97 | """Sidebar to the left of the screen
|
109 | 98 | contains the various inputs used to interact
|
110 | 99 | with the model.
|
111 | 100 | """
|
| 101 | + callbacks_cls = SidebarCallbacks |
112 | 102 |
|
113 | 103 | # localization temp. for widget descriptions
|
114 | 104 | localization_file = "sidebar.yml"
|
115 |
| - |
116 |
| - @staticmethod |
117 |
| - def get_ordered_input_keys(): |
118 |
| - return [key for key in _INPUTS if _INPUTS[key]["type"] not in ("header", )] |
119 |
| - |
120 |
| - @staticmethod |
121 |
| - def get_formated_values(input_values): |
122 |
| - result = dict(zip(Sidebar.get_ordered_input_keys(), input_values)) |
123 |
| - # todo remove this hack needed because of how Checklist type used for switch input returns values |
124 |
| - for key in _INPUTS: |
125 |
| - if _INPUTS[key]["type"] == "switch": |
126 |
| - value = False |
127 |
| - if result[key] == [True]: |
128 |
| - value = True |
129 |
| - result[key] = value |
130 |
| - elif _INPUTS[key]["type"] == "date": |
131 |
| - value = result[key] |
132 |
| - result[key] = datetime.strptime(value, "%Y-%m-%d").date() if value else value |
133 |
| - return result |
134 |
| - |
135 |
| - @staticmethod |
136 |
| - def update_parameters(*input_values, **kwargs) -> List[str]: |
137 |
| - """Reads html form outputs and converts them to a parameter instance |
138 |
| -
|
139 |
| - Returns Parameters |
140 |
| - """ |
141 |
| - inputs_dict = Sidebar.get_formated_values(input_values) |
142 |
| - dt = inputs_dict["doubling_time"] if inputs_dict["doubling_time"] else None |
143 |
| - dfh = inputs_dict["date_first_hospitalized"] if not dt else None |
144 |
| - pars = Parameters( |
145 |
| - population=inputs_dict["population"], |
146 |
| - current_hospitalized=inputs_dict["current_hospitalized"], |
147 |
| - date_first_hospitalized=dfh, |
148 |
| - doubling_time=dt, |
149 |
| - hospitalized=Disposition( |
150 |
| - inputs_dict["hospitalized_rate"] / 100, inputs_dict["hospitalized_los"] |
151 |
| - ), |
152 |
| - icu=Disposition(inputs_dict["icu_rate"] / 100, inputs_dict["icu_los"]), |
153 |
| - infectious_days=inputs_dict["infectious_days"], |
154 |
| - market_share=inputs_dict["market_share"] / 100, |
155 |
| - n_days=inputs_dict["n_days"], |
156 |
| - relative_contact_rate=inputs_dict["relative_contact_rate"] / 100, |
157 |
| - ventilated=Disposition( |
158 |
| - inputs_dict["ventilated_rate"] / 100, inputs_dict["ventilated_los"] |
159 |
| - ), |
160 |
| - max_y_axis=inputs_dict.get("max_y_axis_value", None), |
161 |
| - ) |
162 |
| - return [parameters_serializer(pars)] |
163 |
| - |
164 |
| - def __init__(self, language, defaults): |
165 |
| - changed_elements = OrderedDict( |
166 |
| - (key, _PROPERTY_OUTPUT_MAP.get(_INPUTS[key]["type"], "value")) |
167 |
| - for key in _INPUTS |
168 |
| - if _INPUTS[key]["type"] not in ("header",) |
169 |
| - ) |
170 |
| - |
171 |
| - input_change_callback = ChimeCallback( |
172 |
| - changed_elements=changed_elements, |
173 |
| - dom_updates=OrderedDict(pars="children"), |
174 |
| - callback_fn=Sidebar.update_parameters, |
175 |
| - ) |
176 |
| - super().__init__(language, defaults, [input_change_callback]) |
| 105 | + # Different kind of inputs store different kind of "values" |
| 106 | + # This tells the callback output for which field to look |
| 107 | + input_type_map = ReadOnlyDict(OrderedDict( |
| 108 | + (key, value["type"]) |
| 109 | + for key, value in _SIDEBAR_ELEMENTS.items() |
| 110 | + if value["type"] not in ("header",) |
| 111 | + )) |
| 112 | + input_value_map = ReadOnlyDict(OrderedDict( |
| 113 | + (key, {"number": "value", "date": "date"}.get(value, "value")) |
| 114 | + for key, value in input_type_map.items() |
| 115 | + )) |
177 | 116 |
|
178 | 117 | def get_html(self) -> List[ComponentMeta]:
|
179 | 118 | """Initializes the view
|
180 | 119 | """
|
181 | 120 | elements = [
|
182 | 121 | Div(id='pars', style={'display': 'none'})
|
183 | 122 | ]
|
184 |
| - for idx, data in _INPUTS.items(): |
| 123 | + for idx, data in _SIDEBAR_ELEMENTS.items(): |
185 | 124 | if data["type"] == "number":
|
186 | 125 | element = create_number_input(idx, data, self.content, self.defaults)
|
187 | 126 | elif data["type"] == "switch":
|
@@ -216,4 +155,3 @@ def get_html(self) -> List[ComponentMeta]:
|
216 | 155 | )
|
217 | 156 |
|
218 | 157 | return [sidebar]
|
219 |
| - |
0 commit comments