diff --git a/cara/apps/calculator/model_generator.py b/cara/apps/calculator/model_generator.py index 2d65089b..7e4a95c3 100644 --- a/cara/apps/calculator/model_generator.py +++ b/cara/apps/calculator/model_generator.py @@ -24,11 +24,14 @@ class FormData: coffee_duration: int event_type: str floor_area: float + hepa_amount: float hepa_option: bool infected_people: int lunch_option: bool + mask_type: str mask_wearing: str mechanical_ventilation_type: str + model_version: str opening_distance: float recurrent_event_month: str room_number: str @@ -58,6 +61,7 @@ class FormData: validation_tuples = [('activity_type', ACTIVITY_TYPES), ('event_type', EVENT_TYPES), ('mechanical_ventilation_type', MECHANICAL_VENTILATION_TYPES), + ('mask_type', MASK_TYPES), ('mask_wearing', MASK_WEARING), ('ventilation_type', VENTILATION_TYPES), ('volume_type', VOLUME_TYPES), @@ -89,13 +93,16 @@ class FormData: coffee_duration=int(form_data['coffee_duration']), event_type=form_data['event_type'], floor_area=float(form_data['floor_area']), + hepa_amount=float(form_data['hepa_amount']), hepa_option=(form_data['hepa_option'] == '1'), infected_people=int(form_data['infected_people']), lunch_finish=time_string_to_minutes(form_data['lunch_finish']), lunch_option=(form_data['lunch_option'] == '1'), lunch_start=time_string_to_minutes(form_data['lunch_start']), + mask_type=form_data['mask_type'], mask_wearing=form_data['mask_wearing'], mechanical_ventilation_type=form_data['mechanical_ventilation_type'], + model_version=form_data['model_version'], opening_distance=float(form_data['opening_distance']), recurrent_event_month=form_data['recurrent_event_month'], room_number=form_data['room_number'], @@ -151,7 +158,7 @@ class FormData: active=always_on, q_air_mech=self.air_supply) if self.hepa_option: - hepa = models.HEPAFilter(active=always_on, q_air_mech=250.) + hepa = models.HEPAFilter(active=always_on, q_air_mech=self.hepa_amount) return models.MultipleVentilation((ventilation,hepa)) else: return ventilation @@ -228,9 +235,9 @@ def model_from_form(form: FormData) -> models.Model: # Initializes the virus as SARS_Cov_2 virus = models.Virus.types['SARS_CoV_2'] - # Initializes a mask of type 1 if mask wearing is "continuous", otherwise instantiates the mask attribute as + # Initializes the mask type if mask wearing is "continuous", otherwise instantiates the mask attribute as # the "No mask"-mask - mask = models.Mask.types['Type I' if form.mask_wearing == "continuous" else 'No mask'] + mask = models.Mask.types[form.mask_type if form.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. @@ -279,6 +286,7 @@ def baseline_raw_form_data(): 'coffee_duration': '10', 'event_type': 'recurrent_event', 'floor_area': '', + 'hepa_amount': '250', 'hepa_option': '0', 'infected_finish': '18:00', 'infected_people': '1', @@ -286,8 +294,10 @@ def baseline_raw_form_data(): 'lunch_finish': '13:30', 'lunch_option': '1', 'lunch_start': '12:30', + 'mask_type': 'Type I', 'mask_wearing': 'removed', 'mechanical_ventilation_type': '', + 'model_version': 'BetaV1.1.0', 'opening_distance': '0.2', 'recurrent_event_month': 'January', 'room_number': '123', @@ -307,6 +317,7 @@ def baseline_raw_form_data(): ACTIVITY_TYPES = {'office', 'training', 'workshop'} EVENT_TYPES = {'single_event', 'recurrent_event'} MECHANICAL_VENTILATION_TYPES = {'air_changes', 'air_supply', 'not-applicable'} +MASK_TYPES = {'Type I', 'FFP2'} MASK_WEARING = {'continuous', 'removed'} VENTILATION_TYPES = {'natural', 'mechanical', 'no-ventilation'} VOLUME_TYPES = {'room_volume', 'room_dimensions'} diff --git a/cara/apps/calculator/report_generator.py b/cara/apps/calculator/report_generator.py index 93d07d01..ee9aa22e 100644 --- a/cara/apps/calculator/report_generator.py +++ b/cara/apps/calculator/report_generator.py @@ -87,8 +87,7 @@ def build_report(model: models.Model, form: FormData): 'model': model, 'request': request, 'form': form, - 'creation_date': time, - 'model_version': 'Beta v1.0.0', + 'creation_date': time, } context.update(calculate_report_data(model)) diff --git a/cara/apps/calculator/static/js/form.js b/cara/apps/calculator/static/js/form.js index 33106522..a15f403f 100644 --- a/cara/apps/calculator/static/js/form.js +++ b/cara/apps/calculator/static/js/form.js @@ -15,7 +15,6 @@ function on_ventilation_type_change() { }); } - function getChildElement(elem) { // Get the element named in the given element's data-enables attribute. return $("#" + elem.data("enables")); @@ -63,6 +62,18 @@ function require_fields(obj) { case "lunch_option_yes": require_lunch(true); break; + case "mask_on": + require_mask(true); + break; + case "mask_off": + require_mask(false); + break; + case "hepa_yes": + require_hepa(true); + break; + case "hepa_no": + require_hepa(false); + break; default: break; } @@ -122,9 +133,18 @@ function require_recurrent_event(option) { function require_lunch(option) { $("#lunch_start").prop('required', option); + $("#mask_ffp2").prop('required', option); +} + +function require_lunch(option) { + $("#mask_type1").prop('required', option); $("#lunch_finish").prop('required', option); } +function require_hepa(option) { + $("#hepa_amount").prop('required', option); +} + function setMaxInfectedPeople() { $("#infected_people").attr("max", $("#total_people").val()); } @@ -136,7 +156,6 @@ $(function () { }); }); - function show_disclaimer() { var dots = document.getElementById("dots"); var moreText = document.getElementById("more"); @@ -203,8 +222,7 @@ function parseValToNumber(obj) { return parseInt(obj.val().replace(':',''), 10); } -/* ------ On Load ---------- */ - +/* -------On Load------- */ $(document).ready(function () { // When the document is ready, deal with the fact that we may be here // as a result of a forward/back browser action. If that is the case, update @@ -230,7 +248,6 @@ $(document).ready(function () { if (radioValue.val()) { require_fields(radioValue.get(0)); } - }); /* -------Debugging------- */ diff --git a/cara/apps/calculator/templates/calculator.form.html.j2 b/cara/apps/calculator/templates/calculator.form.html.j2 index d255f814..24fcc42d 100644 --- a/cara/apps/calculator/templates/calculator.form.html.j2 +++ b/cara/apps/calculator/templates/calculator.form.html.j2 @@ -1,6 +1,8 @@ {% extends "layout.html.j2" %} +{% set MODEL_VERSION="BetaV1.1.0" %} {% set CALCULATOR_ACTIVE=1 %} +{% set DEBUG=False %} {% block extra_headers %} @@ -18,10 +20,16 @@
-Beta v1.0.0 Please send feedback to CARA-dev@cern.ch +{{ MODEL_VERSION }} Please send feedback to CARA-dev@cern.ch

