// LiveResults.tsx import React, { useEffect, useMemo, useState } from 'react'; import { Socket } from 'socket.io-client'; import { GIFTQuestion } from 'gift-pegjs'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faCheck, faCircleXmark } from '@fortawesome/free-solid-svg-icons'; import { QuestionType } from '../../Types/QuestionType'; import './liveResult.css'; import { FormControlLabel, FormGroup, Paper, Switch, Table, TableBody, TableCell, TableFooter, TableHead, TableRow } from '@mui/material'; import { StudentType, Answer } from '../../Types/StudentType'; import { formatLatex } from '../GiftTemplate/templates/TextType'; interface LiveResultsProps { socket: Socket | null; questions: QuestionType[]; showSelectedQuestion: (index: number) => void; quizMode: 'teacher' | 'student'; connectedStudents: StudentType[] } // interface Answer { // answer: string | number | boolean; // isCorrect: boolean; // idQuestion: number; // } // interface StudentResult { // username: string; // idUser: string; // answers: Answer[]; // } const LiveResults: React.FC = ({ socket, questions, showSelectedQuestion, connectedStudents }) => { const [showUsernames, setShowUsernames] = useState(false); const [showCorrectAnswers, setShowCorrectAnswers] = useState(false); const [students, setStudents] = useState(connectedStudents); // const [studentResultsMap, setStudentResultsMap] = useState>(new Map()); const maxQuestions = questions.length; // useEffect(() => { // // Initialize the map with the current students // const newStudentResultsMap = new Map(); // for (const student of students) { // newStudentResultsMap.set(student.id, { username: student.name, idUser: student.id, answers: [] }); // } // setStudentResultsMap(newStudentResultsMap); // }, []) // update when students change // useEffect(() => { // // studentResultsMap is inconsistent with students -- need to update // for (const student of students as StudentType[]) { // } // }, [students]) useEffect(() => { // Update the students state when the initialStudents prop changes setStudents(connectedStudents); }, [connectedStudents]); useEffect(() => { if (socket) { const submitAnswerHandler = ({ idUser, answer, idQuestion }: { idUser: string; username: string; answer: string | number | boolean; idQuestion: number; }) => { // make a copy of the students array so we can update it const updatedStudents = [...students]; const student = updatedStudents.find((student) => student.id === idUser); if (!student) { // this is a bad thing if an answer was submitted but the student isn't in the list return; } const isCorrect = checkIfIsCorrect(answer, idQuestion); const newAnswer: Answer = { answer, isCorrect, idQuestion }; student.answers.push(newAnswer); setStudents(updatedStudents); // update the state }; socket.on('submit-answer', submitAnswerHandler); return () => { socket.off('submit-answer'); }; } }, [socket]); const getStudentGrade = (student: StudentType): number => { if (student.answers.length === 0) { return 0; } const uniqueQuestions = new Set(); let correctAnswers = 0; for (const answer of student.answers) { const { idQuestion, isCorrect } = answer; if (!uniqueQuestions.has(idQuestion)) { uniqueQuestions.add(idQuestion); if (isCorrect) { correctAnswers++; } } } return (correctAnswers / questions.length) * 100; }; const classAverage: number = useMemo(() => { let classTotal = 0; students.forEach((student) => { classTotal += getStudentGrade(student); }); return classTotal / students.length; }, [students]); const getCorrectAnswersPerQuestion = (index: number): number => { return ( (students.filter((student) => student.answers.some( (answer) => parseInt(answer.idQuestion.toString()) === index + 1 && answer.isCorrect ) ).length / students.length) * 100 ); }; // (studentResults.filter((student) => // student.answers.some( // (answer) => // parseInt(answer.idQuestion.toString()) === index + 1 && answer.isCorrect // ) // ).length / // studentResults.length) * // 100 // ); // }; function checkIfIsCorrect(answer: string | number | boolean, idQuestion: number): boolean { const questionInfo = questions.find((q) => q.question.id ? q.question.id === idQuestion.toString() : false ) as QuestionType | undefined; const answerText = answer.toString(); if (questionInfo) { const question = questionInfo.question as GIFTQuestion; if (question.type === 'TF') { return ( (question.isTrue && answerText == 'true') || (!question.isTrue && answerText == 'false') ); } else if (question.type === 'MC') { return question.choices.some( (choice) => choice.isCorrect && choice.text.text === answerText ); } else if (question.type === 'Numerical') { if (question.choices && !Array.isArray(question.choices)) { if ( question.choices.type === 'high-low' && question.choices.numberHigh && question.choices.numberLow ) { const answerNumber = parseFloat(answerText); if (!isNaN(answerNumber)) { return ( answerNumber <= question.choices.numberHigh && answerNumber >= question.choices.numberLow ); } } } if (question.choices && Array.isArray(question.choices)) { if ( question.choices[0].text.type === 'range' && question.choices[0].text.number && question.choices[0].text.range ) { const answerNumber = parseFloat(answerText); const range = question.choices[0].text.range; const correctAnswer = question.choices[0].text.number; if (!isNaN(answerNumber)) { return ( answerNumber <= correctAnswer + range && answerNumber >= correctAnswer - range ); } } if ( question.choices[0].text.type === 'simple' && question.choices[0].text.number ) { const answerNumber = parseFloat(answerText); if (!isNaN(answerNumber)) { return answerNumber === question.choices[0].text.number; } } } } else if (question.type === 'Short') { return question.choices.some( (choice) => choice.text.text.toUpperCase() === answerText.toUpperCase() ); } } return false; } return (
Résultats du quiz
Afficher les noms
} control={ ) => setShowUsernames(e.target.checked) } /> } /> Afficher les réponses
} control={ ) => setShowCorrectAnswers(e.target.checked) } /> } />
Nom d'utilisateur
{Array.from({ length: maxQuestions }, (_, index) => ( showSelectedQuestion(index)} >
{`Q${index + 1}`}
))}
% réussite
{students.map((student) => (
{showUsernames ? student.name : '******'}
{Array.from({ length: maxQuestions }, (_, index) => { const answer = student.answers.find( (answer) => parseInt(answer.idQuestion.toString()) === index + 1 ); const answerText = answer ? answer.answer.toString() : ''; const isCorrect = answer ? answer.isCorrect : false; return ( {showCorrectAnswers ? (
{formatLatex(answerText)}
) : isCorrect ? ( ) : ( answerText !== '' && ( ) )}
); })} {getStudentGrade(student).toFixed()} %
))}
% réussite
{Array.from({ length: maxQuestions }, (_, index) => ( {students.length > 0 ? `${getCorrectAnswersPerQuestion(index).toFixed()} %` : '-'} ))} {students.length > 0 ? `${classAverage.toFixed()} %` : '-'}
); }; export default LiveResults;