Merge branch 'feature/repeated-events' into 'master'

Provide a table in the report to show the effect of repeating the event

See merge request cara/cara!78
This commit is contained in:
Philip James Elson 2020-11-11 11:27:00 +00:00
commit 6e3b5587a0
5 changed files with 57 additions and 11 deletions

View file

@ -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)

View file

@ -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)
return template.render(**context)

View file

@ -46,4 +46,4 @@ p.result_title {
.discalimer {
font-size: 12pt;
}
}

View file

@ -6,7 +6,8 @@
<title>Report | CARA (COVID Airborne Risk Assessment)</title>
<link rel="stylesheet" type="text/css" href="/calculator/static/css/report.css">
<link rel="stylesheet" type="text/css" href="/calculator/static/css/report.css">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
</head>
<body id="body">
@ -124,12 +125,36 @@
<p class="result_title">Results:</p>
<p class="data_text">
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 }}.
<p>
<p class="data_title">Exposure graph:</p>
<img id="scenario_concentration_plot" src="{{ scenario_plot_src }}">
<br><br><br><br><br><br><br>
<p class="data_title">Repeated events:</p>
<p class="data_text">
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:
<table class="table table-striped w-auto">
<thead class="thead-light">
<tr>
<th># of repeated events</th>
<th>P(i)</th>
<th>R0</th>
</tr>
</thead>
<tbody>
{% for repeat_event in repeated_events %}
<tr>
<td>{{ repeat_event.repeats }}</td>
<td>{{ repeat_event.probability_of_infection | int_format }}%</td>
<td>{{ repeat_event.R0 | float_format }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<p>
<br><br><br>
<div style="border: 2px solid black; padding: 15px;">
<p class="image"> <img <img align="middle" src="/calculator/static/images/disclaimer.jpg" width="40" height="40"><b>Disclaimer:</b><br><br></p>
<p class="discalimer">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.<br><br>

View file

@ -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()