Skip to content

Commit adcbdfb

Browse files
feat: qr routes
1 parent e07268b commit adcbdfb

File tree

1 file changed

+93
-0
lines changed

1 file changed

+93
-0
lines changed

app/modules/dataset/routes.py

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
import shutil
44
import tempfile
55
from datetime import datetime
6+
from io import BytesIO
67

8+
import qrcode
79
from flask import (
810
abort,
911
current_app,
@@ -17,6 +19,9 @@
1719
url_for,
1820
)
1921
from flask_login import current_user, login_required
22+
from PIL import Image, ImageDraw
23+
from qrcode.image.styledpil import StyledPilImage
24+
from qrcode.image.styles.moduledrawers.pil import RoundedModuleDrawer
2025

2126
from app.modules.apikeys.decorators import require_api_key
2227
from app.modules.dataset import dataset_bp
@@ -198,6 +203,94 @@ def download_dataset(dataset_id):
198203
return resp
199204

200205

206+
def _build_dataset_qr_response(dataset: DataSet):
207+
if not dataset.ds_meta_data.dataset_doi:
208+
abort(404, description="QR available only for synchronized datasets with DOI.")
209+
210+
target_url = url_for("dataset.subdomain_index", doi=dataset.ds_meta_data.dataset_doi, _external=True)
211+
qr = qrcode.QRCode(
212+
version=None,
213+
error_correction=qrcode.constants.ERROR_CORRECT_H,
214+
box_size=10,
215+
border=5,
216+
)
217+
qr.add_data(target_url)
218+
qr.make(fit=True)
219+
220+
img = qr.make_image(
221+
image_factory=StyledPilImage,
222+
module_drawer=RoundedModuleDrawer(radius_ratio=0.9),
223+
fill_color="black",
224+
back_color="white",
225+
).convert("RGBA")
226+
227+
logo_path = os.path.join(current_app.root_path, "static", "media", "logos", "uvlhub_ball.png")
228+
if os.path.exists(logo_path):
229+
logo = Image.open(logo_path).convert("RGBA")
230+
qr_width, qr_height = img.size
231+
232+
logo_size = max(40, qr_width // 5)
233+
logo.thumbnail((logo_size, logo_size), Image.Resampling.LANCZOS)
234+
235+
padding = max(4, logo_size // 12)
236+
logo_background = Image.new(
237+
"RGBA",
238+
(logo.width + (2 * padding), logo.height + (2 * padding)),
239+
(0, 0, 0, 0),
240+
)
241+
bg_draw = ImageDraw.Draw(logo_background)
242+
bg_radius = max(6, logo_background.width // 5)
243+
bg_draw.rounded_rectangle(
244+
[(0, 0), (logo_background.width - 1, logo_background.height - 1)],
245+
radius=bg_radius,
246+
fill=(255, 255, 255, 255),
247+
)
248+
249+
rounded_logo = Image.new("RGBA", logo.size, (0, 0, 0, 0))
250+
logo_mask = Image.new("L", logo.size, 0)
251+
logo_mask_draw = ImageDraw.Draw(logo_mask)
252+
logo_radius = max(4, logo.width // 5)
253+
logo_mask_draw.rounded_rectangle(
254+
[(0, 0), (logo.width - 1, logo.height - 1)],
255+
radius=logo_radius,
256+
fill=255,
257+
)
258+
rounded_logo.paste(logo, (0, 0), logo_mask)
259+
260+
bg_position = ((qr_width - logo_background.width) // 2, (qr_height - logo_background.height) // 2)
261+
img.paste(logo_background, bg_position, logo_background)
262+
263+
logo_position = ((qr_width - rounded_logo.width) // 2, (qr_height - rounded_logo.height) // 2)
264+
img.paste(rounded_logo, logo_position, rounded_logo)
265+
266+
img_io = BytesIO()
267+
img.save(img_io, format="PNG")
268+
img_io.seek(0)
269+
270+
return send_file(
271+
img_io,
272+
mimetype="image/png",
273+
as_attachment=True,
274+
download_name=f"dataset_{dataset.id}_qr.png",
275+
)
276+
277+
278+
@dataset_bp.route("/datasets/<int:dataset_id>/qr", methods=["GET"])
279+
@dataset_bp.route("/datasets/<int:dataset_id>/qr/", methods=["GET"])
280+
def dataset_qr_by_id(dataset_id):
281+
dataset = dataset_service.get_or_404(dataset_id)
282+
return _build_dataset_qr_response(dataset)
283+
284+
285+
@dataset_bp.route("/doi/<path:doi>/qr", methods=["GET"])
286+
@dataset_bp.route("/doi/<path:doi>/qr/", methods=["GET"])
287+
def dataset_qr_by_doi(doi):
288+
ds_meta_data = dsmetadata_service.filter_by_doi(doi)
289+
if not ds_meta_data:
290+
abort(404, description="Dataset not found for the given DOI.")
291+
return _build_dataset_qr_response(ds_meta_data.dataset)
292+
293+
201294
@dataset_bp.route("/datasets/download/all", methods=["GET"])
202295
def download_all_dataset():
203296
# Crear un directorio temporal

0 commit comments

Comments
 (0)