Files
Forge-Engine/document_engine/labels/labels.md
T
williamandClaude Sonnet 5 ce750ec697 Implemente le mini-jeu Association (glisser-deposer par paires)
document_engine/labels/association_config.py (nouveau) : modele de
donnees, meme convention resolve_X/sanitize_X que quiz_config.py -
DEFAULT_ASSOCIATION_CONFIG, sanitize_association_config (chaque paire
doit avoir ses deux cotes non vides, sinon supprimee silencieusement ;
liste tronquee a MAX_PAIRS=8).

Cote serveur, routes/document/document_element_update.py revalide
desormais aussi l'association avant persistance (meme raisonnement que
pour le quiz). Le rendu (_render_association) affiche un resume reel
(nombre de paires) et, des qu'au moins une paire existe, un plateau de
glisser-deposer REELEMENT interactif en Mode Apercu
(_render_association_player) : les deux colonnes (termes/
correspondances) sont melangees independamment (random.shuffle,
melange d'affichage documente dans CODE_QUALITY.md) puis embarquees en
JSON dans un attribut data-assoc-config.

Cote editeur, le panneau Proprietes d'une association ("relier
visuellement deux champs qui vont ensemble") est une liste de paires
repetable, chaque ligne reliant visuellement un champ "Element" et un
champ "Correspondance" par un glyphe ↔. Le plateau jouable en Apercu
(static/document/js/document-editor.js) supporte deux facons de jouer,
toutes deux reelles : glisser-deposer HTML5 natif, ou cliquer une
carte puis son emplacement (repli pour les appareils sans support
fiable du drag) - bonne association verrouillee en vert, mauvaise
signalee puis reinitialisee, ecran de resultat une fois toutes les
paires associees.

Bug reel trouve ET corrige via simulation DOM complete (glisser-
deposer + clic simules, pas juste un chargement de page) : le
feedback visuel reutilisait la classe CSS du quiz via une
reaffectation de className qui supprimait au passage la classe
d'identite docAssocFeedback, rendant l'element introuvable des le
premier essai de match (aurait plante en usage reel des la premiere
tentative). Corrige en gardant toujours les deux classes ensemble.

Tests : 8 tests purs (tests/document/test_association_config.py, sans
Flask) + 1 test de route verifiant la sanitization a l'ecriture.

SKIP=djlint : backlog H021 pre-existant, aucun template touche ici.
ruff/mypy --strict/vulture/bandit/import-linter/eslint/stylelint tous
verts ; 50 tests document verifies frais. Verification manuelle live
complete : ajout, sanitization sur paire invalide, rendu du plateau,
et simulation DOM du gameplay reel (glisser-deposer correct/incorrect,
clic-selection, progression, ecran de resultat) - script de
diagnostic non conserve dans le depot.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-20 13:01:21 +02:00

5.2 KiB

document_engine/labels/

Catalogue statique des types d'éléments du support de formation : bibliothèque groupée par catégorie (panneau gauche de l'éditeur), libellés d'affichage, et attributs par défaut posés à la création de chaque type.

SHAPE_KINDS: tuple[str, ...]

("rectangle", "cercle", "triangle", "trait") — couche de formes libres, toujours en position absolue (parent_id = NULL).

CONTENT_KINDS: tuple[str, ...]

("titre", "paragraphe", "image", "bouton") — éléments du flux, peuvent être top-niveau ou enfants d'une rangée.

MINIGAME_KINDS: tuple[str, ...]

("quiz", "association", "memory", "mots", "scenario", "zones") — "quiz" et "association" sont implémentés (voir quiz_config.py/ association_config.py ci-dessous) ; les 4 autres gardent un panneau Propriétés minimal (emplacement réservé), formulaires de contenu dédiés hors périmètre de cette passe.

ELEMENT_LIBRARY: dict[str, dict[str, Any]]

Bibliothèque affichée dans le panneau gauche, groupée par catégorie (mise_en_page / contenu / minigames), chaque entrée portant un label et sa liste de kinds. "row" n'y apparaît jamais — créé implicitement par le moteur de layout, jamais choisi directement dans la bibliothèque.

ELEMENT_KIND_LABELS: dict[str, str]

