Merge branch 'master' into feature/concentration_cumulative_dose

This commit is contained in:
Luis Aleixo 2021-10-27 16:15:49 +02:00
commit f3d23cf405
12 changed files with 57 additions and 111 deletions

View file

@ -1,17 +1,15 @@
import concurrent.futures
import base64
import dataclasses
from datetime import datetime, timedelta
from datetime import datetime
import io
import json
import typing
import urllib
import zlib
import loky
import jinja2
import numpy as np
import qrcode
import json
from cara import models
from ... import monte_carlo as mc
@ -128,7 +126,7 @@ def calculate_report_data(model: models.ExposureModel):
}
def generate_qr_code(base_url, calculator_prefix, form: FormData):
def generate_permalink(base_url, calculator_prefix, form: FormData):
form_dict = FormData.to_dict(form, strip_defaults=True)
# Generate the calculator URL arguments that would be needed to re-create this
@ -141,20 +139,9 @@ def generate_qr_code(base_url, calculator_prefix, form: FormData):
qr_url = f"{base_url}/_c/{compressed_args}"
url = f"{base_url}{calculator_prefix}?{args}"
qr = qrcode.QRCode(
version=1,
error_correction=qrcode.constants.ERROR_CORRECT_H,
box_size=10,
border=4,
)
qr.add_data(qr_url)
qr.make(fit=True)
img = qr.make_image(fill_color="black", back_color="white").convert('RGB')
return {
'image': img2base64(_img2bytes(img)),
'link': url,
'qr_url': qr_url,
'shortened': qr_url,
}
@ -318,7 +305,7 @@ class ReportGenerator:
context['alternative_scenarios'] = comparison_report(
alternative_scenarios, scenario_sample_times, executor_factory=executor_factory,
)
context['qr_code'] = generate_qr_code(base_url, self.calculator_prefix, form)
context['permalink'] = generate_permalink(base_url, self.calculator_prefix, form)
context['calculator_prefix'] = self.calculator_prefix
context['scale_warning'] = {
'level': 'yellow-2',

View file

@ -56,7 +56,7 @@ p.notes {
margin: 1%
}
#pdf-qr-code {
#pdf_qrcode_aref {
margin-right: 1%;
width: 100pt;
}
@ -102,11 +102,6 @@ p.notes {
border-radius: 5px;
}
.print-button {
margin-left: auto;
margin-right: 1%;
}
/* @media (width: 1200px) { */
@media print {
/* #body {
@ -120,7 +115,7 @@ p.notes {
#link_reproduce_results {
display: none!important;
}
#pdf-qr-code {
#pdf_qrcode_aref {
visibility: inherit!important;
}
.collapse {
@ -138,17 +133,12 @@ p.notes {
.icon_button {
display: none!important;
}
.print-button {
display: none!important;
}
.card {
page-break-inside: avoid;
}
/* CSS styling to avoid page breaks. */
.break-after {
page-break-after: always;
}
.break-avoid {
#disclaimer {
border: 2px solid black;
padding: 15px;
page-break-inside: avoid;
}
}
@ -162,7 +152,8 @@ p.notes {
position: relative;
text-align: center;
border-radius: 100px;
z-index: 1
z-index: 1;
-webkit-print-color-adjust: exact!important;
}
.intro-banner-vdo-play-btn i {

View file

@ -1,29 +0,0 @@
function generate_pdf_version(qr_link) {
const pdf_version = this.document.getElementById("body");
// PDF styling
var opt = {
filename: 'myfile.pdf',
image: { type: 'jpeg', quality: 0.98 },
html2canvas: { scale: 2, width: 1200, windowWidth: 1200 },
enableLinks: false,
jsPDF: {
unit: 'pt',
format: 'letter',
orientation: 'portrait',
},
pagebreak: { mode: '', avoid: '.break-avoid' },
};
html2pdf().set(opt).from(pdf_version).toPdf().get('pdf').then(function(pdf) {
var totalPages = pdf.internal.getNumberOfPages();
pdf.setPage(1);
pdf.link(530, 25, 60, 60, { url: qr_link }); //Hyperlink to reproduce results
for (i = 1; i <= totalPages; i++) {
pdf.setPage(i);
pdf.setFontSize(10);
pdf.setTextColor(150);
pdf.text('Page ' + i + ' of ' + totalPages, (pdf.internal.pageSize.getWidth() / 2.25), (pdf.internal.pageSize.getHeight() - 10));
}
}).save();
};

View file

@ -23,9 +23,8 @@
<h2 class="text-component-title mb-0">CARA - CALCULATOR REPORT</h1>
<p class="mb-0"> Created {{ creation_date }} using CARA calculator version v{{ form.calculator_version }}</p>
</div>
<button type="button" class="btn btn-outline-dark align-self-center" style="margin-right: -100pt" id="download-pdf" onclick="print()">Print Report</button>
{# To be replaced by "Generate PDF" #}
<img id="pdf-qr-code" class="align-self-center invisible" src="{{ qr_code.image }}"/>
<button type="button" class="btn btn-outline-dark align-self-center" id="print-button" style="margin-right: -100pt" onclick="print()">Print Report</button>
<a href="{{ permalink.link }}" style="float: left;" id="pdf_qrcode_aref" class="align-self-center invisible"><div id="pdf_qrcode"></div></a>
</div>
{% endblock report_header %}
@ -163,10 +162,10 @@
<div class="collapse show" id="collapseQRcode">
<div class="card-body">
<div>
<a href="{{ qr_code.link }}" style="float: left;"><img style="width:250pt;" id="qr_code" src="{{ qr_code.image }}"/></a>
<a href="{{ permalink.link }}" style="float: left;"><div id="qrcode"></div></a>
<span style="float: left; min-height: 250pt; line-height: 250pt; vertical-align: middle; display: inline-block;">
<p style="display: inline-block; vertical-align: middle; line-height: normal;">
Click the QR code to regenerate the report and get a shareable link.<br>Alternatively, scan to regenerate the report.<br> Mobile-friendly app coming soon!
Click the QR code to regenerate the report and get a shareable link.<br>Alternatively, scan to regenerate the report.<br>
</p>
</span>
</div>
@ -392,7 +391,7 @@
{% block disclaimer_container %}
<br><br><br>
<div style="border: 2px solid black; padding: 15px;">
<div id="disclaimer">
{% block disclaimer %}
<p class="image"> <img align="middle" src="{{ calculator_prefix }}/static/images/disclaimer.jpg" width="40" height="40"><b>Disclaimer:</b><br><br></p>
@ -434,11 +433,26 @@
</div>
{% endblock disclaimer_container %}
<script src="{{ calculator_prefix }}/static/js/pdf.js"></script>
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.9.2/html2pdf.bundle.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/qrcodejs/1.0.0/qrcode.min.js" integrity="sha512-CNgIRecGo7nphbeZ04Sc13ka07paqdeTu0WR1IM4kNcpmBAUSHSQX0FslNhTDadL4O5SAGapGt4FodqL8My0mA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script type="text/javascript">
new QRCode(document.getElementById("qrcode"), {
text: "{{ permalink.shortened }}",
width: 330,
height: 330,
correctLevel : QRCode.CorrectLevel.L
});
new QRCode(document.getElementById("pdf_qrcode"), {
text: "{{ permalink.shortened }}",
width: 133,
height: 133,
correctLevel : QRCode.CorrectLevel.L
});
</script>
</body>
</html>

View file

@ -145,7 +145,6 @@
</div>
</div>
</div>
<div class="break-avoid fourth_page"></div>
{% endblock report_preamble %}
{% block report_footer %}

View file

@ -40,8 +40,11 @@ Although the user is able to calculate the infection probability of a stand-alon
<li>Etc…</li>
</ul>
<h1>Authors:</h1><br>
{{ text_blocks['Authors'] }}
<h1>Main Developers:</h1><br>
{{ text_blocks['Main Developers'] }}
<h2>Code Contributors:</h2><br>
{{ text_blocks['Code Contributors'] }}
<h1>Acknowledgements:</h1><br>
{{ text_blocks['Acknowledgements'] }}

View file

@ -1,6 +1,6 @@
## Authors
## Main Developers
<h4>Andre Henriques<sup>1</sup>, Marco Andreini<sup>1</sup>, Gabriella Azzopardi<sup>2</sup>, James Devine<sup>3</sup>, Philip Elson<sup>4</sup>, Nicolas Mounet<sup>2</sup>, Markus Kongstein Rognlien<sup>2,6</sup>, Nicola Tarocco<sup>5</sup></h4><br>
<h4>Andre Henriques<sup>1</sup>, Luis Aleixo<sup>1</sup>, Marco Andreini<sup>1</sup>, Gabriella Azzopardi<sup>2</sup>, James Devine<sup>3</sup>, Philip Elson<sup>4</sup>, Nicolas Mounet<sup>2</sup>, Markus Kongstein Rognlien<sup>2,6</sup>, Nicola Tarocco<sup>5</sup></h4><br>
<sup>1</sup>HSE Unit, Occupational Health & Safety Group, CERN<br>
<sup>2</sup>Beams Department, Accelerators and Beam Physics Group, CERN<br>
@ -9,6 +9,12 @@
<sup>5</sup>Information Technology Department, Collaboration, Devices & Applications Group, CERN<br>
<sup>6</sup>Norwegian University of Science and Technology (NTNU)<br>
## Code Contributors
<h4>Anna Efimova<sup>1</sup>, Anel Massalimova<sup>1</sup>, Cole Austin Coughlin<sup>1</sup></h4>
<sup>1</sup>Summer Students, CERN<br>
## Acknowledgements
We wish to thank CERNs HSE Unit, Beams Department, Experimental Physics Department, Information Technology Department, Industry, Procurement and Knowledge Transfer Department and International Relations Sector for their support to the study. Thanks to Doris Forkel-Wirth, Benoit Delille, Walid Fadel, Olga Beltramello, Letizia Di Giulio, Evelyne Dho, Wayne Salter, Benoit Salvant and colleagues from the COVID working group for providing expert advice and extensively testing the model. Finally, we wish to thank Fabienne Landua and the design service for preparing the illustrations and Alessandro Raimondo, Ana Padua and Manuela Cirilli from the Knowledge Transfer Group for their continuous support. Our compliments towards the work and research performed by world leading scientists in this domain: Prof. Manuel Gameiro, Prof. Shelly Miller, Prof. Linsey Marr, Prof. Jose Jimenez, Dr. Lidia Morawska, Prof Yuguo Li et al. their scientific contribution was indispensable for this project.

View file

@ -40,29 +40,6 @@
</div>
<br>
<h2>Authors</h2>
<div class="text-component-text cern_full_html" >
<p>
<h4>Andre Henriques<sup>1</sup>, Luis Aleixo<sup>1</sup>, Marco Andreini<sup>1</sup>, Gabriella Azzopardi<sup>2</sup>, James Devine<sup>3</sup>, Philip Elson<sup>4</sup>, Nicolas Mounet<sup>2</sup>, Markus Kongstein Rognlien<sup>2,6</sup>, Nicola Tarocco<sup>5</sup></h4><br>
<sup>1</sup>HSE Unit, Occupational Health & Safety Group, CERN<br>
<sup>2</sup>Beams Department, Accelerators and Beam Physics Group, CERN<br>
<sup>3</sup>Experimental Physics Department, Safety Office, CERN<br>
<sup>4</sup>Beams Department, Controls Group, CERN<br>
<sup>5</sup>Information Technology Department, Collaboration, Devices & Applications Group, CERN<br>
<sup>6</sup>Norwegian University of Science and Technology (NTNU)<br>
</p>
<br>
<h3>Acknowledgements:</h3>
<p>
We wish to thank CERNs HSE Unit, Beams Department, Experimental Physics Department, Information Technology Department, Industry, Procurement and Knowledge Transfer Department and International Relations Sector for their support to the study.
Thanks to Doris Forkel-Wirth, Benoit Delille, Walid Fadel, Olga Beltramello, Letizia Di Giulio, Evelyne Dho, Wayne Salter, Benoit Salvant and colleagues from the COVID working group for providing expert advice and extensively testing the model.
Finally, we wish to thank Fabienne Landua and the design service for preparing the illustrations and Alessandro Raimondo, Ana Padua and Manuela Cirilli from the Knowledge Transfer Group for their continuous support.
Our compliments towards the work and research performed by world leading scientists in this domain: Prof. Manuel Gameiro, Prof. Shelly Miller, Prof. Linsey Marr, Prof. Jose Jimenez, Dr. Lidia Morawska, Prof Yuguo Li et al. their scientific contribution was indispensable for this project.
<span style="height: 3vh; display: block;"></span>
</p>
</div>
</div>

View file

@ -872,7 +872,7 @@ class ConcentrationModel:
@method_cache
def normed_integrated_concentration(self, start: float, stop: float) -> _VectorisedFloat:
"""
Get the integrated concentration dose between the times start and stop,
Get the integrated concentration of viruses in the air between the times start and stop,
normalized by the emission rate.
"""
if stop <= self._first_presence_time():
@ -901,7 +901,7 @@ class ConcentrationModel:
def integrated_concentration(self, start: float, stop: float) -> _VectorisedFloat:
"""
Get the integrated concentration dose between the times start and stop.
Get the integrated concentration of viruses in the air between the times start and stop.
"""
return (self.normed_integrated_concentration(start, stop) *
self.infected.emission_rate_when_present())

View file

@ -4,7 +4,7 @@ import pytest
import tornado.testing
import cara.apps.calculator
from cara.apps.calculator.report_generator import generate_qr_code
from cara.apps.calculator.report_generator import generate_permalink
_TIMEOUT = 20.
@ -97,26 +97,26 @@ class TestOpenApp(tornado.testing.AsyncHTTPTestCase):
assert response.code == 404
async def test_qrcode_urls(http_server_client, baseline_form):
async def test_permalink_urls(http_server_client, baseline_form):
base_url = 'proto://hostname/prefix'
qr_data = generate_qr_code(base_url, "/calculator", baseline_form)
permalink_data = generate_permalink(base_url, "/calculator", baseline_form)
expected = f'{base_url}/calculator?exposed_coffee_break_option={baseline_form.exposed_coffee_break_option}&'
assert qr_data['link'].startswith(expected)
assert permalink_data['link'].startswith(expected)
# We should get a 200 for the link.
response = await http_server_client.fetch(qr_data['link'].replace(base_url, ''))
response = await http_server_client.fetch(permalink_data['link'].replace(base_url, ''))
assert response.code == 200
# And a 302 for the QR url itself. The redirected URL should be the same as
# in the link.
assert qr_data['qr_url'].startswith(base_url)
assert permalink_data['shortened'].startswith(base_url)
response = await http_server_client.fetch(
qr_data['qr_url'].replace(base_url, ''),
permalink_data['shortened'].replace(base_url, ''),
max_redirects=0,
raise_error=False,
)
assert response.code == 302
assert response.headers['Location'] == qr_data['link'].replace(base_url, '')
assert response.headers['Location'] == permalink_data['link'].replace(base_url, '')
async def test_invalid_compressed_url(http_server_client, baseline_form):

View file

@ -62,7 +62,6 @@ pyparsing==2.4.7
pyrsistent==0.18.0
python-dateutil==2.8.2
pyzmq==22.1.0
qrcode==7.2
requests==2.26.0
requests-unixsocket==0.2.0
scikit-learn==0.24.2

View file

@ -20,7 +20,7 @@ REQUIREMENTS: dict = {
'core': [
'dataclasses; python_version < "3.7"',
'ipykernel',
'ipympl == 0.7.0',
'ipympl != 0.8.0, != 0.8.1',
'ipywidgets',
'Jinja2',
'loky',
@ -30,7 +30,6 @@ REQUIREMENTS: dict = {
'numpy',
'psutil',
'python-dateutil',
'qrcode[pil]',
'scipy',
'sklearn',
'timezonefinder',