Implemente le mini-jeu Scenario (situation, choix, consequences)
Build and deploy / test-python (push) Failing after 59s
Build and deploy / test-js (push) Successful in 50s
Build and deploy / lint-python (push) Failing after 59s
Build and deploy / lint-js (push) Failing after 1m1s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Build and deploy / sonarqube (push) Failing after 59s

Cinquieme mini-jeu du support de formation : le createur ecrit une
situation initiale, definit 2 a 4 choix, indique lequel est le bon, et
redige la consequence de chaque choix. Plusieurs scenarios peuvent etre
crees, joues dans l'ordre d'ecriture (jamais melanges, contrairement a
Association/Memory/Mots meles : ce sont des mises en situation
sequentielles). En Apercu, l'apprenant lit la situation, choisit une
option, decouvre la consequence de SON choix et si c'etait le bon, puis
passe au scenario suivant. Comme Association/Memory/Mots meles, le
dernier scenario reste affiche une fois repondu : seul le bouton
Recommencer apparait, jamais un ecran de resultat separe (reserve au
Quiz). Reutilise les classes visuelles du Quiz (docQuizOptions/
docQuizFeedback/docQuizNextBar) plutot que de dupliquer ces regles.

Verifie via simulation DOM reelle (jsdom) : progression entre plusieurs
scenarios, choix correct/incorrect avec revelation de la bonne reponse,
comportement de fin de partie, panneau Proprietes (ajout/suppression de
scenario, changement du nombre de choix, selection du bon choix).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
william
2026-09-20 16:50:10 +02:00
co-authored by Claude Sonnet 5
parent d8be80ebd1
commit b2292de122
10 changed files with 669 additions and 23 deletions
@@ -458,6 +458,62 @@ def _render_mots(el: dict[str, Any], _children_by_parent: dict[int | None, list[
)
def _render_scenario_player(config: dict[str, Any]) -> str:
"""Mise en situation RÉELLEMENT interactive — affichée uniquement en
Mode Aperçu, même principe que les autres mini-jeux : aucun
aller-retour serveur, tout le déroulé (lecture de la situation, choix,
conséquence révélée, scénario suivant) est géré par
static/document/js/document-editor.js à partir du JSON embarqué. Les
scénarios gardent l'ORDRE d'écriture du créateur (contrairement à
l'Association/Memory/Mots mêlés, jamais mélangés) : ce sont des mises
en situation séquentielles, pas des éléments à faire correspondre ou
retrouver — un mélange n'aurait ici aucun sens pédagogique. Réutilise
les classes visuelles du Quiz (.docQuizOptions/.docQuizFeedback/
.docQuizNextBar/.docQuizQuestionText) plutôt que de dupliquer ces
règles, même esprit que .docAssocCard/.docMinigameRestartBar."""
config_json = html_lib.escape(json.dumps({"scenarios": config["scenarios"]}), quote=True)
return (
f'<div class="docScenarioPlayer" data-scenario-config="{config_json}">'
f'<div class="docAssocCard">'
f'<div class="docQuizKicker">Scénario</div>'
f'<div class="docAssocMeta"><span class="docScenarioProg"></span></div>'
f'<div class="docScenarioSituation docQuizQuestionText"></div>'
f'<div class="docScenarioChoices docQuizOptions"></div>'
f'<div class="docScenarioConsequence docQuizFeedback"></div>'
f'<div class="docQuizNextBar">'
f'<button type="button" class="docQuizNextBtn docScenarioNextBtn">Scénario suivant</button>'
f"</div>"
# Même choix que l'Association/Memory/Mots mêlés : le dernier
# scénario reste affiché une fois répondu, seul ce bouton
# apparaît (jamais un écran de résultat séparé — ça reste le
# comportement du Quiz).
f'<div class="docMinigameRestartBar">'
f'<button type="button" class="docScenarioRestartBtn">Recommencer</button>'
f"</div>"
f"</div></div>"
)
def _render_scenario(el: dict[str, Any], _children_by_parent: dict[int | None, list[dict[str, Any]]]) -> str:
from ..labels.scenario_config import sanitize_scenario_config
config = sanitize_scenario_config(el["attributes"])
theme_color = html_lib.escape(str(config["theme_color"]))
scenario_count = len(config["scenarios"])
scenario_label = "scénario" if scenario_count <= 1 else "scénarios"
player_html = _render_scenario_player(config) if config["scenarios"] else ""
return (
f'<div class="docMinigame" data-element-id="{el["id"]}" data-kind="scenario" '
f'style="border-color:{theme_color};">'
f'<div class="docMinigameBadge">'
f'<span class="docMinigameLabel">Scénario</span>'
f'<span class="docMinigamePlaceholder">{scenario_count} {scenario_label}</span>'
f"</div>"
f"{player_html}"
f"</div>"
)
def _render_unknown(el: dict[str, Any], _children_by_parent: dict[int | None, list[dict[str, Any]]]) -> str:
return f'<div class="docUnknown" data-element-id="{el["id"]}">Type inconnu : {html_lib.escape(el["kind"])}</div>'
@@ -476,6 +532,6 @@ _RENDERERS = {
"association": _render_association,
"memory": _render_memory,
"mots": _render_mots,
"scenario": _render_minigame_placeholder,
"scenario": _render_scenario,
"zones": _render_minigame_placeholder,
}