Libellé d'affichage pour chaque kind, y compris "row" (pour l'affichage/debug hors bibliothèque).

element_default_attributes(kind: str) -> dict[str, Any]

Attributs posés à la création d'un élément de ce type (voir document_engine/elements/add_document_element.py).

  • Retour : un dict d'attributs par défaut, dépendant du kind : formes (x/y/width/height/rotation/z_index/fill/stroke/stroke_width/label), texte (content/style + bold/italic/underline/align/color), image (src/alt), bouton (label/target), rangée (gap/align/justify), quiz (DEFAULT_QUIZ_CONFIG, voir quiz_config.py), association (DEFAULT_ASSOCIATION_CONFIG, voir association_config.py), autre mini-jeu (theme_color), ou {} pour un kind inconnu.
  • Exceptions : aucune.

quiz_config.py — modèle de données du mini-jeu Quiz

Seul mini-jeu réellement implémenté (les autres MINIGAME_KINDS restent un emplacement réservé). Même convention resolve_X/sanitize_X que game_engine/rendering/quiz_box_config.py côté jeu, mais sans aucun import croisé (voir docs/plan/PLAN.md).

MIN_CHOICES, MAX_CHOICES, MIN_TIMER_SECONDS, MAX_TIMER_SECONDS, DEFAULT_TIMER_SECONDS: int

Bornes de validation (2/4 choix, 5/300 secondes, 30 par défaut).

DEFAULT_QUIZ_CONFIG: dict[str, Any]

{"theme_color": "#ff5f2e", "timer_enabled": False, "timer_seconds": 30, "questions": []}.

sanitize_quiz_config(raw_config: Any) -> dict[str, Any]

Valide/nettoie une config de quiz arbitraire (JSON venu du client) — jamais lève, renvoie toujours un dict COMPLET fusionné sur DEFAULT_QUIZ_CONFIG. Chaque question de raw_config["questions"] est validée indépendamment (voir _sanitize_question, privée) : texte non vide, au moins 2 choix non vides (tronqués à 4), correct_index remis à 0 s'il est absent/hors bornes/non-entier (un bool — qui est un int en Python — est explicitement rejeté), points codé en entier ≥ 0. Une question invalide est silencieusement supprimée de la liste (jamais une levée qui ferait échouer tout le reste du quiz).

  • Retour : dict complet (mêmes clés que DEFAULT_QUIZ_CONFIG).
  • Exceptions : aucune.

quiz_total_points(config: dict[str, Any]) -> int

Somme du points de toutes les questions d'une config déjà sanitizée — mirroir de db/dialogue_lines.py::sum_question_rewards côté jeu, utile le jour où un export calculera un score maximum.

  • Retour : entier ≥ 0.
  • Exceptions : aucune.

association_config.py — modèle de données du mini-jeu Association

Deuxième mini-jeu implémenté : l'apprenant relie chaque carte de gauche ("terme") à sa correspondance de droite ("définition") par glisser-déposer (voir document_engine/rendering/render_document_element.py:: _render_association_player). Même convention que quiz_config.py.

MIN_PAIRS, MAX_PAIRS: int

Bornes de validation (2/8 paires). MIN_PAIRS n'est pas imposé par sanitize_association_config (une seule paire valide reste acceptée, comme un quiz à une seule question) — c'est une recommandation pour le panneau Propriétés, pas une contrainte technique du rendu.

DEFAULT_ASSOCIATION_CONFIG: dict[str, Any]

{"theme_color": "#ff5f2e", "pairs": []}.

sanitize_association_config(raw_config: Any) -> dict[str, Any]

Valide/nettoie une config d'association arbitraire (JSON venu du client) — jamais ne lève, renvoie toujours un dict COMPLET fusionné sur DEFAULT_ASSOCIATION_CONFIG. Chaque paire de raw_config["pairs"] est validée indépendamment (voir _sanitize_pair, privée) : les deux côtés (left/right) doivent être non vides une fois .strip()-és, sinon la paire entière est silencieusement supprimée de la liste (jamais une levée qui ferait échouer tout le reste du mini-jeu). La liste finale est tronquée à MAX_PAIRS.

  • Retour : dict complet (mêmes clés que DEFAULT_ASSOCIATION_CONFIG).
  • Exceptions : aucune.