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
| Type | questionType | Données | Validation |
|---|---|---|---|
| Choix unique | singleChoice | multipleChoiceQuestion.answerOptions + correctAnswers: boolean[] | Comparaison directe d'index |
| Choix multiple | multipleChoice | idem | Scoring partiel |
| Numérique | numeric | numericQuestion.correctAnswer + tolerance | ±tolérance |
| Mathématique | math | mathQuestion.targetLatex | CAS via checkMathAnswer |
| Texte libre | text | textQuestion.expectedAnswer + validationConfig | Fuzzy 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 :
| Statut | Signification |
|---|---|
CORRECT | Réponse correcte |
CORRECT_VALUE_WRONG_FORM | Valeur correcte, forme imposée non respectée |
INCORRECT | Réponse incorrecte |
INVALID_INPUT | Saisie invalide (ne peut pas être parsée) |
AMBIGUOUS | Ré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 :
| Type | correctAnswers | mathCorrectAnswer |
|---|---|---|
| Choix unique/multiple | boolean[] | non défini |
| Numérique | [number] | non défini |
| Mathématique | non défini | string (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 dansmathCorrectAnswer.
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 viagetStudentAnswerFeedback)
Props QuestionCard pour chaque type
| Prop | Choix | Numérique | Math | Texte (quiz) | Texte (pratique) |
|---|---|---|---|---|---|
correctAnswers | boolean[] | [number] | non défini | [string] | non défini |
mathFeedbackCorrectAnswer | — | — | LaTeX string | undefined | textCorrectAnswer |
mathFeedbackStatus | — | — | enum | enum | enum |
showMathInlineFeedback | false | true | true | true | true |
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 :
<input>dans undiv.relative.w-full- Badge
AnswerFeedbackBadge(CORRECT/INCORRECT) — à positionner en absolu, comme pour math - Panneau de détails : statut + explication + "Réponse attendue"
Issue connue :
mathFeedbackCorrectAnswern'est pas défini pour les questions texte en quiz en direct. La réponse attendue est danscorrectAnswers[0]. Le panneau de détails doit se rabattre surcorrectAnswers[0]quandmathFeedbackCorrectAnswerest 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
| URL | Composant | À tester |
|---|---|---|
datadisplay--statsvisualization--playground | TextStatisticsDisplay / MathStatisticsDisplay | Tous les scénarios text/math |
datadisplay--questioncard--correct-answers-reveal-text-wrong | QuestionCard | Badge + panneau INCORRECT texte |
datadisplay--questioncard--correct-answers-reveal-text-correct | QuestionCard | Badge + panneau CORRECT texte |