Badges : texte libre saisi dans l'éditeur, champs ramenés dans le badge
Texte libre commun à tous les badges, saisi dans l'éditeur (BadgeField.text, migration 0025) ; le « Texte du badge » de la fiche le remplace pour un bénévole. Champs ramenés à l'intérieur du badge et champ sélectionné au premier plan : la photo restait bloquée hors d'un badge plus étroit que le CR80 (bornes de glisser-déposer négatives). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
6892e0307d
commit
cfed76c239
@@ -280,7 +280,9 @@ def _field_text(
|
||||
if field.field_type == BadgeFieldType.FULL_NAME:
|
||||
return f"{volunteer.first_name} {volunteer.last_name}"
|
||||
if field.field_type == BadgeFieldType.FREE_TEXT:
|
||||
return volunteer.badge_text or ""
|
||||
# Texte commun saisi dans l'éditeur, remplacé pour un bénévole par
|
||||
# le « Texte du badge » de sa fiche s'il en a un.
|
||||
return volunteer.badge_text or field.text or ""
|
||||
assignments = assignments_by_volunteer.get(volunteer.id, [])
|
||||
if field.field_type == BadgeFieldType.POSITIONS:
|
||||
return "\n".join(
|
||||
|
||||
@@ -355,6 +355,8 @@ class BadgeFieldIn(ApiModel):
|
||||
align: BadgeAlign = BadgeAlign.LEFT
|
||||
bold: bool = False
|
||||
color: str = "#000000"
|
||||
# `FREE_TEXT` seulement (voir `BadgeField.text`), ignoré sinon.
|
||||
text: str | None = None
|
||||
|
||||
|
||||
class BadgeFieldsSet(ApiModel):
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
"""Texte commun du champ libre des badges
|
||||
|
||||
Revision ID: 0025_badge_field_text
|
||||
Revises: 0024_volunteer_badge_text
|
||||
Create Date: 2026-10-27
|
||||
|
||||
`badge_field.text` : texte du champ `FREE_TEXT` saisi dans l'éditeur de
|
||||
badges, imprimé sur tous les badges — sauf ceux dont la fiche porte un
|
||||
`volunteer.badge_text`, qui le remplace pour ce bénévole.
|
||||
"""
|
||||
|
||||
from collections.abc import Sequence
|
||||
|
||||
import sqlalchemy as sa
|
||||
from alembic import op
|
||||
|
||||
revision: str = "0025_badge_field_text"
|
||||
down_revision: str | None = "0024_volunteer_badge_text"
|
||||
branch_labels: str | Sequence[str] | None = None
|
||||
depends_on: str | Sequence[str] | None = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
op.add_column("badge_field", sa.Column("text", sa.String(), nullable=True))
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.drop_column("badge_field", "text")
|
||||
@@ -183,8 +183,9 @@ class Volunteer(TimestampMixin, table=True):
|
||||
# cette note sur la fiche et la transforme, si besoin, en un vrai
|
||||
# `VolunteerCompanionWish` une fois les deux bénévoles inscrits.
|
||||
companion_wish_note: str | None = None
|
||||
# Texte libre imprimé sur le badge (champ `BadgeFieldType.FREE_TEXT`),
|
||||
# saisi par la coordination sur la fiche — ex. « Accès backstage ».
|
||||
# Texte du badge propre à ce bénévole (champ `BadgeFieldType.FREE_TEXT`),
|
||||
# saisi par la coordination sur la fiche — ex. « Accès backstage ». Vide :
|
||||
# le texte commun de l'éditeur (`BadgeField.text`) s'applique.
|
||||
badge_text: str | None = None
|
||||
# Cases à cocher du formulaire public — purement informatives : aucune
|
||||
# logique n'y est attachée (pas de workflow de paiement).
|
||||
@@ -466,6 +467,10 @@ class BadgeField(TimestampMixin, table=True):
|
||||
sa_column=Column(Boolean, nullable=False, server_default="false"),
|
||||
)
|
||||
color: str = Field(default="#000000")
|
||||
# Champ `FREE_TEXT` seulement : texte saisi dans l'éditeur, imprimé sur
|
||||
# tous les badges — sauf ceux dont la fiche porte un « Texte du badge »
|
||||
# (`Volunteer.badge_text`), qui le remplace pour ce bénévole.
|
||||
text: str | None = None
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
|
||||
@@ -39,8 +39,9 @@ class BadgeFieldType(enum.StrEnum):
|
||||
ROLE = "ROLE"
|
||||
# Photo du bénévole (`Volunteer.photo`), ajustée au cadre du champ.
|
||||
PHOTO = "PHOTO"
|
||||
# Texte libre propre à chaque bénévole, saisi par la coordination sur sa
|
||||
# fiche (`Volunteer.badge_text`).
|
||||
# Texte libre saisi par la coordination dans l'éditeur (`BadgeField.text`,
|
||||
# commun à tous les badges), remplacé pour un bénévole par le « Texte du
|
||||
# badge » de sa fiche (`Volunteer.badge_text`) s'il en a un.
|
||||
FREE_TEXT = "FREE_TEXT"
|
||||
|
||||
|
||||
|
||||
@@ -221,12 +221,15 @@ def test_generating_with_photo_and_free_text_fields(client, edition, session):
|
||||
{
|
||||
"field_type": "FREE_TEXT", "x_mm": 5, "y_mm": 40,
|
||||
"width_mm": 50, "height_mm": 8, "font_size": 9,
|
||||
"text": "Staff 2026",
|
||||
},
|
||||
]
|
||||
},
|
||||
)
|
||||
assert resp.status_code == 200
|
||||
assert {f["field_type"] for f in resp.json()} == {"PHOTO", "FREE_TEXT"}
|
||||
by_type = {f["field_type"]: f for f in resp.json()}
|
||||
assert set(by_type) == {"PHOTO", "FREE_TEXT"}
|
||||
assert by_type["FREE_TEXT"]["text"] == "Staff 2026"
|
||||
|
||||
with_photo = make_volunteer(session, edition, first_name="Alex", last_name="Martin")
|
||||
with_photo.photo = _PNG
|
||||
|
||||
@@ -111,3 +111,16 @@ def test_field_text_free_text_is_the_volunteer_badge_text():
|
||||
|
||||
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"
|
||||
|
||||
Reference in New Issue
Block a user