diff --git a/caimira/apps/expert.py b/caimira/apps/expert.py index c8955512..77e7e82b 100644 --- a/caimira/apps/expert.py +++ b/caimira/apps/expert.py @@ -319,7 +319,7 @@ class ModelWidgets(View): ])], title="Infected") def _build_room_volume(self, node): - room_volume = widgets.IntText(value=node.volume, min=10, max=500, step=5) + room_volume = widgets.BoundedIntText(value=node.volume, min=10, max=500, step=5) def on_volume_change(change): node.volume = change['new'] @@ -331,8 +331,8 @@ class ModelWidgets(View): def _build_room_area(self, node): - room_surface = widgets.IntText(value=25, min=1, max=200, step=10) - room_ceiling_height = widgets.IntText(value=3, min=1, max=20, step=1) + room_surface = widgets.BoundedIntText(value=25, min=1, max=200, step=10) + room_ceiling_height = widgets.BoundedIntText(value=3.5, min=1, max=10, step=0.1) displayed_volume=widgets.Label('75') def on_room_surface_change(change): @@ -365,7 +365,6 @@ class ModelWidgets(View): room_w = widgets.RadioButtons( options= list(zip(['Volume', 'Room area and height'], room_widgets.keys())), - button_style='info', layout=widgets.Layout(height='auto', width='auto'), ) @@ -452,7 +451,6 @@ class ModelWidgets(View): window_w = widgets.RadioButtons( options= list(zip(['Sliding window', 'Hinged window'], window_widgets.keys())), - button_style='info', layout=widgets.Layout(height='auto', width='auto'), ) @@ -470,7 +468,7 @@ class ModelWidgets(View): window_w.observe(lambda event: toggle_window(event['new']), 'value') toggle_window(window_w.value) - number_of_windows= widgets.IntText(value= 1, min= 0, max= 5, step=1) + number_of_windows= widgets.BoundedIntText(value=1, min=1, max=10, step=1) period = widgets.IntSlider(value=node.active.period, min=0, max=240) interval = widgets.IntSlider(value=node.active.duration, min=0, max=240) opening_length = widgets.FloatSlider(value=node.opening_length, min=0, max=3, step=0.1) @@ -581,9 +579,8 @@ class ModelWidgets(View): for name, widget in mechanical_widgets.items(): widget.layout.visible = False - mechanival_w = widgets.RadioButtons( + mechanical_w = widgets.RadioButtons( options=list(zip(['Air supply flow rate (m³/h)', 'Air changes per hour (h⁻¹)'], mechanical_widgets.keys())), - button_style='info', ) def toggle_mechanical(value): @@ -596,10 +593,10 @@ class ModelWidgets(View): widget.layout.visible = True widget.layout.display = 'flex' - mechanival_w.observe(lambda event: toggle_mechanical(event['new']), 'value') - toggle_mechanical(mechanival_w.value) + mechanical_w.observe(lambda event: toggle_mechanical(event['new']), 'value') + toggle_mechanical(mechanical_w.value) - return widgets.VBox([mechanival_w, widgets.HBox(list(mechanical_widgets.values()))]) + return widgets.VBox([mechanical_w, widgets.HBox(list(mechanical_widgets.values()))]) def _build_month(self, node) -> WidgetGroup: @@ -811,7 +808,7 @@ class ModelWidgets(View): break virus_choice = widgets.Dropdown(options=list(models.Virus.types.keys()), value=name) transmissibility_factor = widgets.FloatSlider(value=node.transmissibility_factor, min=0, max=1, step=0.1) - infectious_dose = widgets.FloatText(value=node.infectious_dose, placeholder='50', disabled=False) + infectious_dose = widgets.FloatText(value=node.infectious_dose, disabled=False) def on_virus_change(change): virus = models.Virus.types[change['new']] @@ -934,8 +931,6 @@ class CAIMIRAStateBuilder(state.StateBuilder): class ExpertApplication(Controller): def __init__(self) -> None: - self._debug_output = widgets.Output() - #: A list of scenario name and ModelState instances. This is intended to be #: mutated. Any mutation should notify the appropriate Views for handling. self._model_scenarios: typing.List[ScenarioType] = [] @@ -946,7 +941,6 @@ class ExpertApplication(Controller): self._results_tab = widgets.Tab(children=( self.current_scenario_figure.widget, self.comparison_view.widget, - # self._debug_output, )) for i, title in enumerate(['Current scenario', 'Scenario comparison', "Debug"]): self._results_tab.set_title(i, title) diff --git a/caimira/apps/expert_co2.py b/caimira/apps/expert_co2.py index cade8f72..c78a5b10 100644 --- a/caimira/apps/expert_co2.py +++ b/caimira/apps/expert_co2.py @@ -614,7 +614,7 @@ class ModelWidgets(View): for name, widget in mechanical_widgets.items(): widget.layout.visible = False - mechanival_w = widgets.RadioButtons( + mechanical_w = widgets.RadioButtons( options=list(zip(['Air supply flow rate (m³/h)', 'Air changes per hour (h⁻¹)'], mechanical_widgets.keys())), button_style='info', ) @@ -629,10 +629,10 @@ class ModelWidgets(View): widget.layout.visible = True widget.layout.display = 'flex' - mechanival_w.observe(lambda event: toggle_mechanical(event['new']), 'value') - toggle_mechanical(mechanival_w.value) + mechanical_w.observe(lambda event: toggle_mechanical(event['new']), 'value') + toggle_mechanical(mechanical_w.value) - return widgets.VBox([mechanival_w, widgets.HBox(list(mechanical_widgets.values()))]) + return widgets.VBox([mechanical_w, widgets.HBox(list(mechanical_widgets.values()))]) def _build_no_ventilation(self, node): return widgets.HBox([]) diff --git a/caimira/monte_carlo/models.py b/caimira/monte_carlo/models.py index dfa446bd..47e8ebab 100644 --- a/caimira/monte_carlo/models.py +++ b/caimira/monte_carlo/models.py @@ -8,7 +8,7 @@ import caimira.models from .sampleable import SampleableDistribution, _VectorisedFloatOrSampleable _ModelType = typing.TypeVar('_ModelType') - +dataclass_instance = typing.Any class MCModelBase(typing.Generic[_ModelType]): """ @@ -19,7 +19,7 @@ class MCModelBase(typing.Generic[_ModelType]): ``caimira.models` model instance on demand. """ - _base_cls: typing.Type[_ModelType] + _base_cls: typing.Type[dataclass_instance] @classmethod def _to_vectorized_form(cls, item, size): @@ -44,10 +44,10 @@ class MCModelBase(typing.Generic[_ModelType]): for field in dataclasses.fields(self._base_cls): attr = getattr(self, field.name) kwargs[field.name] = self._to_vectorized_form(attr, size) - return self._base_cls(**kwargs) # type: ignore + return self._base_cls(**kwargs) -def _build_mc_model(model: _ModelType) -> typing.Type[MCModelBase[_ModelType]]: +def _build_mc_model(model: dataclass_instance) -> typing.Type[MCModelBase[_ModelType]]: """ Generate a new MCModelBase subclass for the given caimira.models model. diff --git a/caimira/state.py b/caimira/state.py index f284ca4d..c2f2fcd5 100644 --- a/caimira/state.py +++ b/caimira/state.py @@ -137,7 +137,7 @@ class DataclassInstanceState(DataclassState[Datamodel_T]): self._base = dataclass #: The actual instance type that this state represents (i.e. may be a #: subclass of _base). - self._instance_type: typing.Type[Datamodel_T] = dataclass + self._instance_type: typing.Type[dataclass_instance] = dataclass #: The instance of dataclass which this state represents. Undefined until #: sufficient data is provided. @@ -169,7 +169,7 @@ class DataclassInstanceState(DataclassState[Datamodel_T]): self._held_events.clear() self._fire_observers() - def dcs_update_from(self, data: Datamodel_T): + def dcs_update_from(self, data: dataclass_instance): with self.dcs_state_transaction(): self.dcs_set_instance_type(data.__class__) for field in dataclasses.fields(data): @@ -225,7 +225,7 @@ class DataclassInstanceState(DataclassState[Datamodel_T]): else: raise AttributeError(f"No attribute {attr_name} on a {self._instance_type.__name__}") - def dcs_set_instance_type(self, instance_dataclass: typing.Type[Datamodel_T]): + def dcs_set_instance_type(self, instance_dataclass: typing.Type[dataclass_instance]): if not dataclasses.is_dataclass(instance_dataclass): raise TypeError("The given class is not a valid dataclass") if not issubclass(instance_dataclass, self._base): diff --git a/setup.py b/setup.py index 116bc0fd..f9cb7dba 100644 --- a/setup.py +++ b/setup.py @@ -37,13 +37,13 @@ REQUIREMENTS: dict = { 'timezonefinder', 'tornado', 'types-retry', - 'voila <= 0.2.16', + 'voila', ], 'app': [], 'test': [ 'pytest', - 'pytest-mypy != v0.10.1', - 'mypy < 1.1.1', + 'pytest-mypy', + 'mypy', 'pytest-tornasync', 'numpy-stubs @ git+https://github.com/numpy/numpy-stubs.git', 'types-dataclasses',