Badges : permettre d'agrandir le badge proportionnellement à l'image
Le format déduit de l'image importée (300 dpi supposés, étape
précédente) donne un badge minuscule pour une image à résolution web —
ajout d'un contrôle « largeur du badge (mm) » dans l'écran de
personnalisation : la hauteur suit proportionnellement (ratio de
l'image). PUT /editions/{id}/badges/template/size écrase la taille
déduite de l'image ; la réimporter la recalcule à nouveau.
Vérifié : pytest (250 passés), ruff, tsc --noEmit, eslint, vite build,
smoke — tous verts.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
bc165757ca
commit
55c5d539a8
@@ -22,6 +22,7 @@ from app.api.schemas import (
|
||||
BadgeFieldRead,
|
||||
BadgeFieldsSet,
|
||||
BadgeGenerateRequest,
|
||||
BadgeSizeUpdate,
|
||||
BadgeTemplateRead,
|
||||
)
|
||||
from app.auth.deps import require_coordinator
|
||||
@@ -219,6 +220,28 @@ def set_fields(
|
||||
return _fields(session, edition.id)
|
||||
|
||||
|
||||
@router.put("/template/size", response_model=BadgeTemplateRead)
|
||||
def set_size(
|
||||
payload: BadgeSizeUpdate,
|
||||
edition: Edition = Depends(get_edition),
|
||||
_guard: Account = Depends(require_coordinator),
|
||||
session: Session = Depends(get_session),
|
||||
) -> BadgeTemplateRead:
|
||||
"""Redimensionne le badge indépendamment de l'image (ex. une image
|
||||
importée à basse résolution donne un format minuscule à 300 dpi
|
||||
supposés — l'utilisateur l'agrandit ici). Écrase la taille déduite de
|
||||
l'image ; ré-importer l'image la recalcule à nouveau depuis ses pixels
|
||||
(`set_front_image`)."""
|
||||
if payload.badge_width_mm <= 0 or payload.badge_height_mm <= 0:
|
||||
raise HTTPException(status_code=422, detail={"code": "badge.invalid_size"})
|
||||
template = _get_or_create_template(session, edition.id)
|
||||
template.front_image_width_mm = payload.badge_width_mm
|
||||
template.front_image_height_mm = payload.badge_height_mm
|
||||
session.add(template)
|
||||
session.commit()
|
||||
return _template_read(template, _fields(session, edition.id))
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Génération du PDF #
|
||||
# --------------------------------------------------------------------------- #
|
||||
|
||||
@@ -374,6 +374,15 @@ class BadgeTemplateRead(ApiModel):
|
||||
fields: list[BadgeFieldRead]
|
||||
|
||||
|
||||
class BadgeSizeUpdate(ApiModel):
|
||||
"""Ajuste la taille du badge indépendamment d'un nouvel import d'image —
|
||||
ex. agrandir proportionnellement une image importée trop petite (voir
|
||||
`app/api/badges.py:set_size`). Écrase la taille déduite de l'image."""
|
||||
|
||||
badge_width_mm: float
|
||||
badge_height_mm: float
|
||||
|
||||
|
||||
class BadgeGenerateRequest(ApiModel):
|
||||
# None/absent = tous les bénévoles de l'édition.
|
||||
volunteer_ids: list[uuid.UUID] | None = None
|
||||
|
||||
@@ -60,6 +60,7 @@
|
||||
"badge.empty_image": "L'image est vide.",
|
||||
"badge.image_too_large": "L'image est trop volumineuse.",
|
||||
"badge.duplicate_field_type": "Le champ « {field_type} » ne peut apparaître qu'une fois.",
|
||||
"badge.invalid_size": "La taille du badge doit être strictement positive.",
|
||||
"badge.no_fields": "Aucun champ n'est configuré sur le recto du badge.",
|
||||
"badge.no_volunteers": "Aucun bénévole ne correspond à cette sélection.",
|
||||
"badge.role.referent": "Référent",
|
||||
|
||||
@@ -97,6 +97,37 @@ def test_uploading_an_unsupported_image_type_is_rejected(client, edition):
|
||||
assert resp.json()["detail"]["code"] == "badge.image_type_not_supported"
|
||||
|
||||
|
||||
def test_resizing_the_badge_scales_it_independently_of_the_image(client, edition):
|
||||
client.put(
|
||||
f"{_badges_url(edition)}/template/front-image",
|
||||
files={"file": ("front.png", _PNG, "image/png")},
|
||||
)
|
||||
# Image 1×1 px à 300 dpi supposés ⇒ badge minuscule (~0,08 mm) : on
|
||||
# l'agrandit à un format exploitable, en conservant le ratio (ici 1:1).
|
||||
resp = client.put(
|
||||
f"{_badges_url(edition)}/template/size",
|
||||
json={"badge_width_mm": 90, "badge_height_mm": 90},
|
||||
)
|
||||
assert resp.status_code == 200
|
||||
body = resp.json()
|
||||
assert body["badge_width_mm"] == 90
|
||||
assert body["badge_height_mm"] == 90
|
||||
|
||||
# Persisté : une relecture du gabarit renvoie toujours la taille agrandie.
|
||||
again = client.get(f"{_badges_url(edition)}/template").json()
|
||||
assert again["badge_width_mm"] == 90
|
||||
assert again["badge_height_mm"] == 90
|
||||
|
||||
|
||||
def test_resizing_the_badge_to_a_non_positive_size_is_rejected(client, edition):
|
||||
resp = client.put(
|
||||
f"{_badges_url(edition)}/template/size",
|
||||
json={"badge_width_mm": 0, "badge_height_mm": 54},
|
||||
)
|
||||
assert resp.status_code == 422
|
||||
assert resp.json()["detail"]["code"] == "badge.invalid_size"
|
||||
|
||||
|
||||
def test_setting_the_same_field_type_twice_is_rejected(client, edition):
|
||||
resp = client.put(
|
||||
f"{_badges_url(edition)}/template/fields",
|
||||
|
||||
Reference in New Issue
Block a user