diff --git a/cara/apps/calculator/model_generator.py b/cara/apps/calculator/model_generator.py index e9557012..8e92f361 100644 --- a/cara/apps/calculator/model_generator.py +++ b/cara/apps/calculator/model_generator.py @@ -71,6 +71,7 @@ class FormData: virus_type: str volume_type: str windows_duration: float + windows_frequency: float window_height: float window_type: str window_width: float @@ -128,6 +129,7 @@ class FormData: 'window_height': 0., 'window_width': 0., 'windows_duration': 0., + 'windows_frequency': 0., 'windows_number': 0, 'window_opening_regime': 'windows_open_permanently', 'short_range_option': 'short_range_no', @@ -322,11 +324,11 @@ class FormData: return outside_temp def ventilation(self) -> models._VentilationBase: - always_on = models.PeriodicInterval(period=60, duration=60) + always_on = models.PeriodicInterval(period=120, duration=120) # Initializes a ventilation instance as a window if 'natural_ventilation' is selected, or as a HEPA-filter otherwise if self.ventilation_type == 'natural_ventilation': if self.window_opening_regime == 'windows_open_periodically': - window_interval = models.PeriodicInterval(60, self.windows_duration, min(self.infected_start, self.exposed_start)/60) + window_interval = models.PeriodicInterval(self.windows_frequency, self.windows_duration, min(self.infected_start, self.exposed_start)/60) else: window_interval = always_on @@ -715,6 +717,7 @@ def baseline_raw_form_data() -> typing.Dict[str, typing.Union[str, float]]: 'virus_type': 'SARS_CoV_2', 'volume_type': 'room_volume_explicit', 'windows_duration': '', + 'windows_frequency': '', 'window_height': '2', 'window_type': 'window_sliding', 'window_width': '2', diff --git a/cara/apps/calculator/static/js/form.js b/cara/apps/calculator/static/js/form.js index 7471ccea..26e72eea 100644 --- a/cara/apps/calculator/static/js/form.js +++ b/cara/apps/calculator/static/js/form.js @@ -125,6 +125,7 @@ function require_natural_ventilation(option) { if (!option) { require_input_field("#window_width", option); require_input_field("#windows_duration", option); + require_input_field("#windows_frequency", option); } } @@ -145,7 +146,9 @@ function require_air_supply(option) { function require_venting(option) { require_input_field("#windows_duration", option); + require_input_field("#windows_frequency", option); set_disabled_status("#windows_duration", !option); + set_disabled_status("#windows_frequency", !option); } function require_lunch(id, option) { @@ -401,13 +404,14 @@ function validate_form(form) { } }); - //Validate window venting duration < 60 minutes. + //Validate window venting duration < venting frequency if (!$("#windows_duration").hasClass("disabled")) { var windowsDurationObj = document.getElementById("windows_duration"); - removeErrorFor(windowsDurationObj); + var windowsFrequencyObj = document.getElementById("windows_frequency"); + removeErrorFor(windowsFrequencyObj); - if (parseInt(windowsDurationObj.value) > 60.) { - insertErrorFor(windowsDurationObj, "Duration > 60 minutes."); + if (parseInt(windowsDurationObj.value) >= parseInt(windowsFrequencyObj.value)) { + insertErrorFor(windowsFrequencyObj, "Duration >= Frequency"); submit = false; } } diff --git a/cara/apps/templates/base/calculator.form.html.j2 b/cara/apps/templates/base/calculator.form.html.j2 index fd8e1fd1..c4bf405f 100644 --- a/cara/apps/templates/base/calculator.form.html.j2 +++ b/cara/apps/templates/base/calculator.form.html.j2 @@ -237,8 +237,9 @@
-
- +
+ +

