changes after rebase from master
This commit is contained in:
parent
e545d99190
commit
b92bbb6334
11 changed files with 147 additions and 101 deletions
|
|
@ -1873,50 +1873,42 @@ class ExposureModel:
|
|||
2) Short- and long-range exposure: take the infection_probability of long-range multiplied by the occupants exposed to long-range only,
|
||||
plus the infection_probability of short- and long-range multiplied by the occupants exposed to short-range only.
|
||||
|
||||
In the case dynamic occupancy is defined, the maximum number of exposed occupants during the course of the simulation will be considered.
|
||||
Currently disabled when dynamic occupancy is defined for the exposed population.
|
||||
"""
|
||||
exposed_occ: int = max(self.exposed.number.values) if isinstance(self.exposed.number, IntPiecewiseConstant) else self.exposed.number
|
||||
|
||||
if (isinstance(self.concentration_model.infected.number, IntPiecewiseConstant) or
|
||||
isinstance(self.exposed.number, IntPiecewiseConstant)):
|
||||
raise NotImplementedError("Cannot compute expected new cases "
|
||||
"with dynamic occupancy")
|
||||
|
||||
if self.short_range != ():
|
||||
new_cases_long_range = nested_replace(self, {'short_range': [],}).infection_probability() * (exposed_occ - self.exposed_to_short_range)
|
||||
new_cases_long_range = nested_replace(self, {'short_range': [],}).infection_probability() * (self.exposed.number - self.exposed_to_short_range)
|
||||
return (new_cases_long_range + (self.infection_probability() * self.exposed_to_short_range)) / 100
|
||||
|
||||
return self.infection_probability() * exposed_occ / 100
|
||||
return self.infection_probability() * self.exposed.number / 100
|
||||
|
||||
def reproduction_number(self) -> _VectorisedFloat:
|
||||
"""
|
||||
The reproduction number can be thought of as the expected number of
|
||||
cases directly generated by one infected case in a population.
|
||||
|
||||
It handles the cases when dynamic occupancy for the infected population is defined.
|
||||
Currently disabled when dynamic occupancy is defined for both the infected and exposed population.
|
||||
"""
|
||||
|
||||
infected_number = self.concentration_model.infected.number
|
||||
if isinstance(infected_number, IntPiecewiseConstant):
|
||||
# Handle case when infected number is dynamic
|
||||
max_occ = max(infected_number.values)
|
||||
if max_occ == 1:
|
||||
return self.expected_new_cases()
|
||||
else:
|
||||
# Adjust to treat dynamic occupancy, limiting infected to 1 when present
|
||||
inf_occ_values = [1 if occ > 0 else occ for occ in infected_number.values]
|
||||
single_exposure_model = nested_replace(
|
||||
self, {
|
||||
'concentration_model.infected.number.values': inf_occ_values
|
||||
}
|
||||
)
|
||||
return single_exposure_model.expected_new_cases()
|
||||
|
||||
elif isinstance(infected_number, int):
|
||||
# Handle case when infected number is a single integer
|
||||
if infected_number == 1:
|
||||
return self.expected_new_cases()
|
||||
if (isinstance(self.concentration_model.infected.number, IntPiecewiseConstant) or
|
||||
isinstance(self.exposed.number, IntPiecewiseConstant)):
|
||||
raise NotImplementedError("Cannot compute reproduction number "
|
||||
"with dynamic occupancy")
|
||||
|
||||
# Create an equivalent exposure model but with precisely
|
||||
# one infected case.
|
||||
single_exposure_model = nested_replace(
|
||||
self, {
|
||||
'concentration_model.infected.number': 1}
|
||||
)
|
||||
if self.concentration_model.infected.number == 1:
|
||||
return self.expected_new_cases()
|
||||
|
||||
return single_exposure_model.expected_new_cases()
|
||||
# Create an equivalent exposure model but with precisely
|
||||
# one infected case.
|
||||
single_exposure_model = nested_replace(
|
||||
self, {
|
||||
'concentration_model.infected.number': 1}
|
||||
)
|
||||
|
||||
return single_exposure_model.expected_new_cases()
|
||||
|
||||
|
|
@ -173,10 +173,13 @@ def calculate_report_data(form: VirusFormData, executor_factory: typing.Callable
|
|||
prob = np.array(model.infection_probability())
|
||||
prob_dist_count, prob_dist_bins = np.histogram(prob/100, bins=100, density=True)
|
||||
|
||||
# Probabilistic exposure
|
||||
if form.exposure_option == "p_probabilistic_exposure" and form.occupancy_format == "static":
|
||||
prob_probabilistic_exposure = np.array(model.total_probability_rule()).mean()
|
||||
else: prob_probabilistic_exposure = None
|
||||
# Probabilistic exposure and expected new cases (only for static occupancy)
|
||||
prob_probabilistic_exposure = None
|
||||
expected_new_cases = None
|
||||
if form.occupancy_format == "static":
|
||||
if form.exposure_option == "p_probabilistic_exposure":
|
||||
prob_probabilistic_exposure = np.array(model.total_probability_rule()).mean()
|
||||
expected_new_cases = np.array(model.expected_new_cases()).mean()
|
||||
|
||||
exposed_presence_intervals = [list(interval) for interval in model.exposed.presence_interval().boundaries()]
|
||||
|
||||
|
|
@ -207,11 +210,10 @@ def calculate_report_data(form: VirusFormData, executor_factory: typing.Callable
|
|||
"prob_hist_count": list(prob_dist_count),
|
||||
"prob_hist_bins": list(prob_dist_bins),
|
||||
"prob_probabilistic_exposure": prob_probabilistic_exposure,
|
||||
"expected_new_cases": np.array(model.expected_new_cases()).mean(),
|
||||
"expected_new_cases": expected_new_cases,
|
||||
"uncertainties_plot_src": uncertainties_plot_src,
|
||||
"CO2_concentrations": CO2_concentrations,
|
||||
"conditional_probability_data": conditional_probability_data,
|
||||
"uncertainties_plot_src": uncertainties_plot_src,
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -417,24 +419,20 @@ def manufacture_alternative_scenarios(form: VirusFormData) -> typing.Dict[str, m
|
|||
def scenario_statistics(
|
||||
mc_model: mc.ExposureModel,
|
||||
sample_times: typing.List[float],
|
||||
static_occupancy: bool,
|
||||
compute_prob_exposure: bool,
|
||||
):
|
||||
model = mc_model.build_model(
|
||||
size=mc_model.data_registry.monte_carlo['sample_size'])
|
||||
if (compute_prob_exposure):
|
||||
# It means we have data to calculate the total_probability_rule
|
||||
prob_probabilistic_exposure = model.total_probability_rule()
|
||||
else:
|
||||
prob_probabilistic_exposure = -1
|
||||
|
||||
return {
|
||||
'probability_of_infection': np.mean(model.infection_probability()),
|
||||
'expected_new_cases': np.mean(model.expected_new_cases()),
|
||||
'expected_new_cases': np.mean(model.expected_new_cases()) if static_occupancy else None,
|
||||
'concentrations': [
|
||||
np.mean(model.concentration(time))
|
||||
for time in sample_times
|
||||
],
|
||||
'prob_probabilistic_exposure': prob_probabilistic_exposure,
|
||||
'prob_probabilistic_exposure': model.total_probability_rule() if compute_prob_exposure else None,
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -455,16 +453,15 @@ def comparison_report(
|
|||
else:
|
||||
statistics = {}
|
||||
|
||||
if (form.short_range_option == "short_range_yes" and form.exposure_option == "p_probabilistic_exposure" and form.occupancy_format == "static"):
|
||||
compute_prob_exposure = True
|
||||
else:
|
||||
compute_prob_exposure = False
|
||||
static_occupancy = form.occupancy_format == "static"
|
||||
compute_prob_exposure = form.short_range_option == "short_range_yes" and form.exposure_option == "p_probabilistic_exposure" and static_occupancy
|
||||
|
||||
with executor_factory() as executor:
|
||||
results = executor.map(
|
||||
scenario_statistics,
|
||||
scenarios.values(),
|
||||
[report_data['times']] * len(scenarios),
|
||||
[static_occupancy] * len(scenarios),
|
||||
[compute_prob_exposure] * len(scenarios),
|
||||
timeout=60,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -201,11 +201,13 @@ class CO2FormData(FormData):
|
|||
# intervals and number of people are dynamic. Activity type is not needed.
|
||||
if self.occupancy_format == 'dynamic':
|
||||
if isinstance(self.dynamic_infected_occupancy, typing.List) and len(self.dynamic_infected_occupancy) > 0:
|
||||
infected_people, infected_presence = self.generate_dynamic_occupancy(self.dynamic_infected_occupancy)
|
||||
infected_people = self.generate_dynamic_occupancy(self.dynamic_infected_occupancy)
|
||||
infected_presence = None
|
||||
else:
|
||||
raise TypeError(f'If dynamic occupancy is selected, a populated list of occupancy intervals is expected. Got "{self.dynamic_infected_occupancy}".')
|
||||
if isinstance(self.dynamic_exposed_occupancy, typing.List) and len(self.dynamic_exposed_occupancy) > 0:
|
||||
exposed_people, exposed_presence = self.generate_dynamic_occupancy(self.dynamic_exposed_occupancy)
|
||||
exposed_people = self.generate_dynamic_occupancy(self.dynamic_exposed_occupancy)
|
||||
exposed_presence = None
|
||||
else:
|
||||
raise TypeError(f'If dynamic occupancy is selected, a populated list of occupancy intervals is expected. Got "{self.dynamic_exposed_occupancy}".')
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ minutes_since_midnight = typing.NewType('minutes_since_midnight', int)
|
|||
|
||||
@dataclasses.dataclass
|
||||
class FormData:
|
||||
specific_breaks: dict
|
||||
# Static occupancy inputs
|
||||
exposed_coffee_break_option: str
|
||||
exposed_coffee_duration: int
|
||||
exposed_finish: minutes_since_midnight
|
||||
|
|
@ -27,19 +27,23 @@ class FormData:
|
|||
exposed_lunch_option: bool
|
||||
exposed_lunch_start: minutes_since_midnight
|
||||
exposed_start: minutes_since_midnight
|
||||
infected_coffee_break_option: str
|
||||
infected_coffee_duration: int
|
||||
infected_coffee_break_option: str #Used if infected_dont_have_breaks_with_exposed
|
||||
infected_coffee_duration: int #Used if infected_dont_have_breaks_with_exposed
|
||||
infected_dont_have_breaks_with_exposed: bool
|
||||
infected_finish: minutes_since_midnight
|
||||
infected_lunch_finish: minutes_since_midnight # Used if infected_dont_have_breaks_with_exposed
|
||||
infected_lunch_option: bool # Used if infected_dont_have_breaks_with_exposed
|
||||
infected_lunch_start: minutes_since_midnight # Used if infected_dont_have_breaks_with_exposed
|
||||
infected_people: int
|
||||
dynamic_infected_occupancy: list
|
||||
infected_lunch_finish: minutes_since_midnight #Used if infected_dont_have_breaks_with_exposed
|
||||
infected_lunch_option: bool #Used if infected_dont_have_breaks_with_exposed
|
||||
infected_lunch_start: minutes_since_midnight #Used if infected_dont_have_breaks_with_exposed
|
||||
infected_start: minutes_since_midnight
|
||||
infected_people: int
|
||||
occupancy_format: str
|
||||
room_volume: float
|
||||
specific_breaks: dict
|
||||
total_people: int
|
||||
|
||||
# Dynamic occupancy inputs
|
||||
dynamic_exposed_occupancy: list
|
||||
dynamic_infected_occupancy: list
|
||||
|
||||
data_registry: DataRegistry
|
||||
|
||||
|
|
@ -385,24 +389,24 @@ class FormData:
|
|||
for occupancy in dynamic_occupancy:
|
||||
# Check if each occupancy entry is a dictionary
|
||||
if not isinstance(occupancy, typing.Dict):
|
||||
raise TypeError(f'Each occupancy entry should be in a dictionary format. Got "{type(occupancy)}."')
|
||||
raise TypeError(f'Each occupancy entry should be in a dictionary format. Got "{type(occupancy)}".')
|
||||
|
||||
# Check for required keys in each occupancy entry
|
||||
dict_keys = list(occupancy.keys())
|
||||
if "total_people" not in dict_keys:
|
||||
raise TypeError(f'Unable to fetch "total_people" key. Got "{dict_keys[0]}".')
|
||||
raise TypeError(f'Unable to fetch "total_people" key. Got "{dict_keys}".')
|
||||
else:
|
||||
value = occupancy["total_people"]
|
||||
# Check if the value is a non-negative integer
|
||||
if not isinstance(value, int):
|
||||
raise ValueError(f"Total number of people should be integer. Got {value}.")
|
||||
raise ValueError(f'Total number of people should be integer. Got "{type(value)}".')
|
||||
elif not value >= 0:
|
||||
raise ValueError(f"Total number of people should be non-negative. Got {value}.")
|
||||
raise ValueError(f'Total number of people should be non-negative. Got "{value}".')
|
||||
|
||||
if "start_time" not in dict_keys:
|
||||
raise TypeError(f'Unable to fetch "start_time" key. Got "{dict_keys[1]}".')
|
||||
raise TypeError(f'Unable to fetch "start_time" key. Got "{dict_keys}".')
|
||||
if "finish_time" not in dict_keys:
|
||||
raise TypeError(f'Unable to fetch "finish_time" key. Got "{dict_keys[2]}".')
|
||||
raise TypeError(f'Unable to fetch "finish_time" key. Got "{dict_keys}".')
|
||||
|
||||
# Validate time format for start_time and finish_time
|
||||
for time_key in ["start_time", "finish_time"]:
|
||||
|
|
@ -427,8 +431,7 @@ class FormData:
|
|||
transition_times=tuple(unique_transition_times_sorted),
|
||||
values=tuple(values)
|
||||
)
|
||||
population_presence: typing.Union[None, models.Interval] = None
|
||||
return population_occupancy, population_presence
|
||||
return population_occupancy
|
||||
|
||||
|
||||
def _hours2timestring(hours: float):
|
||||
|
|
|
|||
|
|
@ -470,7 +470,8 @@ class VirusFormData(FormData):
|
|||
if isinstance(self.dynamic_infected_occupancy, typing.List) and len(self.dynamic_infected_occupancy) > 0:
|
||||
# If dynamic occupancy is defined, the generator will parse and validate the
|
||||
# respective input to a format readable by the model - `IntPiecewiseConstant`.
|
||||
infected_occupancy, infected_presence = self.generate_dynamic_occupancy(self.dynamic_infected_occupancy)
|
||||
infected_occupancy = self.generate_dynamic_occupancy(self.dynamic_infected_occupancy)
|
||||
infected_presence = None
|
||||
else:
|
||||
raise TypeError(f'If dynamic occupancy is selected, a populated list of occupancy intervals is expected. Got "{self.dynamic_infected_occupancy}".')
|
||||
else:
|
||||
|
|
@ -515,7 +516,8 @@ class VirusFormData(FormData):
|
|||
if isinstance(self.dynamic_exposed_occupancy, typing.List) and len(self.dynamic_exposed_occupancy) > 0:
|
||||
# If dynamic occupancy is defined, the generator will parse and validate the
|
||||
# respective input to a format readable by the model - IntPiecewiseConstant.
|
||||
exposed_occupancy, exposed_presence = self.generate_dynamic_occupancy(self.dynamic_exposed_occupancy)
|
||||
exposed_occupancy = self.generate_dynamic_occupancy(self.dynamic_exposed_occupancy)
|
||||
exposed_presence = None
|
||||
else:
|
||||
raise TypeError(f'If dynamic occupancy is selected, a populated list of occupancy intervals is expected. Got "{self.dynamic_exposed_occupancy}".')
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import dataclasses
|
||||
import typing
|
||||
import re
|
||||
|
||||
import numpy as np
|
||||
import numpy.testing as npt
|
||||
|
|
@ -588,3 +588,33 @@ def test_form_timezone(baseline_form_data, data_registry, longitude, latitude, m
|
|||
name, offset = form.tz_name_and_utc_offset()
|
||||
assert name == expected_tz_name
|
||||
assert offset == expected_offset
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
["dynamic_occupancy_input", "error"],
|
||||
[
|
||||
[[["total_people", 10, "start_time", "10:00", "finish_time", "11:00"]], "Each occupancy entry should be in a dictionary format. Got \"<class 'list'>\"."],
|
||||
[[{"tal_people": 10, "start_time": "10:00", "finish_time": "11:00"}], "Unable to fetch \"total_people\" key. Got \"['tal_people', 'start_time', 'finish_time']\"."],
|
||||
[[{"total_people": 10, "art_time": "10:00", "finish_time": "11:00"}], "Unable to fetch \"start_time\" key. Got \"['total_people', 'art_time', 'finish_time']\"."],
|
||||
[[{"total_people": 10, "start_time": "10:00", "ish_time": "11:00"}], "Unable to fetch \"finish_time\" key. Got \"['total_people', 'start_time', 'ish_time']\"."],
|
||||
[[{"total_people": 10, "start_time": "10", "finish_time": "11:00"}], "Wrong time format - \"HH:MM\". Got \"10\"."],
|
||||
[[{"total_people": 10, "start_time": "10:00", "finish_time": "11"}], "Wrong time format - \"HH:MM\". Got \"11\"."],
|
||||
]
|
||||
)
|
||||
def test_dynamic_occupancy_structure(dynamic_occupancy_input, error, baseline_form: virus_validator.VirusFormData):
|
||||
with pytest.raises(TypeError, match=re.escape(error)):
|
||||
baseline_form.generate_dynamic_occupancy(dynamic_occupancy_input)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
["dynamic_occupancy_input", "error"],
|
||||
[
|
||||
[[{"total_people": "10", "start_time": "10:00", "finish_time": "11:00"}], "Total number of people should be integer. Got \"<class 'str'>\"."],
|
||||
[[{"total_people": 9.8, "start_time": "10:00", "finish_time": "11:00"}], "Total number of people should be integer. Got \"<class 'float'>\"."],
|
||||
[[{"total_people": [10], "start_time": "10:00", "finish_time": "11:00"}], "Total number of people should be integer. Got \"<class 'list'>\"."],
|
||||
[[{"total_people": -1, "start_time": "10:00", "finish_time": "11:00"}], "Total number of people should be non-negative. Got \"-1\"."],
|
||||
]
|
||||
)
|
||||
def test_dynamic_occupancy_total_people(dynamic_occupancy_input, error, baseline_form: virus_validator.VirusFormData):
|
||||
with pytest.raises(ValueError, match=re.escape(error)):
|
||||
baseline_form.generate_dynamic_occupancy(dynamic_occupancy_input)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
from typing import Type
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from caimira.calculator.validators.virus import virus_validator
|
||||
|
|
|
|||
|
|
@ -232,24 +232,32 @@ def test_dynamic_total_probability_rule(
|
|||
|
||||
|
||||
def test_dynamic_expected_new_cases(
|
||||
full_exposure_model: models.ExposureModel,
|
||||
dynamic_infected_single_exposure_model: models.ExposureModel,
|
||||
dynamic_exposed_single_exposure_model: models.ExposureModel,
|
||||
dynamic_population_exposure_model: models.ExposureModel):
|
||||
|
||||
base_expected_new_cases = full_exposure_model.expected_new_cases()
|
||||
npt.assert_almost_equal(base_expected_new_cases, dynamic_infected_single_exposure_model.expected_new_cases())
|
||||
npt.assert_almost_equal(base_expected_new_cases, dynamic_exposed_single_exposure_model.expected_new_cases())
|
||||
npt.assert_almost_equal(base_expected_new_cases, dynamic_population_exposure_model.expected_new_cases())
|
||||
with pytest.raises(NotImplementedError, match=re.escape("Cannot compute expected new cases "
|
||||
"with dynamic occupancy")):
|
||||
dynamic_infected_single_exposure_model.expected_new_cases()
|
||||
with pytest.raises(NotImplementedError, match=re.escape("Cannot compute expected new cases "
|
||||
"with dynamic occupancy")):
|
||||
dynamic_exposed_single_exposure_model.expected_new_cases()
|
||||
with pytest.raises(NotImplementedError, match=re.escape("Cannot compute expected new cases "
|
||||
"with dynamic occupancy")):
|
||||
dynamic_population_exposure_model.expected_new_cases()
|
||||
|
||||
|
||||
def test_dynamic_reproduction_number(
|
||||
full_exposure_model: models.ExposureModel,
|
||||
dynamic_infected_single_exposure_model: models.ExposureModel,
|
||||
dynamic_exposed_single_exposure_model: models.ExposureModel,
|
||||
dynamic_population_exposure_model: models.ExposureModel):
|
||||
|
||||
base_reproduction_number = full_exposure_model.reproduction_number()
|
||||
npt.assert_almost_equal(base_reproduction_number, dynamic_infected_single_exposure_model.reproduction_number())
|
||||
npt.assert_almost_equal(base_reproduction_number, dynamic_exposed_single_exposure_model.reproduction_number())
|
||||
npt.assert_almost_equal(base_reproduction_number, dynamic_population_exposure_model.reproduction_number())
|
||||
with pytest.raises(NotImplementedError, match=re.escape("Cannot compute reproduction number "
|
||||
"with dynamic occupancy")):
|
||||
dynamic_infected_single_exposure_model.reproduction_number()
|
||||
with pytest.raises(NotImplementedError, match=re.escape("Cannot compute reproduction number "
|
||||
"with dynamic occupancy")):
|
||||
dynamic_exposed_single_exposure_model.reproduction_number()
|
||||
with pytest.raises(NotImplementedError, match=re.escape("Cannot compute reproduction number "
|
||||
"with dynamic occupancy")):
|
||||
dynamic_population_exposure_model.reproduction_number()
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@
|
|||
</div>
|
||||
{% endblock long_range_warning_animation %}
|
||||
</div>
|
||||
<h6><b>Expected new cases:</b> {{ long_range_expected_cases | float_format }}</h6>
|
||||
{% if form.occupancy_format == "static" %}<h6><b>Expected new cases:</b> {{ long_range_expected_cases | float_format }}</h6>{% endif %}
|
||||
</div>
|
||||
<br>
|
||||
{% if form.short_range_option == "short_range_yes" %}
|
||||
|
|
@ -126,19 +126,27 @@
|
|||
</div>
|
||||
{% endblock warning_animation %}
|
||||
</div>
|
||||
<h6><b>Expected new cases:</b> {{ expected_new_cases | float_format }}</h6>
|
||||
{% if form.occupancy_format == "static" %}
|
||||
<h6><b>Expected new cases:</b> {{ expected_new_cases | float_format }}</h6>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="d-flex">
|
||||
{% block report_summary %}
|
||||
<div class="flex-row align-self-center">
|
||||
<div class="align-self-center alert alert-dark mb-0" role="alert">
|
||||
Taking into account the uncertainties tied to the model variables, in this scenario and assuming all occupants are exposed equally (i.e. without short-range interactions), the <b>probability of one exposed occupant getting infected is {{ long_range_prob_inf | non_zero_percentage }}</b> and the <b>expected number of new cases is {{ long_range_expected_cases | float_format }}</b>*.
|
||||
Taking into account the uncertainties tied to the model variables, in this scenario and assuming all occupants are exposed equally (i.e. without short-range interactions), the <b>probability of one exposed occupant getting infected is {{ long_range_prob_inf | non_zero_percentage }}</b>
|
||||
{% if form.occupancy_format == "static" %}
|
||||
and the <b>expected number of new cases is {{ long_range_expected_cases | float_format }}</b>
|
||||
{% endif %}*.
|
||||
</div>
|
||||
{% if form.short_range_option == "short_range_yes" %}
|
||||
<br>
|
||||
<div class="align-self-center alert alert-dark mb-0" role="alert">
|
||||
In this scenario, the <b>probability the occupant(s) exposed to short-range interactions get infected can go as high as {{ prob_inf | non_zero_percentage }}</b> and the <b>expected number of new cases increases to {{ expected_new_cases | float_format }}</b>.
|
||||
In this scenario, the <b>probability the occupant(s) exposed to short-range interactions get infected can go as high as {{ prob_inf | non_zero_percentage }}</b>
|
||||
{% if form.occupancy_format == "static" %}
|
||||
and the <b>expected number of new cases increases to {{ expected_new_cases | float_format }}</b>
|
||||
{% endif %}.
|
||||
</div>
|
||||
{% endif %}
|
||||
{% block probabilistic_exposure_probability %}
|
||||
|
|
@ -302,7 +310,7 @@
|
|||
<tr>
|
||||
<th>Scenario</th>
|
||||
<th>P(I)</th>
|
||||
<th>Expected new cases</th>
|
||||
{% if form.occupancy_format == "static" %}<th>Expected new cases</th>{% endif %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
|
@ -310,7 +318,7 @@
|
|||
<tr>
|
||||
<td> {{ scenario_name }}</td>
|
||||
<td> {{ scenario_stats.probability_of_infection | non_zero_percentage }}</td>
|
||||
<td style="text-align:right">{{ scenario_stats.expected_new_cases | float_format }}</td>
|
||||
{% if form.occupancy_format == "static" %}<td style="text-align:right">{{ scenario_stats.expected_new_cases | float_format }}</td>{% endif %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
|
|
@ -630,11 +638,11 @@
|
|||
<ul>
|
||||
{% for interaction in form.short_range_interactions %}
|
||||
<li>Interaction no. {{ loop.index }}:
|
||||
<ul>
|
||||
<li>Expiratory activity: {{ "Shouting/Singing" if interaction.expiration == "Shouting" else interaction.expiration }} </li>
|
||||
<li>Start time: {{ interaction.start_time }} </li>
|
||||
<li>Duration: {{ interaction.duration }} {{ "minutes" if interaction.duration|float > 1 else "minute" }}</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li>Expiratory activity: {{ "Shouting/Singing" if interaction.expiration == "Shouting" else interaction.expiration }} </li>
|
||||
<li>Start time: {{ interaction.start_time }} </li>
|
||||
<li>Duration: {{ interaction.duration }} {{ "minutes" if interaction.duration|float > 1 else "minute" }}</li>
|
||||
</ul>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
{% set long_range_prob_inf = prob_inf %}
|
||||
{% endif %}
|
||||
|
||||
{% if ((long_range_prob_inf > red_prob_lim) or (expected_new_cases >= 1)) %}
|
||||
{% if ((long_range_prob_inf > red_prob_lim) or (form.occupancy_format == "static" and expected_new_cases >= 1)) %}
|
||||
{% set long_range_scale_warning = 'red' %}
|
||||
{% set long_range_warning_color= 'bg-danger' %}
|
||||
{% elif (orange_prob_lim <= long_range_prob_inf <= red_prob_lim) %}
|
||||
|
|
@ -22,7 +22,7 @@
|
|||
{% set long_range_warning_color = 'bg-success' %}
|
||||
{% endif %}
|
||||
|
||||
{% if ((prob_inf > red_prob_lim) or (expected_new_cases >= 1)) %} {% set scale_warning = 'red' %}
|
||||
{% if ((prob_inf > red_prob_lim) or (form.occupancy_format == "static" and expected_new_cases >= 1)) %} {% set scale_warning = 'red' %}
|
||||
{% elif (orange_prob_lim <= prob_inf <= red_prob_lim) %} {% set scale_warning = 'orange' %}
|
||||
{% else %} {% set scale_warning = 'green' %}
|
||||
{% endif %}
|
||||
|
|
@ -70,7 +70,10 @@
|
|||
<div class="alert alert-success mb-0" role="alert">
|
||||
<strong>Acceptable:</strong>
|
||||
{% endif %}
|
||||
Taking into account the uncertainties tied to the model variables, in this scenario and assuming all occupants are exposed equally (i.e. without short-range interactions), the <b>probability of one exposed occupant getting infected is {{long_range_prob_inf | non_zero_percentage}}</b> and the <b>expected number of new cases is {{ long_range_expected_cases | float_format }}</b>*.
|
||||
Taking into account the uncertainties tied to the model variables, in this scenario and assuming all occupants are exposed equally (i.e. without short-range interactions), the <b>probability of one exposed occupant getting infected is {{long_range_prob_inf | non_zero_percentage}}</b>
|
||||
{% if form.occupancy_format == "static" %}
|
||||
and the <b>expected number of new cases is {{ long_range_expected_cases | float_format }}</b>
|
||||
{% endif %}*.
|
||||
</div>
|
||||
{% if form.short_range_option == "short_range_yes" %}
|
||||
<br>
|
||||
|
|
@ -84,7 +87,10 @@
|
|||
<div class="alert alert-success mb-0" role="alert">
|
||||
<strong>Acceptable:</strong>
|
||||
{% endif %}
|
||||
In this scenario, the <b>probability the occupant(s) exposed to short-range interactions get infected can go as high as {{ prob_inf | non_zero_percentage }}</b> and the <b>expected number of new cases increases to {{ expected_new_cases | float_format }}</b>.
|
||||
In this scenario, the <b>probability the occupant(s) exposed to short-range interactions get infected can go as high as {{ prob_inf | non_zero_percentage }}</b>
|
||||
{% if form.occupancy_format == "static" %}
|
||||
and the <b>expected number of new cases increases to {{ expected_new_cases | float_format }}</b>
|
||||
{% endif %}.
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
|
|
@ -137,12 +143,12 @@
|
|||
<tr>
|
||||
<th>Scenario</th>
|
||||
<th>P(i)</th>
|
||||
<th>Expected new cases</th>
|
||||
{% if form.occupancy_format == "static" %}<th>Expected new cases</th>{% endif %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for scenario_name, scenario_stats in alternative_scenarios.stats.items() %}
|
||||
{%if (( scenario_stats.probability_of_infection > red_prob_lim) or (scenario_stats.expected_new_cases >= 1)) %}
|
||||
{%if (( scenario_stats.probability_of_infection > red_prob_lim) or (form.occupancy_format == "static" and scenario_stats.expected_new_cases >= 1)) %}
|
||||
<tr class="alert-danger">
|
||||
{% elif (orange_prob_lim <= scenario_stats.probability_of_infection <= red_prob_lim) %}
|
||||
<tr class="alert-warning">
|
||||
|
|
@ -151,7 +157,7 @@
|
|||
{% endif%}
|
||||
<td> {{ scenario_name }}</td>
|
||||
<td> {{ scenario_stats.probability_of_infection | non_zero_percentage }}</td>
|
||||
<td style="text-align:right">{{ scenario_stats.expected_new_cases | float_format }}</td>
|
||||
{% if form.occupancy_format == "static" %}<td style="text-align:right">{{ scenario_stats.expected_new_cases | float_format }}</td>{% endif %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
|
|
|
|||
|
|
@ -180,6 +180,6 @@ def test_static_vs_dynamic_occupancy_from_form(baseline_form_data, data_registry
|
|||
list(dynamic_occupancy_model.exposed.number.transition_times))
|
||||
|
||||
np.testing.assert_almost_equal(static_occupancy_report_data['prob_inf'], dynamic_occupancy_report_data['prob_inf'], 1)
|
||||
np.testing.assert_almost_equal(static_occupancy_report_data['expected_new_cases'], dynamic_occupancy_report_data['expected_new_cases'], 1)
|
||||
assert dynamic_occupancy_report_data['expected_new_cases'] == None
|
||||
assert dynamic_occupancy_report_data['prob_probabilistic_exposure'] == None
|
||||
|
||||
Loading…
Reference in a new issue