Merge branch 'master' into feature/concentration_cumulative_dose
This commit is contained in:
commit
f3d23cf405
12 changed files with 57 additions and 111 deletions
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
};
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -145,7 +145,6 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="break-avoid fourth_page"></div>
|
||||
{% endblock report_preamble %}
|
||||
|
||||
{% block report_footer %}
|
||||
|
|
|
|||
|
|
@ -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'] }}
|
||||
|
|
|
|||
|
|
@ -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 CERN’s 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.
|
||||
|
|
|
|||
|
|
@ -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 CERN’s 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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
3
setup.py
3
setup.py
|
|
@ -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',
|
||||
|
|
|
|||
Loading…
Reference in a new issue