CARA Covid Airborne Risk Assessment tool

+{% if DEBUG %} +
+{% else %} +{% endif %} + +
@@ -63,11 +71,20 @@ Beta v1.0.0 Please send feedback to
HEPA filtration: - - - - - + + + + + +
+ + Face masks:
+ Are masks worn when occupants are at workstations? + Yes + No
+ Type of masks used: + Type 1 + FFP2

@@ -79,6 +96,7 @@ Beta v1.0.0 Please send feedback to Total number of occupants:
Number of infected people:

+ Activity type:    Finish:
-
+ When is the event?
   @@ -116,6 +134,7 @@ Beta v1.0.0 Please send feedback to

+ Lunch break: @@ -147,13 +166,8 @@ Beta v1.0.0 Please send feedback to
Coffee breaks are spread evenly throughout the day. -

- Face masks:
- Are masks worn when occupants are at workstations?
- Yes - No
diff --git a/cara/apps/calculator/templates/report.html.j2 b/cara/apps/calculator/templates/report.html.j2 index aab7f3dc..4a4ccfda 100644 --- a/cara/apps/calculator/templates/report.html.j2 +++ b/cara/apps/calculator/templates/report.html.j2 @@ -13,7 +13,7 @@

Output from CARA - COVID Airborne Risk Assessment tool

-

Created {{ creation_date }} using model version {{ model_version }}


+

Created {{ creation_date }} using model version {{ form.model_version }}


Simulation Name: {{ form.simulation_name }}

Room Number: {{ form.room_number }}

@@ -55,7 +55,11 @@ No

{% endif %}
  • HEPA Filtration: {{ 'Yes' if form.hepa_option else 'No' }}

  • - + {% if form.hepa_option %} +
      +
    • HEPA amount: {{ form.hepa_amount }}

    • +
    + {% endif %}

    Event data:

    @@ -112,10 +116,10 @@

    Mask wearing:

      -
    • Masks worn at workstations? - {{ "No" if form.mask_wearing == "removed" else "Yes" }} -

    • -
    • Mask type: Type 1

    • +
    • Masks worn at workstations? {{ 'Yes' if form.mask_wearing == "continuous" else 'No' }}

    • + {% if form.mask_wearing == "continuous" %} +
    • Mask type: {{ form.mask_type }}

    • + {% endif %}

    Results:

    diff --git a/cara/models.py b/cara/models.py index 61a2182d..10ff25c8 100644 --- a/cara/models.py +++ b/cara/models.py @@ -338,6 +338,11 @@ Mask.types = { η_leaks=0.15, # (Huang 2007) η_inhale=0.3, # (Browen 2010) ), + 'FFP2': Mask( + η_exhale=0.95, # (same outward effect as type 1 - Asadi 2020) + η_leaks=0.15, # (same outward effect as type 1 - Asadi 2020) + η_inhale=0.865, # (94% penetration efficiency + 8% max inward leakage -> EN 149) + ), }