From 335ba64312da988dc1f842d4775cc37f33ee155a Mon Sep 17 00:00:00 2001 From: Phil Elson Date: Wed, 11 Nov 2020 09:50:54 +0100 Subject: [PATCH 1/2] Provide a table in the report to show the effect of repeating the event. --- cara/apps/calculator/__init__.py | 6 +--- cara/apps/calculator/report_generator.py | 21 +++++++++++++ cara/apps/calculator/static/css/report.css | 2 +- cara/apps/calculator/templates/report.html.j2 | 31 +++++++++++++++++-- cara/models.py | 5 ++- 5 files changed, 55 insertions(+), 10 deletions(-) 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..c872ccce 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,6 @@ 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 template = env.get_template("report.html.j2") return template.render(**context) \ No newline at end of file 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..f28d8fe6 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 | float_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: + + + + + + + + + + + {% for repeat_event in repeated_events %} + + + + + + {% endfor %} + +
# of repeatsP(i)R0
{{ repeat_event.repeats }}{{ repeat_event.probability_of_infection | float_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() From f763692633a9b11bbfac50debbc3f7afc881fe1f Mon Sep 17 00:00:00 2001 From: Phil Elson Date: Wed, 11 Nov 2020 12:25:44 +0100 Subject: [PATCH 2/2] Review actions for repeated events table in report. --- cara/apps/calculator/report_generator.py | 3 ++- cara/apps/calculator/templates/report.html.j2 | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/cara/apps/calculator/report_generator.py b/cara/apps/calculator/report_generator.py index c872ccce..b78d5069 100644 --- a/cara/apps/calculator/report_generator.py +++ b/cara/apps/calculator/report_generator.py @@ -119,5 +119,6 @@ def build_report(model: models.ExposureModel, form: FormData): ) 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/templates/report.html.j2 b/cara/apps/calculator/templates/report.html.j2 index f28d8fe6..f7b5bb11 100644 --- a/cara/apps/calculator/templates/report.html.j2 +++ b/cara/apps/calculator/templates/report.html.j2 @@ -125,19 +125,19 @@

Results:

- In this scenario, the estimated probability of one exposed occupant getting infected P(i) is {{ prob_inf | float_format }}% and the estimated basic reproduction rate (R0) is {{ R0 | float_format }}. + 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: + 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: - + @@ -146,7 +146,7 @@ {% for repeat_event in repeated_events %} - + {% endfor %}
# of repeats# of repeated events P(i) R0
{{ repeat_event.repeats }}{{ repeat_event.probability_of_infection | float_format }}%{{ repeat_event.probability_of_infection | int_format }}% {{ repeat_event.R0 | float_format }}