diff --git a/cara/apps/calculator/__init__.py b/cara/apps/calculator/__init__.py index b91cafb2..42e6f763 100644 --- a/cara/apps/calculator/__init__.py +++ b/cara/apps/calculator/__init__.py @@ -38,12 +38,8 @@ class ConcentrationModel(RequestHandler): class StaticModel(RequestHandler): def get(self): - requested_model_config = model_generator.baseline_raw_form_data() form = model_generator.FormData.from_dict(model_generator.baseline_raw_form_data()) - model = form.build_model( - # TODO: This argument to be removed. - tmp_raw_form_data=requested_model_config, - ) + model = form.build_model() report = build_report(model, form) self.finish(report) diff --git a/cara/apps/calculator/report_generator.py b/cara/apps/calculator/report_generator.py index 2a8fa1c5..b78d5069 100644 --- a/cara/apps/calculator/report_generator.py +++ b/cara/apps/calculator/report_generator.py @@ -1,4 +1,5 @@ import base64 +import dataclasses from datetime import datetime import io from pathlib import Path @@ -13,6 +14,13 @@ from cara import models from .model_generator import FormData +@dataclasses.dataclass(frozen=True) +class RepeatEvents: + repeats: int + probability_of_infection: float + R0: float + + def calculate_report_data(model: models.ExposureModel): resolution = 600 @@ -27,6 +35,17 @@ def calculate_report_data(model: models.ExposureModel): exposed_occupants = model.exposed.number r0 = model.reproduction_rate() + repeated_events = [] + for n in [1, 2, 3, 4, 5, 10, 15, 20]: + repeat_model = dataclasses.replace(model, repeats=n) + repeated_events.append( + RepeatEvents( + repeats=n, + probability_of_infection=repeat_model.infection_probability(), + R0=repeat_model.reproduction_rate(), + ) + ) + return { "times": times, "concentrations": concentrations, @@ -36,6 +55,7 @@ def calculate_report_data(model: models.ExposureModel): "exposed_occupants": exposed_occupants, "R0": r0, "scenario_plot_src": embed_figure(plot(times, concentrations)), + "repeated_events": repeated_events, } @@ -98,5 +118,7 @@ def build_report(model: models.ExposureModel, form: FormData): undefined=jinja2.StrictUndefined, ) env.filters['minutes_to_time'] = minutes_to_time + env.filters['float_format'] = "{0:.2f}".format + env.filters['int_format'] = "{:0.0f}".format template = env.get_template("report.html.j2") - return template.render(**context) \ No newline at end of file + return template.render(**context) diff --git a/cara/apps/calculator/static/css/report.css b/cara/apps/calculator/static/css/report.css index 8239df09..500ffbb3 100644 --- a/cara/apps/calculator/static/css/report.css +++ b/cara/apps/calculator/static/css/report.css @@ -46,4 +46,4 @@ p.result_title { .discalimer { font-size: 12pt; -} \ No newline at end of file +} diff --git a/cara/apps/calculator/templates/report.html.j2 b/cara/apps/calculator/templates/report.html.j2 index 0be69661..f7b5bb11 100644 --- a/cara/apps/calculator/templates/report.html.j2 +++ b/cara/apps/calculator/templates/report.html.j2 @@ -6,7 +6,8 @@ Report | CARA (COVID Airborne Risk Assessment) - + + @@ -124,12 +125,36 @@

Results:

- In this scenario, the estimated probability of one exposed occupant getting infected P(i) is {{ "{0:.2f}".format(prob_inf) }}% and the estimated basic reproduction rate (R0) is {{ "{0:.2f}".format(R0) }}. + In this scenario, the estimated probability of one exposed occupant getting infected P(i) is {{ prob_inf | int_format }}% and the estimated basic reproduction rate (R0) is {{ R0 | float_format }}.

Exposure graph:

-






+

Repeated events:

+

+ The P(i) and R0 if repeating this scenario event - provided the infected person emits the same amount of viruses each day and the exposed person is subject to the same daily exposure time: + + + + + + + + + + + {% for repeat_event in repeated_events %} + + + + + + {% endfor %} + +
# of repeated eventsP(i)R0
{{ repeat_event.repeats }}{{ repeat_event.probability_of_infection | int_format }}%{{ repeat_event.R0 | float_format }}
+

+ +


Disclaimer:

The risk assessment tool simulates the long range airborne spread SARS-CoV-2 virus in a finite volume, assuming a homogenous mixture, and estimates the risk of COVID-19 infection thereto. The results DO NOT include short-range airborne exposure (where the physical distance plays a factor) nor the other know modes of transmission of SARS-CoV-2. Hence, this model implies that proper physical distancing, good hand hygiene and other barrier measures are ensured.

diff --git a/cara/models.py b/cara/models.py index 2fcd576d..918ca439 100644 --- a/cara/models.py +++ b/cara/models.py @@ -538,6 +538,9 @@ class ExposureModel: #: The population of non-infected people to be used in the model. exposed: Population + #: The number of times the exposure event is repeated (default 1). + repeats: int = 1 + def quanta_exposure(self) -> float: """The number of virus quanta per meter^3.""" exposure = 0.0 @@ -548,7 +551,7 @@ class ExposureModel: for start, stop in self.exposed.presence.boundaries(): exposure += integrate(self.concentration_model.concentration, start, stop) - return exposure + return exposure * self.repeats def infection_probability(self): exposure = self.quanta_exposure()