diff --git a/cara/apps/templates/base/calculator.report.html.j2 b/cara/apps/templates/base/calculator.report.html.j2 index 67b579a8..0b6b63c8 100644 --- a/cara/apps/templates/base/calculator.report.html.j2 +++ b/cara/apps/templates/base/calculator.report.html.j2 @@ -325,7 +325,8 @@
  • Opening distance: {{ form.opening_distance }} m

  • Windows open: {% if form.window_opening_regime == "windows_open_periodically" %} - Periodically for {{ form.windows_duration | readable_minutes}} every 1 hour. + Periodically for {{ form.windows_duration | readable_minutes}} + every {{ form.windows_frequency | readable_minutes}} {% elif form.window_opening_regime == "windows_open_permanently" %} Permanently {% endif %} diff --git a/cara/tests/apps/calculator/test_model_generator.py b/cara/tests/apps/calculator/test_model_generator.py index a00bde34..cd7de105 100644 --- a/cara/tests/apps/calculator/test_model_generator.py +++ b/cara/tests/apps/calculator/test_model_generator.py @@ -45,7 +45,8 @@ def test_blend_expiration(mask_type): def test_ventilation_slidingwindow(baseline_form: model_generator.FormData): baseline_form.ventilation_type = 'natural_ventilation' - baseline_form.windows_duration = 5 + baseline_form.windows_duration = 10 + baseline_form.windows_frequency = 120 baseline_form.window_opening_regime = 'windows_open_periodically' baseline_form.window_type = 'window_sliding' baseline_form.event_month = 'December' @@ -58,13 +59,13 @@ def test_ventilation_slidingwindow(baseline_form: model_generator.FormData): assert isinstance(baseline_window, models.SlidingWindow) window = models.SlidingWindow( - active=models.PeriodicInterval(period=60, duration=5, start=9), + active=models.PeriodicInterval(period=120, duration=10, start=9), outside_temp=baseline_window.outside_temp, window_height=1.6, opening_length=0.6, ) ach = models.AirChange( - active=models.PeriodicInterval(period=60, duration=60), + active=models.PeriodicInterval(period=120, duration=120), air_exch=0.25, ) ventilation = models.MultipleVentilation((window, ach)) @@ -74,7 +75,8 @@ def test_ventilation_slidingwindow(baseline_form: model_generator.FormData): def test_ventilation_hingedwindow(baseline_form: model_generator.FormData): baseline_form.ventilation_type = 'natural_ventilation' - baseline_form.windows_duration = 5 + baseline_form.windows_duration = 10 + baseline_form.windows_frequency = 120 baseline_form.window_opening_regime = 'windows_open_periodically' baseline_form.window_type = 'window_hinged' baseline_form.event_month = 'December' @@ -88,12 +90,12 @@ def test_ventilation_hingedwindow(baseline_form: model_generator.FormData): assert isinstance(baseline_window, models.HingedWindow) window = models.HingedWindow( - active=models.PeriodicInterval(period=60, duration=5, start=9), + active=models.PeriodicInterval(period=120, duration=10, start=9), outside_temp=baseline_window.outside_temp, window_height=1.6, window_width=1., opening_length=0.6, ) ach = models.AirChange( - active=models.PeriodicInterval(period=60, duration=60), + active=models.PeriodicInterval(period=120, duration=120), air_exch=0.25, ) ventilation = models.MultipleVentilation((window, ach)) @@ -133,7 +135,8 @@ def test_ventilation_airchanges(baseline_form: model_generator.FormData): def test_ventilation_window_hepa(baseline_form: model_generator.FormData): baseline_form.ventilation_type = 'natural_ventilation' - baseline_form.windows_duration = 5 + baseline_form.windows_duration = 10 + baseline_form.windows_frequency = 120 baseline_form.window_opening_regime = 'windows_open_periodically' baseline_form.event_month = 'December' baseline_form.window_height = 1.6 @@ -147,16 +150,16 @@ def test_ventilation_window_hepa(baseline_form: model_generator.FormData): # Now build the equivalent ventilation instance directly, and compare. window = models.SlidingWindow( - active=models.PeriodicInterval(period=60, duration=5, start=9), + active=models.PeriodicInterval(period=120, duration=10, start=9), outside_temp=baseline_window.outside_temp, window_height=1.6, opening_length=0.6, ) hepa = models.HEPAFilter( - active=models.PeriodicInterval(period=60, duration=60), + active=models.PeriodicInterval(period=120, duration=120), q_air_mech=250., ) ach = models.AirChange( - active=models.PeriodicInterval(period=60, duration=60), + active=models.PeriodicInterval(period=120, duration=120), air_exch=0.25, ) ventilation = models.MultipleVentilation((window, hepa, ach))