KutsumKutsum
Accueil
Utilisation de l'appli
Création de questions
Installation
Détails techniques
Accueil
Utilisation de l'appli
Création de questions
Installation
Détails techniques
  • Détails techniques (utilisateurs avancés seulement)

    • Détails techniques (utilisateurs avancés seulement)
    • Architecture générale
    • 🖼️ Gestion des images
    • Base de données
    • Système de scoring
    • Services backend
    • API REST
    • Configuration et environnement
    • Tests et qualité
    • Tests E2E (Playwright) — runbook
    • Tests backend (Jest)
    • Tests frontend (Jest)
    • Déploiement et DevOps
    • Security Documentation
    • Performance & Monitoring
    • Troubleshooting Guide
    • Moodle et LTI 1.3
    • Multi-tenant Kutsum
    • Éditeur de Questions pour Enseignants
    • Landing Page Variants (App)
    • Types de questions et flux de correction
    • Compilation du validationConfig MathALÉA

Types de questions et flux de correction

Cette page documente comment les différents types de questions sont gérés de bout en bout : validation de la réponse, événements socket, état frontend, et rendu du feedback.

Types de questions

TypequestionTypeDonnéesValidation
Choix uniquesingleChoicemultipleChoiceQuestion.answerOptions + correctAnswers: boolean[]Comparaison directe d'index
Choix multiplemultipleChoiceidemScoring partiel
NumériquenumericnumericQuestion.correctAnswer + tolerance±tolérance
MathématiquemathmathQuestion.targetLatexCAS via checkMathAnswer
Texte libretexttextQuestion.expectedAnswer + validationConfigFuzzy match via validateTextAnswer

Validation des réponses

Choix unique / multiple

Validation par comparaison booléenne directe côté backend (gameAnswer.ts). Le score partiel est calculé par ScoringService selon la formule max(0, C_B/B - C_M/M).

Numérique

Comparaison avec tolérance absolue. Pas de mathValidationStatus.

Mathématique (math)

La validation est faite par checkMathAnswer (service CAS). Elle retourne un mathValidationStatus parmi :

StatutSignification
CORRECTRéponse correcte
CORRECT_VALUE_WRONG_FORMValeur correcte, forme imposée non respectée
INCORRECTRéponse incorrecte
INVALID_INPUTSaisie invalide (ne peut pas être parsée)
AMBIGUOUSRésultat ambigu

La réponse de référence est mathQuestion.targetLatex (LaTeX).

Texte libre (text)

La validation est faite par validateTextAnswer (shared util). Elle retourne aussi un mathValidationStatus qui réutilise les mêmes valeurs que pour les questions math. La réponse de référence est textQuestion.expectedAnswer (chaîne de caractères). La validationConfig contrôle : caseSensitive, ignoreDiacritics, maxDistance (Levenshtein), allowSubstring.

Flux des événements socket selon le mode de jeu

Quiz en direct (playMode: tournament)

Student                          Backend                        Teacher
   |                                |                               |
   |---- GAME_ANSWER --------------->|                               |
   |                                |-- (stores validationStatus     |
   |                                |   to Redis per user)           |
   |<--- ANSWER_RECEIVED -----------|                               |
   |     { questionUid, timeSpent } |  (NO correctAnswers, NO       |
   |     (no validation status)     |   mathValidationStatus)        |
   |                                |                               |
   |                     Teacher clicks "Show answers"              |
   |                                |<--- SHOW_CORRECT_ANSWERS ------|
   |<--- CORRECT_ANSWERS -----------|                               |
   |     {                          |                               |
   |       questionUid,             |                               |
   |       correctAnswers: [...],   |  ← per question type (see     |
   |       mathCorrectAnswer?,      |    table below)               |
   |       mathValidationStatus?,   |  ← personalized from Redis    |
   |     }                          |                               |

Contenu de correctAnswers et mathCorrectAnswer par type :

TypecorrectAnswersmathCorrectAnswer
Choix unique/multipleboolean[]non défini
Numérique[number]non défini
Mathématiquenon définistring (LaTeX)
Texte libre[string] (expectedAnswer)non défini

Point clé : pour les questions texte, la réponse attendue est dans correctAnswers[0] (un string), pas dans mathCorrectAnswer.

Mode pratique (playMode: practice) via usePracticeSession

Student                         Backend
   |                               |
   |--- SUBMIT_ANSWER ------------->|
   |                               |--- (validate + compute status)
   |<-- PRACTICE_ANSWER_FEEDBACK --|
   |    {                          |
   |      mathValidationStatus,    |
   |      mathCorrectAnswer?,      |  ← pour les questions math
   |      textCorrectAnswer?,      |  ← pour les questions texte
   |      explanation,             |  ← générée par buildValidationFeedbackExplanation
   |    }                          |

textCorrectAnswer est textQuestion.expectedAnswer (défini dans practiceSessionService.ts).

buildValidationFeedbackExplanation(status) retourne :

  • CORRECT_VALUE_WRONG_FORM → "La valeur est correcte mais la forme demandée n'est pas respectée."
  • INCORRECT → "La réponse est incorrecte."
  • AMBIGUOUS → "Résultat ambigu : la réponse est proche de la réponse attendue."
  • INVALID_INPUT → "La réponse saisie est invalide."
  • sinon → undefined

