import json from typing import Any # Réglages "❓ Boîte à quiz" propres au quiz AUTONOME/plein écran (demande # explicite : "étendre les fonctionnalités de la boîte à quiz pour les RH # qui veulent juste faire un quiz") — DISTINCT de "🎨 Style" # (dialogue_box_style.py, partagé avec dialogue_box/score_widget) : ce # fichier ne gère QUE plein écran/minuteur/modèle, jamais police/couleur. # # DEUX catégories de modèles bien distinctes (demande explicite, précisée # après coup — les 5 premiers modèles construits avant cette précision # étaient tous de la première catégorie) : # - "dialog_template" (modèles "boîte de dialogue", visibles SEULEMENT # quand fullscreen=False) : variantes de forme/couleur/interaction sur # une structure HTML qui reste globalement la même que "Classique". # - "page_template" (modèles "page de quiz", visibles SEULEMENT quand # fullscreen=True) : thème complet et autonome — police, formes, # couleurs ET structure HTML totalement propres à CHAQUE modèle, # aucune structure partagée entre eux ni avec les modèles "boîte". # Les deux réglages sont conservés INDÉPENDAMMENT (jamais écrasés l'un par # l'autre) : activer/désactiver le plein écran change juste lequel des # deux s'applique, sans jamais perdre le choix de l'autre. DEFAULT_QUIZ_BOX_CONFIG: dict[str, Any] = { "fullscreen": False, "timer_mode": "aucun", "timer_seconds": 20, "dialog_template": "defaut", "page_template": "classique", } # "aucun" : pas de minuteur (réglage par défaut, jamais imposé — demande # explicite du créateur). "question" : redémarre à chaque question. # "quiz" : un seul compte à rebours pour l'ensemble du quiz. TIMER_MODES = ["aucun", "question", "quiz"] # "defaut" (Classique) = le CSS/HTML déjà existant, AUCUN changement # visuel pour un quiz non reconfiguré (voir quiz_box_templates.py) — # "Verre dépoli"/"Néon"/"Minimal"/"Ludique" ont été retirés (demande # explicite : "supprime tous les modele de boite sauf le base"). # "manga_dialogue" (Manga) : pendant, en petite carte flottante (jamais # plein écran), du modèle "page de quiz" "Manga" — validé par le créateur # via une maquette interactive avant intégration (voir # game_engine/rendering/quiz_box_templates.py::render_quiz_box_manga_dialogue). # Clé DIFFÉRENTE du modèle "page" "manga" (registre d'effets JS partagé # entre les deux catégories, voir static/game/js/play/quiz-box-templates.js) # — reste proche de la structure de "Classique" (demande explicite : # les modèles "boîte" ne changent que forme/couleur/interaction). QUIZ_BOX_DIALOG_TEMPLATES = ["defaut", "manga_dialogue"] QUIZ_BOX_DIALOG_TEMPLATE_LABELS = { "defaut": "Classique", "manga_dialogue": "Manga", } # "classique" (Classique) : modèle de PAGE DE QUIZ de base — sobre/ # professionnel, reprend les couleurs par défaut du modèle "boîte de # dialogue" de base (demande explicite : "respecte les couleurs du modele # de boite de base pour le modele de page de base") plutôt que sa propre # palette, contrairement à "manga" qui est un thème pleinement autonome. # "manga" : thème/typographie/structure entièrement dédiés, validé par le # créateur via une maquette interactive avant intégration (voir # game_engine/rendering/quiz_box_templates.py::render_quiz_box_page_manga). QUIZ_BOX_PAGE_TEMPLATES = ["classique", "manga"] QUIZ_BOX_PAGE_TEMPLATE_LABELS = { "classique": "Classique", "manga": "Manga", } _MIN_TIMER_SECONDS = 3 _MAX_TIMER_SECONDS = 600 def sanitize_quiz_box_config(raw_config: Any) -> dict[str, Any]: config = dict(DEFAULT_QUIZ_BOX_CONFIG) if not isinstance(raw_config, dict): return config config["fullscreen"] = bool(raw_config.get("fullscreen")) if raw_config.get("timer_mode") in TIMER_MODES: config["timer_mode"] = raw_config["timer_mode"] raw_timer_seconds = raw_config.get("timer_seconds") try: if raw_timer_seconds is None: raise ValueError config["timer_seconds"] = max(_MIN_TIMER_SECONDS, min(_MAX_TIMER_SECONDS, int(raw_timer_seconds))) except (TypeError, ValueError): pass if raw_config.get("dialog_template") in QUIZ_BOX_DIALOG_TEMPLATES: config["dialog_template"] = raw_config["dialog_template"] if raw_config.get("page_template") in QUIZ_BOX_PAGE_TEMPLATES: config["page_template"] = raw_config["page_template"] return config def resolve_quiz_box_config(obj: dict[str, Any]) -> dict[str, Any]: raw = (obj.get("attributes") or {}).get("_quiz_box_config") if not raw: return dict(DEFAULT_QUIZ_BOX_CONFIG) try: parsed = json.loads(raw) except (TypeError, ValueError): return dict(DEFAULT_QUIZ_BOX_CONFIG) return sanitize_quiz_box_config(parsed)