polished cern caimira frontned model related methods
This commit is contained in:
parent
ebde0e1ae4
commit
36691d9138
5 changed files with 72 additions and 56 deletions
|
|
@ -18,10 +18,9 @@ def generate_model(form_obj, data_registry):
|
|||
return form_obj.build_model(sample_size=sample_size)
|
||||
|
||||
|
||||
def generate_report_results(form_obj, model):
|
||||
def generate_report_results(form_obj):
|
||||
return rg.calculate_report_data(
|
||||
form=form_obj,
|
||||
model=model,
|
||||
executor_factory=functools.partial(
|
||||
concurrent.futures.ThreadPoolExecutor, None, # TODO define report_parallelism
|
||||
),
|
||||
|
|
|
|||
|
|
@ -5,8 +5,6 @@ import io
|
|||
import typing
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
import urllib
|
||||
import zlib
|
||||
|
||||
from caimira.calculator.models import models, dataclass_utils, profiler, monte_carlo as mc
|
||||
from caimira.calculator.models.enums import ViralLoads
|
||||
|
|
@ -123,7 +121,9 @@ def _calculate_co2_concentration(CO2_model, time, fn_name=None):
|
|||
|
||||
|
||||
@profiler.profile
|
||||
def calculate_report_data(form: VirusFormData, model: models.ExposureModel, executor_factory: typing.Callable[[], concurrent.futures.Executor]) -> typing.Dict[str, typing.Any]:
|
||||
def calculate_report_data(form: VirusFormData, executor_factory: typing.Callable[[], concurrent.futures.Executor]) -> typing.Dict[str, typing.Any]:
|
||||
model: models.ExposureModel = form.build_model()
|
||||
|
||||
times = interesting_times(model)
|
||||
short_range_intervals = [interaction.presence.boundaries()[0]
|
||||
for interaction in model.short_range]
|
||||
|
|
@ -191,7 +191,7 @@ def calculate_report_data(form: VirusFormData, model: models.ExposureModel, exec
|
|||
uncertainties_plot(prob, conditional_probability_data)))
|
||||
|
||||
return {
|
||||
"model_repr": repr(model),
|
||||
"model": model,
|
||||
"times": list(times),
|
||||
"exposed_presence_intervals": exposed_presence_intervals,
|
||||
"short_range_intervals": short_range_intervals,
|
||||
|
|
@ -330,26 +330,7 @@ def img2base64(img_data) -> str:
|
|||
return f'data:image/png;base64,{pic_hash}'
|
||||
|
||||
|
||||
def generate_permalink(base_url, get_root_url, get_root_calculator_url, form: VirusFormData):
|
||||
form_dict = VirusFormData.to_dict(form, strip_defaults=True)
|
||||
|
||||
# Generate the calculator URL arguments that would be needed to re-create this
|
||||
# form.
|
||||
args = urllib.parse.urlencode(form_dict)
|
||||
|
||||
# Then zlib compress + base64 encode the string. To be inverted by the
|
||||
# /_c/ endpoint.
|
||||
compressed_args = base64.b64encode(zlib.compress(args.encode())).decode()
|
||||
qr_url = f"{base_url}{get_root_url()}/_c/{compressed_args}"
|
||||
url = f"{base_url}{get_root_calculator_url()}?{args}"
|
||||
|
||||
return {
|
||||
'link': url,
|
||||
'shortened': qr_url,
|
||||
}
|
||||
|
||||
|
||||
def manufacture_viral_load_scenarios_percentiles(model: mc.ExposureModel) -> typing.Dict[str, mc.ExposureModel]:
|
||||
def calculate_vl_scenarios_percentiles(model: mc.ExposureModel) -> typing.Dict[str, mc.ExposureModel]:
|
||||
viral_load = model.concentration_model.infected.virus.viral_load_in_sputum
|
||||
scenarios = {}
|
||||
for percentil in (0.01, 0.05, 0.25, 0.5, 0.75, 0.95, 0.99):
|
||||
|
|
@ -359,7 +340,9 @@ def manufacture_viral_load_scenarios_percentiles(model: mc.ExposureModel) -> typ
|
|||
)
|
||||
scenarios[str(vl)] = np.mean(
|
||||
specific_vl_scenario.infection_probability())
|
||||
return scenarios
|
||||
return {
|
||||
'alternative_viral_load': scenarios,
|
||||
}
|
||||
|
||||
|
||||
def manufacture_alternative_scenarios(form: VirusFormData) -> typing.Dict[str, mc.ExposureModel]:
|
||||
|
|
@ -451,7 +434,6 @@ def comparison_report(
|
|||
form: VirusFormData,
|
||||
report_data: typing.Dict[str, typing.Any],
|
||||
scenarios: typing.Dict[str, mc.ExposureModel],
|
||||
sample_times: typing.List[float],
|
||||
executor_factory: typing.Callable[[], concurrent.futures.Executor],
|
||||
):
|
||||
if (form.short_range_option == "short_range_no"):
|
||||
|
|
@ -474,7 +456,7 @@ def comparison_report(
|
|||
results = executor.map(
|
||||
scenario_statistics,
|
||||
scenarios.values(),
|
||||
[sample_times] * len(scenarios),
|
||||
[report_data['times']] * len(scenarios),
|
||||
[compute_prob_exposure] * len(scenarios),
|
||||
timeout=60,
|
||||
)
|
||||
|
|
@ -485,3 +467,10 @@ def comparison_report(
|
|||
return {
|
||||
'stats': statistics,
|
||||
}
|
||||
|
||||
|
||||
def alternative_scenarios_data(form: VirusFormData, report_data: typing.Dict[str, typing.Any], executor_factory: typing.Callable[[], concurrent.futures.Executor]) -> typing.Dict[str, typing.Any]:
|
||||
alternative_scenarios: typing.Dict[str, typing.Any] = manufacture_alternative_scenarios(form=form)
|
||||
return {
|
||||
'alternative_scenarios': comparison_report(form=form, report_data=report_data, scenarios=alternative_scenarios, executor_factory=executor_factory)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -246,8 +246,7 @@ class ConcentrationModelJsonResponse(BaseRequestHandler):
|
|||
max_workers=self.settings['handler_worker_pool_size'],
|
||||
timeout=300,
|
||||
)
|
||||
model = virus_report_controller.generate_model(form, data_registry)
|
||||
report_data_task = executor.submit(calculate_report_data, form, model,
|
||||
report_data_task = executor.submit(calculate_report_data, form,
|
||||
executor_factory=functools.partial(
|
||||
concurrent.futures.ThreadPoolExecutor,
|
||||
self.settings['report_generation_parallelism'],
|
||||
|
|
|
|||
|
|
@ -5,13 +5,16 @@ import concurrent.futures
|
|||
import json
|
||||
import typing
|
||||
import jinja2
|
||||
import urllib
|
||||
import zlib
|
||||
import base64
|
||||
import numpy as np
|
||||
|
||||
from .. import markdown_tools
|
||||
|
||||
from caimira.calculator.models import models
|
||||
from caimira.calculator.validators.virus.virus_validator import VirusFormData
|
||||
from caimira.calculator.report.virus_report_data import calculate_report_data, interesting_times, manufacture_alternative_scenarios, manufacture_viral_load_scenarios_percentiles, comparison_report, generate_permalink
|
||||
from caimira.calculator.report.virus_report_data import alternative_scenarios_data, calculate_report_data, calculate_vl_scenarios_percentiles
|
||||
|
||||
|
||||
def minutes_to_time(minutes: int) -> str:
|
||||
|
|
@ -62,6 +65,25 @@ def non_zero_percentage(percentage: int) -> str:
|
|||
return "{:0.1f}%".format(percentage)
|
||||
|
||||
|
||||
def generate_permalink(base_url, get_root_url, get_root_calculator_url, form: VirusFormData):
|
||||
form_dict = VirusFormData.to_dict(form, strip_defaults=True)
|
||||
|
||||
# Generate the calculator URL arguments that would be needed to re-create this
|
||||
# form.
|
||||
args = urllib.parse.urlencode(form_dict)
|
||||
|
||||
# Then zlib compress + base64 encode the string. To be inverted by the
|
||||
# /_c/ endpoint.
|
||||
compressed_args = base64.b64encode(zlib.compress(args.encode())).decode()
|
||||
qr_url = f"{base_url}{get_root_url()}/_c/{compressed_args}"
|
||||
url = f"{base_url}{get_root_calculator_url()}?{args}"
|
||||
|
||||
return {
|
||||
'link': url,
|
||||
'shortened': qr_url,
|
||||
}
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class VirusReportGenerator:
|
||||
jinja_loader: jinja2.BaseLoader
|
||||
|
|
@ -74,44 +96,53 @@ class VirusReportGenerator:
|
|||
form: VirusFormData,
|
||||
executor_factory: typing.Callable[[], concurrent.futures.Executor],
|
||||
) -> str:
|
||||
model = form.build_model()
|
||||
context = self.prepare_context(
|
||||
base_url, model, form, executor_factory=executor_factory)
|
||||
base_url, form, executor_factory=executor_factory)
|
||||
return self.render(context)
|
||||
|
||||
def prepare_context(
|
||||
self,
|
||||
base_url: str,
|
||||
model: models.ExposureModel,
|
||||
form: VirusFormData,
|
||||
executor_factory: typing.Callable[[], concurrent.futures.Executor],
|
||||
) -> dict:
|
||||
now = datetime.utcnow().astimezone()
|
||||
time = now.strftime("%Y-%m-%d %H:%M:%S UTC")
|
||||
|
||||
data_registry_version = f"v{model.data_registry.version}" if model.data_registry.version else None
|
||||
context = {
|
||||
'model': model,
|
||||
'form': form,
|
||||
'creation_date': time,
|
||||
'data_registry_version': data_registry_version,
|
||||
}
|
||||
|
||||
scenario_sample_times = interesting_times(model)
|
||||
report_data = calculate_report_data(
|
||||
form, model, executor_factory=executor_factory)
|
||||
# Main report data
|
||||
report_data = calculate_report_data(form, executor_factory)
|
||||
context.update(report_data)
|
||||
|
||||
alternative_scenarios = manufacture_alternative_scenarios(form)
|
||||
context['alternative_viral_load'] = manufacture_viral_load_scenarios_percentiles(
|
||||
model) if form.conditional_probability_viral_loads else None
|
||||
context['alternative_scenarios'] = comparison_report(
|
||||
form, report_data, alternative_scenarios, scenario_sample_times, executor_factory=executor_factory,
|
||||
)
|
||||
context['permalink'] = generate_permalink(
|
||||
# Model and Data Registry
|
||||
model: models.ExposureModel = report_data['model']
|
||||
data_registry_version: typing.Optional[str] = f"v{model.data_registry.version}" if model.data_registry.version else None
|
||||
|
||||
# Alternative scenarios data
|
||||
alternative_scenarios: typing.Dict[str,typing.Any] = alternative_scenarios_data(form, report_data, executor_factory)
|
||||
context.update(alternative_scenarios)
|
||||
|
||||
# Alternative viral load data
|
||||
if form.conditional_probability_viral_loads:
|
||||
alternative_viral_load: typing.Dict[str,typing.Any] = calculate_vl_scenarios_percentiles(model)
|
||||
context.update(alternative_viral_load)
|
||||
|
||||
# Permalink
|
||||
permalink: typing.Dict[str, str] = generate_permalink(
|
||||
base_url, self.get_root_url, self.get_root_calculator_url, form)
|
||||
context['get_url'] = self.get_root_url
|
||||
context['get_calculator_url'] = self.get_root_calculator_url
|
||||
|
||||
# URLs (root, calculator and permalink)
|
||||
context.update({
|
||||
'model_repr': repr(model),
|
||||
'data_registry_version': data_registry_version,
|
||||
'permalink': permalink,
|
||||
'get_url': self.get_root_url,
|
||||
'get_calculator_url': self.get_root_calculator_url,
|
||||
})
|
||||
|
||||
return context
|
||||
|
||||
|
|
|
|||
|
|
@ -104,23 +104,21 @@ def test_interesting_times_w_temp(exposure_model_w_outside_temp_changes):
|
|||
np.testing.assert_allclose(result, expected)
|
||||
|
||||
|
||||
def test_expected_new_cases(baseline_form_with_sr: VirusFormData):
|
||||
model = baseline_form_with_sr.build_model()
|
||||
|
||||
def test_expected_new_cases(baseline_form_with_sr: VirusFormData):
|
||||
executor_factory = partial(
|
||||
concurrent.futures.ThreadPoolExecutor, 1,
|
||||
)
|
||||
|
||||
# Short- and Long-range contributions
|
||||
report_data = rep_gen.calculate_report_data(baseline_form_with_sr, model, executor_factory)
|
||||
report_data = rep_gen.calculate_report_data(baseline_form_with_sr, executor_factory)
|
||||
sr_lr_expected_new_cases = report_data['expected_new_cases']
|
||||
sr_lr_prob_inf = report_data['prob_inf']/100
|
||||
|
||||
# Long-range contributions alone
|
||||
scenario_sample_times = rep_gen.interesting_times(model)
|
||||
scenario_sample_times = report_data['times']
|
||||
alternative_scenarios = rep_gen.manufacture_alternative_scenarios(baseline_form_with_sr)
|
||||
alternative_statistics = rep_gen.comparison_report(
|
||||
baseline_form_with_sr, report_data, alternative_scenarios, scenario_sample_times, executor_factory=executor_factory,
|
||||
baseline_form_with_sr, report_data, alternative_scenarios, executor_factory=executor_factory,
|
||||
)
|
||||
|
||||
lr_expected_new_cases = alternative_statistics['stats']['Base scenario without short-range interactions']['expected_new_cases']
|
||||
|
|
|
|||
Loading…
Reference in a new issue