Mode pratique via gameAnswer.ts (jeu en live mode pratique)

Pour les questions texte :

correctAnswersData = {
  correctAnswers: [textQuestion.expectedAnswer]
}

Le mathValidationStatus est ajouté en dehors de correctAnswersData. La réponse attendue atterrit dans correctAnswers[0], pas dans mathCorrectAnswer.

État frontend (useStudentGameSocket.ts)

À la réception de CORRECT_ANSWERS, le game state est mis à jour ainsi :

{
  phase: 'show_answers',
  correctAnswers: payload.correctAnswers || prev.correctAnswers,
  mathCorrectAnswer: payload.mathCorrectAnswer || prev.mathCorrectAnswer,
  lastAnswerFeedback: payload.mathValidationStatus
    ? { ...prev.lastAnswerFeedback, mathValidationStatus: payload.mathValidationStatus }
    : prev.lastAnswerFeedback
}

Pour les questions texte en quiz en direct :

  • gameState.correctAnswers[0] = la réponse attendue (string)
  • gameState.mathCorrectAnswer = undefined (jamais défini pour le texte)
  • gameState.lastAnswerFeedback.mathValidationStatus = 'CORRECT' | 'INCORRECT' | ... (récupéré depuis Redis via getStudentAnswerFeedback)

Props QuestionCard pour chaque type

PropChoixNumériqueMathTexte (quiz)Texte (pratique)
correctAnswersboolean[][number]non défini[string]non défini
mathFeedbackCorrectAnswer——LaTeX stringundefinedtextCorrectAnswer
mathFeedbackStatus——enumenumenum
showMathInlineFeedbackfalsetruetruetruetrue

Rendu des feedbacks dans QuestionCard

Questions à choix

Icônes ✓/✗ affichées directement sur chaque option à l'aide de correctAnswers[idx] === true. Calcul de statPercent depuis stats.stats[idx] pour la barre de progression.

Questions numériques

Gérées par renderMathOrNumericInput avec keyboardType: 'basic'. Le badge AnswerFeedbackBadge est positionné en absolu à droite de l'input (transform: translate(50%, -50%)). Un panneau de détails sous l'input affiche le statut et la réponse attendue via numericCorrectAnswer.

Questions mathématiques

Gérées par renderMathOrNumericInput avec keyboardType: 'advanced'. Badge absolu sur le bord droit de l'input. Panneau de détails avec mathFeedbackCorrectAnswer rendu via MathJaxWrapper. La projection affiche la réponse avec \( ... \).

Questions texte

Le feedback est contrôlé par shouldShowTextFeedback :

const shouldShowTextFeedback =
  isTextQuestion &&
  (
    (showMathInlineFeedback && (!!mathFeedbackStatus || !!mathFeedbackExplanation))
    || (readonly && correctAnswers && correctAnswers.length > 0)
  );

Structure du rendu :

  1. <input> dans un div.relative.w-full
  2. Badge AnswerFeedbackBadge (CORRECT/INCORRECT) — à positionner en absolu, comme pour math
  3. Panneau de détails : statut + explication + "Réponse attendue"

Issue connue : mathFeedbackCorrectAnswer n'est pas défini pour les questions texte en quiz en direct. La réponse attendue est dans correctAnswers[0]. Le panneau de détails doit se rabattre sur correctAnswers[0] quand mathFeedbackCorrectAnswer est absent.

Page de projection — statistiques

QCM

Les stats sont dans currentStats.stats (tableau de pourcentages, indexé par option). Le QuestionCard les reçoit via la prop stats et affiche une barre de progression par option.

Numérique

StatisticsChart (histogramme Plotly) est rendu sous le QuestionCard, visible uniquement quand showStats === true et currentStats.type === 'numeric'.

Mathématique

MathStatisticsDisplay est rendu sous le QuestionCard quand showStats === true et currentStats.type === 'math'. Affiche les compteurs par statut (CORRECT, INCORRECT, etc.) et un tableau des réponses les plus fréquentes.

Texte libre

TextStatisticsDisplay est rendu sous le QuestionCard quand showStats === true et currentStats.type === 'text'. Même structure que MathStatisticsDisplay : barres de progression par réponse, avec coloration selon le statut quand revealCorrectness === true.

Statistiques de type TextStats (payload socket)

type TextStats = {
  type: 'text';
  isIndicative?: boolean;
  totalAnswers: number;
  totalParticipants: number;
  statusCounts: Record<TextStatus, number>;
  topAnswers: Array<{ text: string; count: number; status: TextStatus }>;
  otherCount: number;
};

Envoyé via l'événement projection_show_stats (shared type projectionShowStats.ts).

Stories Ladle de référence

URLComposantÀ tester
datadisplay--statsvisualization--playgroundTextStatisticsDisplay / MathStatisticsDisplayTous les scénarios text/math
datadisplay--questioncard--correct-answers-reveal-text-wrongQuestionCardBadge + panneau INCORRECT texte
datadisplay--questioncard--correct-answers-reveal-text-correctQuestionCardBadge + panneau CORRECT texte
Dernière mise à jour: 15/05/2026 15:42
Contributors: alexisflesch
Prev
Landing Page Variants (App)
Next
Compilation du validationConfig MathALÉA