From d5d572c94326ea37054e297a529f5a56305bd6f5 Mon Sep 17 00:00:00 2001 From: markus Date: Fri, 6 Nov 2020 12:36:53 +0100 Subject: [PATCH 01/12] add test_key_validation --- cara/tests/apps/calculator/test_model_generator.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cara/tests/apps/calculator/test_model_generator.py b/cara/tests/apps/calculator/test_model_generator.py index 06a0dde2..3e2c9157 100644 --- a/cara/tests/apps/calculator/test_model_generator.py +++ b/cara/tests/apps/calculator/test_model_generator.py @@ -50,3 +50,9 @@ def test_present_intervals(baseline_form): baseline_form.lunch_finish = 13 * 60 + 30 correct = ((9, 10), (10.25, 12), (12.25, 12.5), (13.5, 14), (14.25, 16), (16.25, 17)) assert baseline_form.present_interval().present_times == correct + + +def test_key_validation(baseline_form_data): + baseline_form_data['activity_type'] = 'invalid key' + with pytest.raises(ValueError): + model_generator.FormData.from_dict(baseline_form_data) \ No newline at end of file From 11feee4050b20459736375a7aa022051bf9b457e Mon Sep 17 00:00:00 2001 From: markus Date: Fri, 6 Nov 2020 12:57:21 +0100 Subject: [PATCH 02/12] create sets of valid values --- cara/apps/calculator/model_generator.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/cara/apps/calculator/model_generator.py b/cara/apps/calculator/model_generator.py index a945bd72..19ef97c2 100644 --- a/cara/apps/calculator/model_generator.py +++ b/cara/apps/calculator/model_generator.py @@ -254,6 +254,15 @@ def baseline_raw_form_data(): } +ACTIVITY_TYPES = {'office', 'training', 'workshop'} +EVENT_TYPES = {'single_event', 'recurrent_event'} +MECHANICAL_VENTILATION_TYPES = {'air_changes', 'air_supply'} +MASK_WEARING = {'continuous', 'removed'} +VENTILATION_TYPES = {'natural', 'mechanical'} +VOLUME_TYPES = {'room_volume', 'room_dimensions'} +WINDOWS_OPEN = {'always', 'interval', 'breaks'} + + def time_string_to_minutes(time: str) -> int: """ Converts time from string-format to an integer number of minutes after 00:00 From da95cedb4d7450dd00eac9381b2194281984755b Mon Sep 17 00:00:00 2001 From: markus Date: Fri, 6 Nov 2020 13:50:15 +0100 Subject: [PATCH 03/12] change air_type to mechanical_ventilation_type --- cara/apps/calculator/static/form.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cara/apps/calculator/static/form.html b/cara/apps/calculator/static/form.html index bd4c9df8..4d7b9917 100644 --- a/cara/apps/calculator/static/form.html +++ b/cara/apps/calculator/static/form.html @@ -41,9 +41,9 @@ Beta v1.0.0 Please send feedback to From 7dbb2cc632e9838f10a01870d693750ca95eae65 Mon Sep 17 00:00:00 2001 From: markus Date: Fri, 6 Nov 2020 13:50:37 +0100 Subject: [PATCH 04/12] comment out breaks-option because of lacking model capability --- cara/apps/calculator/static/form.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cara/apps/calculator/static/form.html b/cara/apps/calculator/static/form.html index 4d7b9917..409b60f1 100644 --- a/cara/apps/calculator/static/form.html +++ b/cara/apps/calculator/static/form.html @@ -56,8 +56,8 @@ Beta v1.0.0 Please send feedback to - -
+
HEPA filtration: From c4d474304a3fea2ee7879121d1ad50ab7a3e5078 Mon Sep 17 00:00:00 2001 From: markus Date: Fri, 6 Nov 2020 13:51:03 +0100 Subject: [PATCH 05/12] add minutes_to_string --- cara/apps/calculator/model_generator.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/cara/apps/calculator/model_generator.py b/cara/apps/calculator/model_generator.py index 19ef97c2..4ba654ba 100644 --- a/cara/apps/calculator/model_generator.py +++ b/cara/apps/calculator/model_generator.py @@ -159,6 +159,14 @@ class FormData: return models.SpecificInterval(tuple(present_intervals)) + def minutes_to_string(self, minutes: int) -> str: + minute_string = str(minutes % 60) + minute_string = "0" * (2 - len(minute_string)) + minute_string + hour_string = str(minutes // 60) + hour_string = "0" * (2 - len(hour_string)) + hour_string + + return f"{hour_string}:{minute_string}" + def model_from_form(form: FormData, tmp_raw_form_data) -> models.Model: d = tmp_raw_form_data From 0b6acecd47621b762a6b3bc0b638476745a33e25 Mon Sep 17 00:00:00 2001 From: markus Date: Fri, 6 Nov 2020 13:51:34 +0100 Subject: [PATCH 06/12] use correct keys --- cara/apps/calculator/templates/report.html.j2 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cara/apps/calculator/templates/report.html.j2 b/cara/apps/calculator/templates/report.html.j2 index b716814f..205bbdd9 100644 --- a/cara/apps/calculator/templates/report.html.j2 +++ b/cara/apps/calculator/templates/report.html.j2 @@ -24,7 +24,7 @@

Ventilation data:

  • Mechanical ventilation: - {% if ventilation_type == "mechanical_ventilation"%} + {% if ventilation_type == "mechanical"%} Yes

    • Air supply flow rate: {{ air_supply }}

    • @@ -34,7 +34,7 @@ No {% endif %}
    • Natural ventilation: - {% if ventilation_type == "natural_ventilation"%} + {% if ventilation_type == "natural"%} Yes

      • Number of windows: {{ windows_number }}

      • @@ -61,7 +61,7 @@ {% endif %} infected.

      • Activity type: - {% if activity_type == "office work" %} + {% if activity_type == "office" %} Office work – typical scenario with all persons seated, talking. {% elif activity_type == "workshop" %} Workshop = assembly workshop environment, all persons doing light exercise, talking. From 93207ff8e83636b61ee9931ecd0f3e1b40e50d02 Mon Sep 17 00:00:00 2001 From: markus Date: Fri, 6 Nov 2020 13:52:04 +0100 Subject: [PATCH 07/12] use minutes_to_string in report --- cara/apps/calculator/report_generator.py | 7 ++++--- cara/apps/calculator/templates/report.html.j2 | 6 +++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/cara/apps/calculator/report_generator.py b/cara/apps/calculator/report_generator.py index a4fc0c36..1bc46b60 100644 --- a/cara/apps/calculator/report_generator.py +++ b/cara/apps/calculator/report_generator.py @@ -76,7 +76,8 @@ def build_report(model: models.Model, form: FormData): context = { 'model': model, - 'request': request, + 'request': request, + 'form': form, 'creation_date': time, 'model_version': 'Beta v1.0.0', 'simulation_name': form.simulation_name, @@ -96,8 +97,8 @@ def build_report(model: models.Model, form: FormData): 'activity_type': form.activity_type, 'activity_start': form.activity_start, 'activity_finish': form.activity_finish, - 'exposure_start': '00:00', - 'exposure_finish': '01:15', + 'infected_start': 826, + 'infected_finish': 827, 'event_type': form.event_type, 'single_event_date': form.single_event_date, 'recurrent_event_month': form.recurrent_event_month, diff --git a/cara/apps/calculator/templates/report.html.j2 b/cara/apps/calculator/templates/report.html.j2 index 205bbdd9..f44578d3 100644 --- a/cara/apps/calculator/templates/report.html.j2 +++ b/cara/apps/calculator/templates/report.html.j2 @@ -70,11 +70,11 @@ {% endif %}

        • -
        • Start time: {{ activity_start }}    End time: {{ activity_finish }}

        • +
        • Start time: {{ form.minutes_to_string(activity_start) }}    End time: {{ form.minutes_to_string(activity_finish) }}

      • Exposure time (presence of infected person):

        • -
        • Start time: {{ exposure_start }}    End time: {{ exposure_finish }}

        • +
        • Start time: {{ form.minutes_to_string(infected_start) }}    End time: {{ form.minutes_to_string(infected_finish) }}

        {% if event_type == "single_event"%}
      • Single event on {{ single_event_date }}

      • @@ -91,7 +91,7 @@ {% if lunch_option%} Yes
          -
        • Start: {{ lunch_start }}    End: {{ lunch_finish }}

        • +
        • Start: {{ form.minutes_to_string(lunch_start) }}    End: {{ form.minutes_to_string(lunch_finish) }}

        {% else%} No From 76d74132ef8882e14f528758123f6a079ad77784 Mon Sep 17 00:00:00 2001 From: markus Date: Fri, 6 Nov 2020 14:06:15 +0100 Subject: [PATCH 08/12] use correct windows_open value-name --- cara/tests/apps/calculator/test_model_generator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cara/tests/apps/calculator/test_model_generator.py b/cara/tests/apps/calculator/test_model_generator.py index 3e2c9157..3e4e5126 100644 --- a/cara/tests/apps/calculator/test_model_generator.py +++ b/cara/tests/apps/calculator/test_model_generator.py @@ -29,7 +29,7 @@ def test_ventilation(baseline_form): cd_b=0.6, window_height=1.6, opening_length=0.6, ) baseline_form.ventilation_type = 'natural' - baseline_form.windows_open = '10 min / 2h' + baseline_form.windows_open = 'interval' baseline_form.event_type = 'recurrent_event' baseline_form.recurrent_event_month = 'December' baseline_form.window_height = 1.6 From a88ff3cf1f81a7a0fc774911c76d860ba906a2c0 Mon Sep 17 00:00:00 2001 From: markus Date: Fri, 6 Nov 2020 14:17:34 +0100 Subject: [PATCH 09/12] resolve todo --- cara/apps/calculator/model_generator.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cara/apps/calculator/model_generator.py b/cara/apps/calculator/model_generator.py index 4ba654ba..fddae399 100644 --- a/cara/apps/calculator/model_generator.py +++ b/cara/apps/calculator/model_generator.py @@ -189,8 +189,7 @@ def model_from_form(form: FormData, tmp_raw_form_data) -> models.Model: # Initializes a mask of type 1 if mask wearing is "continuous", otherwise instantiates the mask attribute as # the "No mask"-mask - # TODO: figure out the possible values of mask_wearing in the form - mask = models.Mask.types['Type I' if d['mask_wearing'] == "Continuous" else 'No mask'] + mask = models.Mask.types['Type I' if d['mask_wearing'] == "continuous" else 'No mask'] # A dictionary containing the mapping of activities listed in the UI to the activity level and expiration level # of the infected and exposed occupants respectively. From 5e11292a6025f98dccc385faee0e353beecd1f45 Mon Sep 17 00:00:00 2001 From: markus Date: Fri, 6 Nov 2020 14:18:00 +0100 Subject: [PATCH 10/12] validate keys --- cara/apps/calculator/model_generator.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/cara/apps/calculator/model_generator.py b/cara/apps/calculator/model_generator.py index fddae399..6901bc62 100644 --- a/cara/apps/calculator/model_generator.py +++ b/cara/apps/calculator/model_generator.py @@ -47,6 +47,17 @@ class FormData: if value == "": form_data[key] = "0" + validation_tuples = [('activity_type', ACTIVITY_TYPES), + ('event_type', EVENT_TYPES), + ('mechanical_ventilation_type', MECHANICAL_VENTILATION_TYPES), + ('mask_wearing', MASK_WEARING), + ('ventilation_type', VENTILATION_TYPES), + ('volume_type', VOLUME_TYPES), + ('windows_open', WINDOWS_OPEN)] + for key, valid_set in validation_tuples: + if form_data[key] not in valid_set: + raise ValueError(f"{form_data[key]} is not a valid value for {key}") + return cls( activity_finish=time_string_to_minutes(form_data['activity_finish']), activity_start=time_string_to_minutes(form_data['activity_start']), @@ -87,11 +98,14 @@ class FormData: def ventilation(self) -> models.Ventilation: # Initializes a ventilation instance as a window if 'natural' is selected, or as a HEPA-filter otherwise if self.ventilation_type == 'natural': - if self.windows_open == '10 min / 2h': + if self.windows_open == 'interval': period, duration = 120, 10 + elif self.windows_number == 'breaks': + # TODO: Implement windows open in breaks + period, duration = 120, 120 else: period, duration = 120, 120 - # I multiply the opening width by the number of windows to simulate the correct window area + if self.event_type == 'single_event': month_number = int(self.single_event_date.split('/')[1]) month = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'][month_number - 1] @@ -101,6 +115,7 @@ class FormData: inside_temp = models.PiecewiseConstant((0, 24), (293,)) outside_temp = models.GenevaTemperatures[month] + # I multiply the opening width by the number of windows to simulate the correct window area ventilation = models.WindowOpening(active=models.PeriodicInterval(period=period, duration=duration), inside_temp=inside_temp, outside_temp=outside_temp, cd_b=0.6, window_height=self.window_height, From d7c64c875cab0fa54ed5e86a4cbe95e8b9a05745 Mon Sep 17 00:00:00 2001 From: markus Date: Fri, 6 Nov 2020 14:42:15 +0100 Subject: [PATCH 11/12] move minutes_to_string into report_generator.py --- cara/apps/calculator/model_generator.py | 8 -------- cara/apps/calculator/report_generator.py | 9 +++++++++ 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/cara/apps/calculator/model_generator.py b/cara/apps/calculator/model_generator.py index 6901bc62..d71aae50 100644 --- a/cara/apps/calculator/model_generator.py +++ b/cara/apps/calculator/model_generator.py @@ -174,14 +174,6 @@ class FormData: return models.SpecificInterval(tuple(present_intervals)) - def minutes_to_string(self, minutes: int) -> str: - minute_string = str(minutes % 60) - minute_string = "0" * (2 - len(minute_string)) + minute_string - hour_string = str(minutes // 60) - hour_string = "0" * (2 - len(hour_string)) + hour_string - - return f"{hour_string}:{minute_string}" - def model_from_form(form: FormData, tmp_raw_form_data) -> models.Model: d = tmp_raw_form_data diff --git a/cara/apps/calculator/report_generator.py b/cara/apps/calculator/report_generator.py index 1bc46b60..1147897c 100644 --- a/cara/apps/calculator/report_generator.py +++ b/cara/apps/calculator/report_generator.py @@ -69,6 +69,15 @@ def plot(times, concentrations): return fig +def minutes_to_string(minutes: int) -> str: + minute_string = str(minutes % 60) + minute_string = "0" * (2 - len(minute_string)) + minute_string + hour_string = str(minutes // 60) + hour_string = "0" * (2 - len(hour_string)) + hour_string + + return f"{hour_string}:{minute_string}" + + def build_report(model: models.Model, form: FormData): now = datetime.now() time = now.strftime("%d/%m/%Y %H:%M:%S") From a6a91471ba628f436a9dea486ed2616eed7d156f Mon Sep 17 00:00:00 2001 From: markus Date: Fri, 6 Nov 2020 14:51:14 +0100 Subject: [PATCH 12/12] pre-process minutes --- cara/apps/calculator/report_generator.py | 12 ++++++------ cara/apps/calculator/templates/report.html.j2 | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/cara/apps/calculator/report_generator.py b/cara/apps/calculator/report_generator.py index 1147897c..22f84977 100644 --- a/cara/apps/calculator/report_generator.py +++ b/cara/apps/calculator/report_generator.py @@ -104,16 +104,16 @@ def build_report(model: models.Model, form: FormData): 'total_people': form.total_people, 'infected_people': form.infected_people, 'activity_type': form.activity_type, - 'activity_start': form.activity_start, - 'activity_finish': form.activity_finish, - 'infected_start': 826, - 'infected_finish': 827, + 'activity_start': minutes_to_string(form.activity_start), + 'activity_finish': minutes_to_string(form.activity_finish), + 'infected_start': minutes_to_string(826), + 'infected_finish': minutes_to_string(827), 'event_type': form.event_type, 'single_event_date': form.single_event_date, 'recurrent_event_month': form.recurrent_event_month, 'lunch_option': form.lunch_option, - 'lunch_start': form.lunch_start, - 'lunch_finish': form.lunch_finish, + 'lunch_start': minutes_to_string(form.lunch_start), + 'lunch_finish': minutes_to_string(form.lunch_finish), 'coffee_breaks': form.coffee_breaks, 'coffee_duration': form.coffee_duration, 'coffee_times': [['00:00','00:00'], ['00:00','00:00'], ['00:00','00:00'], ['00:00','00:00']], diff --git a/cara/apps/calculator/templates/report.html.j2 b/cara/apps/calculator/templates/report.html.j2 index f44578d3..8c29bb1b 100644 --- a/cara/apps/calculator/templates/report.html.j2 +++ b/cara/apps/calculator/templates/report.html.j2 @@ -70,11 +70,11 @@ {% endif %}

          -
        • Start time: {{ form.minutes_to_string(activity_start) }}    End time: {{ form.minutes_to_string(activity_finish) }}

        • +
        • Start time: {{ activity_start }}    End time: {{ activity_finish }}

      • Exposure time (presence of infected person):

        • -
        • Start time: {{ form.minutes_to_string(infected_start) }}    End time: {{ form.minutes_to_string(infected_finish) }}

        • +
        • Start time: {{ infected_start }}    End time: {{ infected_finish }}

        {% if event_type == "single_event"%}
      • Single event on {{ single_event_date }}

      • @@ -91,7 +91,7 @@ {% if lunch_option%} Yes
          -
        • Start: {{ form.minutes_to_string(lunch_start) }}    End: {{ form.minutes_to_string(lunch_finish) }}

        • +
        • Start: {{ lunch_start }}    End: {{ lunch_finish }}

        {% else%} No