added UI input fields and updated report for the new P(I)
This commit is contained in:
parent
5b578c15ef
commit
8936b0db48
6 changed files with 154 additions and 8 deletions
|
|
@ -131,6 +131,7 @@ def calculate_report_data(form: FormData, model: models.ExposureModel) -> typing
|
|||
])
|
||||
|
||||
prob = np.array(model.infection_probability()).mean()
|
||||
prob_specific_event = np.array(model.total_probability_rule()).mean()
|
||||
er = np.array(model.concentration_model.infected.emission_rate_when_present()).mean()
|
||||
exposed_occupants = model.exposed.number
|
||||
expected_new_cases = np.array(model.expected_new_cases()).mean()
|
||||
|
|
@ -147,6 +148,7 @@ def calculate_report_data(form: FormData, model: models.ExposureModel) -> typing
|
|||
"cumulative_doses": list(cumulative_doses),
|
||||
"long_range_cumulative_doses": list(long_range_cumulative_doses),
|
||||
"prob_inf": prob,
|
||||
"prob_specific_event": prob_specific_event,
|
||||
"emission_rate": er,
|
||||
"exposed_occupants": exposed_occupants,
|
||||
"expected_new_cases": expected_new_cases,
|
||||
|
|
@ -272,8 +274,13 @@ def manufacture_alternative_scenarios(form: FormData) -> typing.Dict[str, mc.Exp
|
|||
return scenarios
|
||||
|
||||
|
||||
def scenario_statistics(mc_model: mc.ExposureModel, sample_times: typing.List[float]):
|
||||
def scenario_statistics(mc_model: mc.ExposureModel, sample_times: typing.List[float], specific_event: bool):
|
||||
model = mc_model.build_model(size=_DEFAULT_MC_SAMPLE_SIZE)
|
||||
if (specific_event):
|
||||
# It means we have data to calculate the total_probability_rule
|
||||
prob_specific_event = np.array(model.total_probability_rule()).mean()
|
||||
else:
|
||||
prob_specific_event = 0.
|
||||
|
||||
return {
|
||||
'probability_of_infection': np.mean(model.infection_probability()),
|
||||
|
|
@ -282,6 +289,7 @@ def scenario_statistics(mc_model: mc.ExposureModel, sample_times: typing.List[fl
|
|||
np.mean(model.concentration(time))
|
||||
for time in sample_times
|
||||
],
|
||||
'prob_specific_event': prob_specific_event,
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -303,11 +311,17 @@ def comparison_report(
|
|||
else:
|
||||
statistics = {}
|
||||
|
||||
if (form.short_range_option == "short_range_yes" and form.p_recurrent_option == "p_specific_event"):
|
||||
specific_event = True
|
||||
else:
|
||||
specific_event = False
|
||||
|
||||
with executor_factory() as executor:
|
||||
results = executor.map(
|
||||
scenario_statistics,
|
||||
scenarios.values(),
|
||||
[sample_times] * len(scenarios),
|
||||
[specific_event] * len(scenarios),
|
||||
timeout=60,
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -61,6 +61,12 @@ function require_fields(obj) {
|
|||
case "hepa_no":
|
||||
require_hepa(false);
|
||||
break;
|
||||
case "p_specific_event":
|
||||
require_population(true);
|
||||
break;
|
||||
case "p_recurrent_event":
|
||||
require_population(false);
|
||||
break;
|
||||
case "mask_on":
|
||||
require_mask(true);
|
||||
break;
|
||||
|
|
@ -172,6 +178,12 @@ function require_lunch(id, option) {
|
|||
}
|
||||
}
|
||||
|
||||
function require_population(option) {
|
||||
require_input_field("#geographic_population", option);
|
||||
require_input_field("#geographic_cases", option);
|
||||
require_input_field("#ascertainment_bias", option);
|
||||
}
|
||||
|
||||
function require_mask(option) {
|
||||
$("#mask_type_1").prop('required', option);
|
||||
$("#mask_type_ffp2").prop('required', option);
|
||||
|
|
@ -269,6 +281,23 @@ function on_hepa_option_change() {
|
|||
})
|
||||
}
|
||||
|
||||
function on_p_recurrent_change() {
|
||||
p_recurrent = $('input[type=radio][name=p_recurrent_option]')
|
||||
p_recurrent.each(function (index) {
|
||||
if (this.checked) {
|
||||
getChildElement($(this)).show();
|
||||
require_fields(this);
|
||||
}
|
||||
else {
|
||||
getChildElement($(this)).hide();
|
||||
unrequire_fields(this);
|
||||
|
||||
//Clear invalid inputs for this newly hidden child element
|
||||
removeInvalid("#"+getChildElement($(this)).find('input').not('input[type=radio]').attr('id'));
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function on_wearing_mask_change() {
|
||||
wearing_mask = $('input[type=radio][name=mask_wearing_option]')
|
||||
wearing_mask.each(function (index) {
|
||||
|
|
@ -538,6 +567,18 @@ function validate_form(form) {
|
|||
}
|
||||
}
|
||||
|
||||
// Validate cases < population
|
||||
if ($("#p_specific_event").prop('checked')) {
|
||||
var geographicPopulationObj = document.getElementById("geographic_population");
|
||||
var geographicCasesObj = document.getElementById("geographic_cases");
|
||||
removeErrorFor(geographicCasesObj);
|
||||
|
||||
if (parseInt(geographicPopulationObj.value) < parseInt(geographicCasesObj.value)) {
|
||||
insertErrorFor(geographicCasesObj, "Cases > Population");
|
||||
submit = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Generate the short-range interactions list
|
||||
var short_range_interactions = [];
|
||||
$(".form_field_outer_row").each(function (index, element){
|
||||
|
|
@ -870,6 +911,12 @@ $(document).ready(function () {
|
|||
// Call the function now to handle forward/back button presses in the browser.
|
||||
on_hepa_option_change();
|
||||
|
||||
// When the p_recurrent_option changes we want to make its respective
|
||||
// children show/hide.
|
||||
$("input[type=radio][name=p_recurrent_option]").change(on_p_recurrent_change);
|
||||
// Call the function now to handle forward/back button presses in the browser.
|
||||
on_p_recurrent_change();
|
||||
|
||||
// When the mask_wearing_option changes we want to make its respective
|
||||
// children show/hide.
|
||||
$("input[type=radio][name=mask_wearing_option]").change(on_wearing_mask_change);
|
||||
|
|
|
|||
|
|
@ -317,6 +317,36 @@
|
|||
<div class="col-sm-6"><input type="number" id="infected_people" class="form-control" name="infected_people" min=1 value=1 required></div>
|
||||
</div>
|
||||
|
||||
<input type="radio" id="p_recurrent_event" name="p_recurrent_option" value="p_recurrent_event" checked="checked">
|
||||
<label for="p_recurrent_event">Recurrent exposure</label>
|
||||
<input class="ml-2" type="radio" id="p_specific_event" name="p_recurrent_option" value="p_specific_event" data-enables="#DIVp_specific_event">
|
||||
<label for="p_specific_event">Specific event</label>
|
||||
<div data-tooltip="Specific event occurring at a given time (e.g. meeting or conference). Indicate the population in a given location and the cumulative 7-day average of new reported cases. Specify the confidence level for these figures.">
|
||||
<span class="tooltip_text">?</span>
|
||||
</div>
|
||||
|
||||
<div id="DIVp_specific_event" class="tabbed" style="display: none">
|
||||
<div class="form-group row">
|
||||
<div class="col-sm-4"><label class="col-form-label">Population:</label></div>
|
||||
<div class="col-sm-6 pl-0 align-self-center"><input type="number" step="any" id="geographic_population" class="non_zero form-control" name="geographic_population" placeholder="Inhabitants (#)" min="0"></div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-sm-4"><label class="col-form-label">New confirmed cases (weekly):</label></div>
|
||||
<div class="col-sm-6 pl-0 align-self-center"><input type="number" step="any" id="geographic_cases" class="non_zero form-control" name="geographic_cases" placeholder="Cases (#7-day average sum)" min="0"></div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-sm-4"><label class="col-form-label">Confidence level:</label></div>
|
||||
<div class="col-sm-6 pl-0 align-self-center">
|
||||
<select id="ascertainment_bias" name="ascertainment_bias" class="form-control">
|
||||
<option value="confidence_low">Low - surveillance only for sympotmatic patients</option>
|
||||
<option value="confidence_medium">Medium - recommended population wide surveillance</option>
|
||||
<option value="confidence_high">High - mandatory population wide surveillance</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<span id="training_limit_error" class="red_text" hidden>Conference/Training activities limited to 1 infected<br></span>
|
||||
<hr width="80%">
|
||||
|
||||
<div class="form-group row">
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@
|
|||
{% if form.short_range_option == "short_range_yes" %}
|
||||
{% set scenario = alternative_scenarios.stats.values() | first %}
|
||||
{% set long_range_prob_inf = scenario.probability_of_infection %}
|
||||
{% set long_range_prob_specific_event = scenario.prob_specific_event if form.p_recurrent_option == 'p_specific_event' %}
|
||||
{% else %}
|
||||
{% set long_range_prob_inf = prob_inf %}
|
||||
{% endif %}
|
||||
|
|
@ -120,19 +121,41 @@
|
|||
{% endif %}
|
||||
<div class="d-flex">
|
||||
{% block report_summary %}
|
||||
<br>
|
||||
<div class="flex-row align-self-center">
|
||||
<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 {{ expected_new_cases | float_format }}</b>*.
|
||||
</div>
|
||||
{% if form.short_range_option == "short_range_yes" %}
|
||||
<br>
|
||||
<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 {{ expected_new_cases | float_format }}</b>*.
|
||||
In this scenario, assuming <b>short-range interactions</b> occur, the <b>probability of one exposed occupant getting infected can go as high as {{ prob_inf | non_zero_percentage }}</b>.
|
||||
</div>
|
||||
{% if form.short_range_option == "short_range_yes" %}
|
||||
{% endif %}
|
||||
{% block specific_event_probability %}
|
||||
{% if form.p_recurrent_option == "p_specific_event" %}
|
||||
<br>
|
||||
<div class="align-self-center alert alert-dark mb-0" role="alert">
|
||||
In this scenario, assuming <b>short-range interactions</b> occur, the <b>probability of one exposed occupant getting infected can go as high as {{ prob_inf | non_zero_percentage }}</b>.
|
||||
The above {{ "result assumes" if form.short_range_option == "short_range_no" else "results assume" }} that <b>{{ form.infected_people }}
|
||||
{{ "occupant is infected" if form.infected_people == 1 else "occupants are infected" }}
|
||||
</b> in the room.
|
||||
By taking into account the estimate of cases currently circulating in <b>{{ form.location_name }}</b>,
|
||||
the probability of on-site transmission, having at least 1 new infection in an <b>event
|
||||
with {{ form.total_people }} occupants</b>, is
|
||||
{% if form.short_range_option == 'short_range_yes' %}:
|
||||
<ul>
|
||||
<li><b>{{ long_range_prob_specific_event | non_zero_percentage }}</b>, assuming all occupants are exposed equally (i.e. without short-range interactions).</li>
|
||||
<li><b>{{ prob_specific_event | non_zero_percentage }}</b>, assuming short-range interactions occur.</li>
|
||||
</ul>
|
||||
{% else %}
|
||||
<b>{{ prob_specific_event | non_zero_percentage }}</b>.
|
||||
{% endif %}
|
||||
{% if prob_specific_event > prob_inf %}
|
||||
<p>The probability of infection is larger than above which signifies that the chosen number of infected occupants in the form was underestimated.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
</div>
|
||||
{% endblock report_summary %}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -419,6 +442,18 @@
|
|||
<li><p class="data_text">Number of attendees and infected people: {{ form.total_people }} in attendance, of whom {{ form.infected_people }}
|
||||
{{ "is" if form.infected_people == 1 else "are" }}
|
||||
infected.</p></li>
|
||||
{% if form.p_recurrent_option == "p_specific_event" %}
|
||||
{% if form.ascertainment_bias == "confidence_high" %}
|
||||
{% set conf_level = "High - Mandatory population surveillance." %}
|
||||
{% elif form.ascertainment_bias == "confidence_medium" %}
|
||||
{% set conf_level = "Medium - Recommended population wide surveillance." %}
|
||||
{% else %}
|
||||
{% set conf_level = "Low - Surveillance only for sympotmatic patients." %}
|
||||
{% endif %}
|
||||
<li><p class="data_text">Population in {{ form.location_name }}: {{ form.geographic_population }}</p></li>
|
||||
<li><p class="data_text">New reported cases in {{ form.location_name }} (7-day average): {{ form.geographic_cases }}</p></li>
|
||||
<li><p class="data_text">Confidence level: {{ conf_level }} </p></li>
|
||||
{% endif %}
|
||||
<li><p class="data_text">
|
||||
Activity type:
|
||||
{% if form.activity_type == "office" %}
|
||||
|
|
|
|||
|
|
@ -113,7 +113,22 @@ The recommended airflow rate for the HEPA filter should correspond to a total ai
|
|||
<p>Here we capture the information about the event being simulated.
|
||||
First enter the number of occupants in the space, if you have a (small) variation in the number of people, please input the average or consider using the expert tool.
|
||||
Within the number of people occupying the space, you should specify how many are infected.</p>
|
||||
<p>As an example, for a shared office with 4 people, where one person is infected, we enter 4 occupants and 1 infected person.</p>
|
||||
<p>As an example, for a shared office with 4 people, where one person is infected, we enter 4 occupants and 1 infected person.</p><br/>
|
||||
<p>In case one would like to simulate an event happening at a given time and location, where the epidemiological situation is known, the tool allows for an estimation of the probability of on-site transmission, considering the chances that a given person in the event is infected.
|
||||
The user will need to select <b>Specific event</b>, input the number of inhabitants and the cumulative weekly (7-day average) value of new reported positive cases at the event location, as well as the confidence level of the inputs. The first two inputs need to the related, i.e. the values of reported new cases and the number of inhabitants shall correspond to the a same geographical location. For example:</p>
|
||||
<ul>
|
||||
<li>Population of Geneva, CH: 508 000 inhabitants</li>
|
||||
<li>New reported cases in the canton of Geneva: 1000 (in one week - avg)</li>
|
||||
</ul>
|
||||
<p>The confidence level has the following options:</p>
|
||||
<ul>
|
||||
<li>High - mandatory population wide surveillance</li>
|
||||
<li>Medium - recommended population wide surveillance</li>
|
||||
<li>Low - surveillance only for sympotmatic patients</li>
|
||||
</ul>
|
||||
<p>Depending on the epidemiological situation in the choosen location, the public health surveillance can be more or less active. The confidence level will provide an ascertainment bias to the data collected by the user.</p>
|
||||
<p>The higher the incidence rate (i.e. new cases / population) the higher are the chances of having at least one infected occupant participating to the event.
|
||||
For general and recurrent layout simply select the <b>Recurrent exposure</b> option.</p>
|
||||
<br>
|
||||
<h4>Activity type</h4>
|
||||
<br>
|
||||
|
|
|
|||
|
|
@ -87,6 +87,11 @@
|
|||
In this scenario, assuming <b>short-range interactions</b> occur, the <b>probability of one exposed occupant getting infected can go as high as {{prob_inf | non_zero_percentage}}</b>.
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% block specific_event_probability %}
|
||||
{{ super() }}
|
||||
{% endblock specific_event_probability %}
|
||||
|
||||
{% if (prob_inf > 2) %}
|
||||
<br>
|
||||
{% if cern_level == "green-1" %}
|
||||
|
|
|
|||
Loading…
Reference in a new issue