Créateur de badges refait d'après la maquette : champs sur le recto et le verso (BadgeField.face, verso placé en miroir dans le PDF pour le recto-verso), fond de zone, réduction automatique du texte, photo ovale, initiales sans photo, nom sur deux lignes (migration 0026) ; éditeur en trois colonnes (champs, aperçu recto/verso avec de vrais bénévoles, inspecteur), formats prédéfinis, annuler, flèches, zoom, repères de découpe, dépôt d'image sur l'aperçu, contrôle de tous les badges avant génération. Nombre de badges par bénévole (Volunteer.badge_count, 1 par défaut, migration 0027) : champ sur la fiche, autant d'exemplaires sur les planches, total affiché à la génération, colonne dans l'export Excel. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
125 lines
4.3 KiB
Python
125 lines
4.3 KiB
Python
"""Texte résolu des champs du badge — fonctions pures (pas de base)."""
|
||
|
||
import datetime as dt
|
||
import uuid
|
||
|
||
from app.api.badges import _field_text, _positions_table
|
||
from app.models import (
|
||
BadgeField,
|
||
BadgeFieldType,
|
||
Position,
|
||
PositionCategory,
|
||
Shift,
|
||
Volunteer,
|
||
)
|
||
|
||
|
||
def _volunteer(**kw) -> Volunteer:
|
||
return Volunteer(id=uuid.uuid4(), edition_id=uuid.uuid4(), **kw)
|
||
|
||
|
||
def _position(**kw) -> Position:
|
||
return Position(id=uuid.uuid4(), edition_id=uuid.uuid4(), category=PositionCategory.BAR, **kw)
|
||
|
||
|
||
def _shift(start: dt.datetime, end: dt.datetime) -> Shift:
|
||
return Shift(
|
||
id=uuid.uuid4(), edition_id=uuid.uuid4(), position_id=uuid.uuid4(),
|
||
starts_at=start, ends_at=end,
|
||
)
|
||
|
||
|
||
_BOUNDARY = dt.time(2, 0) # journée de festival de 02:00 à 02:00
|
||
|
||
|
||
def test_positions_table_groups_shifts_under_their_day():
|
||
accueil = (
|
||
_position(name="Accueil"),
|
||
_shift(dt.datetime(2026, 7, 11, 9, 0), dt.datetime(2026, 7, 11, 13, 0)),
|
||
)
|
||
bar = (
|
||
_position(name="Bar central"),
|
||
_shift(dt.datetime(2026, 7, 11, 19, 0), dt.datetime(2026, 7, 11, 22, 0)),
|
||
)
|
||
demontage = (
|
||
_position(name="Démontage"),
|
||
_shift(dt.datetime(2026, 7, 12, 14, 0), dt.datetime(2026, 7, 12, 18, 0)),
|
||
)
|
||
|
||
table = _positions_table([accueil, bar, demontage], _BOUNDARY)
|
||
|
||
assert [(s.title, s.rows) for s in table.sections] == [
|
||
("Samedi 11", (("09:00–13:00", "Accueil"), ("19:00–22:00", "Bar central"))),
|
||
("Dimanche 12", (("14:00–18:00", "Démontage"),)),
|
||
]
|
||
|
||
|
||
def test_positions_table_keeps_a_night_shift_on_the_day_it_started():
|
||
night = (
|
||
_position(name="Bar central"),
|
||
_shift(dt.datetime(2026, 7, 12, 0, 0), dt.datetime(2026, 7, 12, 1, 0)),
|
||
)
|
||
|
||
table = _positions_table([night], _BOUNDARY)
|
||
|
||
assert table.sections[0].title == "Samedi 11"
|
||
assert table.sections[0].rows == (("00:00–01:00", "Bar central"),)
|
||
|
||
|
||
def test_positions_table_is_empty_without_any_shift():
|
||
assert _positions_table([], _BOUNDARY) is None
|
||
|
||
|
||
def test_field_text_role_is_referent_only_for_lead_accounts():
|
||
volunteer = _volunteer(first_name="Sam", last_name="Nuit")
|
||
field = BadgeField(
|
||
edition_id=uuid.uuid4(), field_type=BadgeFieldType.ROLE,
|
||
x_mm=0, y_mm=0, width_mm=10, height_mm=10, font_size=9,
|
||
)
|
||
|
||
assert _field_text(field, volunteer, {}, lead_volunteer_ids=set()) == "Bénévole"
|
||
assert _field_text(field, volunteer, {}, lead_volunteer_ids={volunteer.id}) == "Référent"
|
||
|
||
|
||
def test_field_text_free_text_is_the_volunteer_badge_text():
|
||
field = BadgeField(
|
||
edition_id=uuid.uuid4(), field_type=BadgeFieldType.FREE_TEXT,
|
||
x_mm=0, y_mm=0, width_mm=10, height_mm=10, font_size=8, color="#000000",
|
||
)
|
||
with_text = _volunteer(first_name="Alex", last_name="Martin", badge_text="Accès backstage")
|
||
without = _volunteer(first_name="Sam", last_name="Nuit")
|
||
|
||
assert _field_text(field, with_text, {}, set()) == "Accès backstage"
|
||
assert _field_text(field, without, {}, set()) == ""
|
||
|
||
|
||
def test_field_text_free_text_falls_back_to_the_common_editor_text():
|
||
field = BadgeField(
|
||
edition_id=uuid.uuid4(), field_type=BadgeFieldType.FREE_TEXT,
|
||
x_mm=0, y_mm=0, width_mm=10, height_mm=10, font_size=8, color="#000000",
|
||
text="Staff 2026",
|
||
)
|
||
own = _volunteer(first_name="Alex", last_name="Martin", badge_text="Accès backstage")
|
||
other = _volunteer(first_name="Sam", last_name="Nuit")
|
||
|
||
assert _field_text(field, own, {}, set()) == "Accès backstage"
|
||
assert _field_text(field, other, {}, set()) == "Staff 2026"
|
||
|
||
|
||
def test_field_text_full_name_is_first_name_then_last_name_on_two_lines():
|
||
volunteer = _volunteer(first_name="Alex", last_name="Martin")
|
||
field = BadgeField(
|
||
edition_id=uuid.uuid4(), field_type=BadgeFieldType.FULL_NAME,
|
||
x_mm=0, y_mm=0, width_mm=10, height_mm=10, font_size=9,
|
||
)
|
||
assert _field_text(field, volunteer, {}, set()) == "Alex\nMartin"
|
||
|
||
|
||
def test_field_text_photo_is_the_initials_used_without_a_photo():
|
||
volunteer = _volunteer(first_name="alex", last_name="martin")
|
||
field = BadgeField(
|
||
edition_id=uuid.uuid4(), field_type=BadgeFieldType.PHOTO,
|
||
x_mm=0, y_mm=0, width_mm=10, height_mm=10, font_size=9,
|
||
)
|
||
assert _field_text(field, volunteer, {}, set()) == "AM"
|