diff --git a/cara/apps/calculator/report_generator.py b/cara/apps/calculator/report_generator.py index 789d8865..5731e6cb 100644 --- a/cara/apps/calculator/report_generator.py +++ b/cara/apps/calculator/report_generator.py @@ -227,12 +227,14 @@ def manufacture_alternative_scenarios(form: FormData) -> typing.Dict[str, mc.Exp FFP2_being_worn = bool(form.mask_wearing_option == 'mask_on' and form.mask_type == 'FFP2') if FFP2_being_worn and form.hepa_option: FFP2andHEPAalternative = dataclass_utils.replace(form, mask_type='Type I') - scenarios['Base scenario with HEPA filter and Type I masks'] = FFP2andHEPAalternative.build_mc_model() + if not (form.hepa_option and form.mask_wearing_option == 'mask_on' and form.mask_type == 'Type I'): + scenarios['Base scenario with HEPA filter and Type I masks'] = FFP2andHEPAalternative.build_mc_model() if not FFP2_being_worn and form.hepa_option: noHEPAalternative = dataclass_utils.replace(form, mask_type = 'FFP2') noHEPAalternative = dataclass_utils.replace(noHEPAalternative, mask_wearing_option = 'mask_on') noHEPAalternative = dataclass_utils.replace(noHEPAalternative, hepa_option=False) - scenarios['Base scenario without HEPA filter, with FFP2 masks'] = noHEPAalternative.build_mc_model() + if not (not form.hepa_option and FFP2_being_worn): + scenarios['Base scenario without HEPA filter, with FFP2 masks'] = noHEPAalternative.build_mc_model() # The remaining scenarios are based on Type I masks (possibly not worn) # and no HEPA filtration. @@ -245,17 +247,22 @@ def manufacture_alternative_scenarios(form: FormData) -> typing.Dict[str, mc.Exp if form.ventilation_type == 'mechanical_ventilation': #scenarios['Mechanical ventilation with Type I masks'] = with_mask.build_mc_model() - scenarios['Mechanical ventilation without masks'] = without_mask.build_mc_model() + if not (form.mask_wearing_option == 'mask_off'): + scenarios['Mechanical ventilation without masks'] = without_mask.build_mc_model() elif form.ventilation_type == 'natural_ventilation': #scenarios['Windows open with Type I masks'] = with_mask.build_mc_model() - scenarios['Windows open without masks'] = without_mask.build_mc_model() + if not (form.mask_wearing_option == 'mask_off'): + scenarios['Windows open without masks'] = without_mask.build_mc_model() # No matter the ventilation scheme, we include scenarios which don't have any ventilation. with_mask_no_vent = dataclass_utils.replace(with_mask, ventilation_type='no_ventilation') without_mask_or_vent = dataclass_utils.replace(without_mask, ventilation_type='no_ventilation') - scenarios['No ventilation with Type I masks'] = with_mask_no_vent.build_mc_model() - scenarios['Neither ventilation nor masks'] = without_mask_or_vent.build_mc_model() + + if not (form.mask_wearing_option == 'mask_on' and form.mask_type == 'Type I' and form.ventilation_type == 'no_ventilation'): + scenarios['No ventilation with Type I masks'] = with_mask_no_vent.build_mc_model() + if not (form.mask_wearing_option == 'mask_off' and form.ventilation_type == 'no_ventilation'): + scenarios['Neither ventilation nor masks'] = without_mask_or_vent.build_mc_model() else: no_short_range_alternative = dataclass_utils.replace(form, short_range_interactions=[]) @@ -278,11 +285,19 @@ def scenario_statistics(mc_model: mc.ExposureModel, sample_times: typing.List[fl def comparison_report( + 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], ): - statistics = {} + statistics = { + 'Current scenario' : { + 'probability_of_infection': report_data['prob_inf'], + 'expected_new_cases': report_data['expected_new_cases'], + 'concentrations': report_data['concentrations'], + } + } + with executor_factory() as executor: results = executor.map( scenario_statistics, @@ -293,6 +308,7 @@ def comparison_report( for (name, model), model_stats in zip(scenarios.items(), results): statistics[name] = model_stats + return { 'stats': statistics, } @@ -330,11 +346,11 @@ class ReportGenerator: } scenario_sample_times = interesting_times(model) - - context.update(calculate_report_data(form, model)) + report_data = calculate_report_data(form, model) + context.update(report_data) alternative_scenarios = manufacture_alternative_scenarios(form) context['alternative_scenarios'] = comparison_report( - alternative_scenarios, scenario_sample_times, executor_factory=executor_factory, + report_data, alternative_scenarios, scenario_sample_times, executor_factory=executor_factory, ) context['permalink'] = generate_permalink(base_url, self.calculator_prefix, form) context['calculator_prefix'] = self.calculator_prefix diff --git a/cara/apps/calculator/static/css/report.css b/cara/apps/calculator/static/css/report.css index dfd11ff6..f0e07d90 100644 --- a/cara/apps/calculator/static/css/report.css +++ b/cara/apps/calculator/static/css/report.css @@ -4,6 +4,8 @@ bottom: 20px; right: 20px; padding: 20px; + -webkit-print-color-adjust: exact; /* chrome & webkit browsers */ + print-color-adjust: exact; /*firefox & IE */ } h1 { @@ -61,24 +63,6 @@ p.notes { width: 100pt; } -.red_bkg { - color: #000000; - background-color: #CD5C5C; - text-decoration: none; -} - -.yellow_bkg { - color: #000000; - background-color: #FFFF00; - text-decoration: none; -} - -.green_bkg { - color: #000000; - background-color: #90EE90; - text-decoration: none; -} - .icon_button { border: none; background: none; @@ -133,15 +117,16 @@ p.notes { .icon_button { display: none!important; } - .card { - page-break-inside: avoid; - } .pi-image { max-height: 9em; } .pi-box { max-width: 260px!important; } + .card { + page-break-inside: avoid!important; + break-inside: avoid!important; + } #link-results { display: none; } @@ -172,6 +157,7 @@ p.notes { border-radius: 100px; z-index: 1; -webkit-print-color-adjust: exact!important; + print-color-adjust: exact!important; } .intro-banner-vdo-play-btn i { diff --git a/cara/apps/calculator/static/js/report.js b/cara/apps/calculator/static/js/report.js index bd41d380..a6010221 100644 --- a/cara/apps/calculator/static/js/report.js +++ b/cara/apps/calculator/static/js/report.js @@ -605,8 +605,9 @@ function draw_alternative_scenarios_plot(concentration_plot_svg_id, alternative_ .text('Mean concentration (virions/m³)'); // Legend bounding box. + max_key_length = Math.max(...(Object.keys(data_for_scenarios).map(el => el.length))); var legendBBox = vis.append('rect') - .attr('width', 275) + .attr('width', 8.25 * max_key_length ) .attr('height', 25 * (Object.keys(data_for_scenarios).length)) .attr('stroke', 'lightgrey') .attr('stroke-width', '2') @@ -711,10 +712,10 @@ function draw_alternative_scenarios_plot(concentration_plot_svg_id, alternative_ var div_height = document.getElementById(concentration_plot_svg_id).clientHeight; graph_width = div_width; graph_height = div_height - if (div_width >= 900) { // For screens with width > 900px legend can be on the graph's right side. + if (div_width >= 1200) { // For screens with width > 900px legend can be on the graph's right side. var margins = { top: 30, right: 20, bottom: 50, left: 60 }; - div_width = 900; - graph_width = div_width * (2/3); + div_width = 1200; + graph_width = 600; const svg_margins = {'margin-left': '0rem'}; Object.entries(svg_margins).forEach(([prop,val]) => vis.style(prop,val)); } diff --git a/cara/apps/templates/cern/calculator.report.html.j2 b/cara/apps/templates/cern/calculator.report.html.j2 index c55cd6dd..04092ed6 100644 --- a/cara/apps/templates/cern/calculator.report.html.j2 +++ b/cara/apps/templates/cern/calculator.report.html.j2 @@ -1,6 +1,8 @@ {% extends "base/calculator.report.html.j2" %} {% set cern_level = 'green-1' %} +{% set orange_prob_lim = 2 %} +{% set red_prob_lim = 10 %} {% if form.short_range_option == "short_range_yes" %} {% set scenario = alternative_scenarios.stats.values() | first %} @@ -9,10 +11,10 @@ {% set long_range_prob_inf = prob_inf %} {% endif %} -{% if ((long_range_prob_inf > 10) or (expected_new_cases >= 1)) %} +{% if ((long_range_prob_inf > red_prob_lim) or (expected_new_cases >= 1)) %} {% set long_range_scale_warning = 'red' %} {% set long_range_warning_color= 'bg-danger' %} -{% elif (2 <= long_range_prob_inf <= 10) %} +{% elif (orange_prob_lim <= long_range_prob_inf <= red_prob_lim) %} {% set long_range_scale_warning = 'orange' %} {% set long_range_warning_color = 'bg-warning' %} {% else %} @@ -20,8 +22,8 @@ {% set long_range_warning_color = 'bg-success' %} {% endif %} -{% if ((prob_inf > 10) or (expected_new_cases >= 1)) %} {% set scale_warning = 'red' %} -{% elif (2 <= prob_inf <= 10) %} {% set scale_warning = 'orange' %} +{% if ((prob_inf > red_prob_lim) or (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 %} @@ -135,12 +137,12 @@
{% for scenario_name, scenario_stats in alternative_scenarios.stats.items() %} - {%if (( scenario_stats.probability_of_infection > 15) or (scenario_stats.expected_new_cases >= 1)) %} -