Move QR generation to the browser, saving some time and effort on the server.
This commit is contained in:
parent
22109c0e04
commit
dfc66991f4
6 changed files with 24 additions and 62 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
|
||||
|
|
@ -123,7 +121,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
|
||||
|
|
@ -136,20 +134,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,
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -313,7 +300,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',
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
};
|
||||
|
|
@ -24,8 +24,6 @@
|
|||
<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 }}"/>
|
||||
</div>
|
||||
|
||||
{% endblock report_header %}
|
||||
|
|
@ -162,10 +160,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>
|
||||
|
|
@ -433,11 +431,19 @@
|
|||
</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}
|
||||
);
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
1
setup.py
1
setup.py
|
|
@ -30,7 +30,6 @@ REQUIREMENTS: dict = {
|
|||
'numpy',
|
||||
'psutil',
|
||||
'python-dateutil',
|
||||
'qrcode[pil]',
|
||||
'scipy',
|
||||
'sklearn',
|
||||
'timezonefinder',
|
||||
|
|
|
|||
Loading…
Reference in a new issue