Commit 3782cd05 authored by Philip Elson's avatar Philip Elson 🐍 Committed by Luis Aleixo
Browse files

Move QR generation to the browser, saving some time and effort on the server.

parent 22109c0e
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',
......
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();
};
\ No newline at end of file
......@@ -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
......
......@@ -30,7 +30,6 @@ REQUIREMENTS: dict = {
'numpy',
'psutil',
'python-dateutil',
'qrcode[pil]',
'scipy',
'sklearn',
'timezonefinder',
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment