mirror of
https://github.com/ets-cfuhrman-pfe/EvalueTonSavoir.git
synced 2025-08-11 21:23:54 -04:00
Fixes #133
Redesign how answers are submitted and updated (react state management)
This commit is contained in:
parent
ab18283db0
commit
c0cc4d01e0
10 changed files with 531 additions and 320 deletions
|
|
@ -48,7 +48,7 @@ describe('StudentModeQuiz', () => {
|
|||
fireEvent.click(screen.getByText('Répondre'));
|
||||
});
|
||||
|
||||
expect(mockSubmitAnswer).toHaveBeenCalledWith('Option A', '1');
|
||||
expect(mockSubmitAnswer).toHaveBeenCalledWith('Option A', 1);
|
||||
});
|
||||
|
||||
test('handles quit button click', async () => {
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ describe('TeacherModeQuiz', () => {
|
|||
act(() => {
|
||||
fireEvent.click(screen.getByText('Répondre'));
|
||||
});
|
||||
expect(mockSubmitAnswer).toHaveBeenCalledWith('Option A', '1');
|
||||
expect(mockSubmitAnswer).toHaveBeenCalledWith('Option A', 1);
|
||||
expect(screen.getByText('Votre réponse est "Option A".')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
// LiveResults.tsx
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import React, { 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';
|
||||
|
|
@ -15,11 +14,12 @@ import {
|
|||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableContainer,
|
||||
TableFooter,
|
||||
TableHead,
|
||||
TableRow
|
||||
} from '@mui/material';
|
||||
import { StudentType, Answer } from '../../Types/StudentType';
|
||||
import { StudentType } from '../../Types/StudentType';
|
||||
import { formatLatex } from '../GiftTemplate/templates/TextType';
|
||||
|
||||
interface LiveResultsProps {
|
||||
|
|
@ -27,7 +27,7 @@ interface LiveResultsProps {
|
|||
questions: QuestionType[];
|
||||
showSelectedQuestion: (index: number) => void;
|
||||
quizMode: 'teacher' | 'student';
|
||||
connectedStudents: StudentType[]
|
||||
students: StudentType[]
|
||||
}
|
||||
|
||||
// interface Answer {
|
||||
|
|
@ -42,10 +42,10 @@ interface LiveResultsProps {
|
|||
// answers: Answer[];
|
||||
// }
|
||||
|
||||
const LiveResults: React.FC<LiveResultsProps> = ({ socket, questions, showSelectedQuestion, connectedStudents }) => {
|
||||
const LiveResults: React.FC<LiveResultsProps> = ({ questions, showSelectedQuestion, students }) => {
|
||||
const [showUsernames, setShowUsernames] = useState<boolean>(false);
|
||||
const [showCorrectAnswers, setShowCorrectAnswers] = useState<boolean>(false);
|
||||
const [students, setStudents] = useState<StudentType[]>(connectedStudents);
|
||||
// const [students, setStudents] = useState<StudentType[]>(initialStudents);
|
||||
// const [studentResultsMap, setStudentResultsMap] = useState<Map<string, StudentResult>>(new Map());
|
||||
|
||||
const maxQuestions = questions.length;
|
||||
|
|
@ -69,83 +69,77 @@ const LiveResults: React.FC<LiveResultsProps> = ({ socket, questions, showSelect
|
|||
// }
|
||||
|
||||
// }, [students])
|
||||
useEffect(() => {
|
||||
// Update the students state when the initialStudents prop changes
|
||||
setStudents(connectedStudents);
|
||||
console.log(`useEffect: connectedStudents: ${JSON.stringify(connectedStudents)}`);
|
||||
}, [connectedStudents]);
|
||||
|
||||
// useEffect(() => {
|
||||
// if (socket) {
|
||||
// const submitAnswerHandler = ({
|
||||
// idUser,
|
||||
// answer,
|
||||
// idQuestion
|
||||
// }: {
|
||||
// idUser: string;
|
||||
// username: string;
|
||||
// answer: string | number | boolean;
|
||||
// idQuestion: number;
|
||||
// }) => {
|
||||
// console.log(`Received answer from ${idUser} for question ${idQuestion}: ${answer}`);
|
||||
|
||||
useEffect(() => {
|
||||
if (socket) {
|
||||
const submitAnswerHandler = ({
|
||||
idUser,
|
||||
answer,
|
||||
idQuestion
|
||||
}: {
|
||||
idUser: string;
|
||||
username: string;
|
||||
answer: string | number | boolean;
|
||||
idQuestion: number;
|
||||
}) => {
|
||||
console.log(`Received answer from ${idUser} for question ${idQuestion}: ${answer}`);
|
||||
|
||||
// print the list of current student names
|
||||
console.log('Current students:');
|
||||
students.forEach((student) => {
|
||||
console.log(student.name);
|
||||
});
|
||||
|
||||
// Update the students state using the functional form of setStudents
|
||||
setStudents((prevStudents) => {
|
||||
let foundStudent = false;
|
||||
const updatedStudents = prevStudents.map((student) => {
|
||||
if (student.id === idUser) {
|
||||
foundStudent = true;
|
||||
const updatedAnswers = student.answers.map((ans) => {
|
||||
const newAnswer: Answer = { answer, isCorrect: checkIfIsCorrect(answer, idQuestion), idQuestion };
|
||||
console.log(`Updating answer for ${student.name} for question ${idQuestion} to ${answer}`);
|
||||
return (ans.idQuestion === idQuestion ? { ...ans, newAnswer } : ans);
|
||||
}
|
||||
);
|
||||
return { ...student, answers: updatedAnswers };
|
||||
}
|
||||
return student;
|
||||
});
|
||||
if (!foundStudent) {
|
||||
console.log(`Student ${idUser} not found in the list of students in LiveResults`);
|
||||
}
|
||||
return updatedStudents;
|
||||
});
|
||||
|
||||
|
||||
// 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
|
||||
// console.log(`Student ${idUser} not found in the list of students in LiveResults`);
|
||||
// return;
|
||||
// }
|
||||
|
||||
// const isCorrect = checkIfIsCorrect(answer, idQuestion);
|
||||
// const newAnswer: Answer = { answer, isCorrect, idQuestion };
|
||||
// student.answers.push(newAnswer);
|
||||
// // print list of answers
|
||||
// console.log('Answers:');
|
||||
// student.answers.forEach((answer) => {
|
||||
// console.log(answer.answer);
|
||||
// // print the list of current student names
|
||||
// console.log('Current students:');
|
||||
// students.forEach((student) => {
|
||||
// console.log(student.name);
|
||||
// });
|
||||
// setStudents(updatedStudents); // update the state
|
||||
};
|
||||
|
||||
socket.on('submit-answer', submitAnswerHandler);
|
||||
return () => {
|
||||
socket.off('submit-answer');
|
||||
};
|
||||
}
|
||||
}, [socket]);
|
||||
// // Update the students state using the functional form of setStudents
|
||||
// setStudents((prevStudents) => {
|
||||
// let foundStudent = false;
|
||||
// const updatedStudents = prevStudents.map((student) => {
|
||||
// if (student.id === idUser) {
|
||||
// foundStudent = true;
|
||||
// const updatedAnswers = student.answers.map((ans) => {
|
||||
// const newAnswer: Answer = { answer, isCorrect: checkIfIsCorrect(answer, idQuestion), idQuestion };
|
||||
// console.log(`Updating answer for ${student.name} for question ${idQuestion} to ${answer}`);
|
||||
// return (ans.idQuestion === idQuestion ? { ...ans, newAnswer } : ans);
|
||||
// }
|
||||
// );
|
||||
// return { ...student, answers: updatedAnswers };
|
||||
// }
|
||||
// return student;
|
||||
// });
|
||||
// if (!foundStudent) {
|
||||
// console.log(`Student ${idUser} not found in the list of students in LiveResults`);
|
||||
// }
|
||||
// return updatedStudents;
|
||||
// });
|
||||
|
||||
|
||||
// // 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
|
||||
// // console.log(`Student ${idUser} not found in the list of students in LiveResults`);
|
||||
// // return;
|
||||
// // }
|
||||
|
||||
// // const isCorrect = checkIfIsCorrect(answer, idQuestion);
|
||||
// // const newAnswer: Answer = { answer, isCorrect, idQuestion };
|
||||
// // student.answers.push(newAnswer);
|
||||
// // // print list of answers
|
||||
// // console.log('Answers:');
|
||||
// // student.answers.forEach((answer) => {
|
||||
// // console.log(answer.answer);
|
||||
// // });
|
||||
// // 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) {
|
||||
|
|
@ -202,73 +196,73 @@ const LiveResults: React.FC<LiveResultsProps> = ({ socket, questions, showSelect
|
|||
// );
|
||||
// };
|
||||
|
||||
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;
|
||||
// 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;
|
||||
}
|
||||
// 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 (
|
||||
<div>
|
||||
|
|
@ -301,7 +295,8 @@ const LiveResults: React.FC<LiveResultsProps> = ({ socket, questions, showSelect
|
|||
</div>
|
||||
|
||||
<div className="table-container">
|
||||
<Table size="small" component={Paper}>
|
||||
<TableContainer component={Paper}>
|
||||
<Table size="small">
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell className="sticky-column">
|
||||
|
|
@ -439,6 +434,7 @@ const LiveResults: React.FC<LiveResultsProps> = ({ socket, questions, showSelect
|
|||
</TableRow>
|
||||
</TableFooter>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import DisconnectButton from '../../components/DisconnectButton/DisconnectButton
|
|||
|
||||
interface StudentModeQuizProps {
|
||||
questions: QuestionType[];
|
||||
submitAnswer: (answer: string | number | boolean, idQuestion: string) => void;
|
||||
submitAnswer: (answer: string | number | boolean, idQuestion: number) => void;
|
||||
disconnectWebSocket: () => void;
|
||||
}
|
||||
|
||||
|
|
@ -38,7 +38,7 @@ const StudentModeQuiz: React.FC<StudentModeQuizProps> = ({
|
|||
};
|
||||
|
||||
const handleOnSubmitAnswer = (answer: string | number | boolean) => {
|
||||
const idQuestion = questionInfos.question.id || '-1';
|
||||
const idQuestion = Number(questionInfos.question.id) || -1;
|
||||
submitAnswer(answer, idQuestion);
|
||||
setIsAnswerSubmitted(true);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import { Dialog, DialogTitle, DialogContent, DialogActions, Button } from '@mui/
|
|||
|
||||
interface TeacherModeQuizProps {
|
||||
questionInfos: QuestionType;
|
||||
submitAnswer: (answer: string | number | boolean, idQuestion: string) => void;
|
||||
submitAnswer: (answer: string | number | boolean, idQuestion: number) => void;
|
||||
disconnectWebSocket: () => void;
|
||||
}
|
||||
|
||||
|
|
@ -29,7 +29,7 @@ const TeacherModeQuiz: React.FC<TeacherModeQuizProps> = ({
|
|||
}, [questionInfos]);
|
||||
|
||||
const handleOnSubmitAnswer = (answer: string | number | boolean) => {
|
||||
const idQuestion = questionInfos.question.id || '-1';
|
||||
const idQuestion = Number(questionInfos.question.id) || -1;
|
||||
submitAnswer(answer, idQuestion);
|
||||
setFeedbackMessage(`Votre réponse est "${answer.toString()}".`);
|
||||
setIsFeedbackDialogOpen(true);
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { ENV_VARIABLES } from '../../../constants';
|
|||
|
||||
import StudentModeQuiz from '../../../components/StudentModeQuiz/StudentModeQuiz';
|
||||
import TeacherModeQuiz from '../../../components/TeacherModeQuiz/TeacherModeQuiz';
|
||||
import webSocketService from '../../../services/WebsocketService';
|
||||
import webSocketService, { AnswerSubmissionToBackendType } from '../../../services/WebsocketService';
|
||||
import DisconnectButton from '../../../components/DisconnectButton/DisconnectButton';
|
||||
|
||||
import './joinRoom.css';
|
||||
|
|
@ -99,8 +99,15 @@ const JoinRoom: React.FC = () => {
|
|||
}
|
||||
};
|
||||
|
||||
const handleOnSubmitAnswer = (answer: string | number | boolean, idQuestion: string) => {
|
||||
webSocketService.submitAnswer(roomName, answer, username, idQuestion);
|
||||
const handleOnSubmitAnswer = (answer: string | number | boolean, idQuestion: number) => {
|
||||
const answerData: AnswerSubmissionToBackendType = {
|
||||
roomName: roomName,
|
||||
answer: answer,
|
||||
username: username,
|
||||
idQuestion: idQuestion
|
||||
};
|
||||
|
||||
webSocketService.submitAnswer(answerData);
|
||||
};
|
||||
|
||||
if (isWaitingForTeacher) {
|
||||
|
|
|
|||
|
|
@ -2,16 +2,16 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
import { Socket } from 'socket.io-client';
|
||||
import { parse } from 'gift-pegjs';
|
||||
import { GIFTQuestion, parse } from 'gift-pegjs';
|
||||
import { QuestionType } from '../../../Types/QuestionType';
|
||||
import LiveResultsComponent from '../../../components/LiveResults/LiveResults';
|
||||
// import { QuestionService } from '../../../services/QuestionService';
|
||||
import webSocketService from '../../../services/WebsocketService';
|
||||
import webSocketService, { AnswerReceptionFromBackendType } from '../../../services/WebsocketService';
|
||||
import { QuizType } from '../../../Types/QuizType';
|
||||
|
||||
import './manageRoom.css';
|
||||
import { ENV_VARIABLES } from '../../../constants';
|
||||
import { StudentType } from '../../../Types/StudentType';
|
||||
import { StudentType, Answer } from '../../../Types/StudentType';
|
||||
import { Button } from '@mui/material';
|
||||
import LoadingCircle from '../../../components/LoadingCircle/LoadingCircle';
|
||||
import { Refresh, Error } from '@mui/icons-material';
|
||||
|
|
@ -121,10 +121,9 @@ const ManageRoom: React.FC = () => {
|
|||
useEffect(() => {
|
||||
// This is here to make sure the correct value is sent when user join
|
||||
if (socket) {
|
||||
console.log(`Listening for user-joined in room ${roomName}`);
|
||||
socket.on('user-joined', (_student: StudentType) => {
|
||||
|
||||
// setUsers((prevUsers) => [...prevUsers, user]);
|
||||
|
||||
if (quizMode === 'teacher') {
|
||||
webSocketService.nextQuestion(roomName, currentQuestion);
|
||||
} else if (quizMode === 'student') {
|
||||
|
|
@ -132,7 +131,122 @@ const ManageRoom: React.FC = () => {
|
|||
}
|
||||
});
|
||||
}
|
||||
}, [currentQuestion, quizQuestions]);
|
||||
|
||||
if (socket) {
|
||||
// handle the case where user submits an answer
|
||||
console.log(`Listening for submit-answer-room in room ${roomName}`);
|
||||
socket.on('submit-answer-room', (answerData: AnswerReceptionFromBackendType) => {
|
||||
const { answer, idQuestion, idUser, username } = answerData;
|
||||
console.log(`Received answer from ${username} for question ${idQuestion}: ${answer}`);
|
||||
if (!quizQuestions) {
|
||||
console.log('Quiz questions not found (cannot update answers without them).');
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the students state using the functional form of setStudents
|
||||
setStudents((prevStudents) => {
|
||||
// print the list of current student names
|
||||
console.log('Current students:');
|
||||
prevStudents.forEach((student) => {
|
||||
console.log(student.name);
|
||||
});
|
||||
|
||||
let foundStudent = false;
|
||||
const updatedStudents = prevStudents.map((student) => {
|
||||
console.log(`Comparing ${student.id} to ${idUser}`);
|
||||
if (student.id === idUser) {
|
||||
foundStudent = true;
|
||||
const existingAnswer = student.answers.find((ans) => ans.idQuestion === idQuestion);
|
||||
let updatedAnswers: Answer[] = [];
|
||||
if (existingAnswer) {
|
||||
// Update the existing answer
|
||||
updatedAnswers = student.answers.map((ans) => {
|
||||
console.log(`Comparing ${ans.idQuestion} to ${idQuestion}`);
|
||||
return (ans.idQuestion === idQuestion ? { ...ans, answer, isCorrect: checkIfIsCorrect(answer, idQuestion, quizQuestions!) } : ans);
|
||||
});
|
||||
} else {
|
||||
// Add a new answer
|
||||
const newAnswer = { idQuestion, answer, isCorrect: checkIfIsCorrect(answer, idQuestion, quizQuestions!) };
|
||||
updatedAnswers = [...student.answers, newAnswer];
|
||||
}
|
||||
return { ...student, answers: updatedAnswers };
|
||||
}
|
||||
return student;
|
||||
});
|
||||
if (!foundStudent) {
|
||||
console.log(`Student ${username} not found in the list.`);
|
||||
}
|
||||
return updatedStudents;
|
||||
});
|
||||
});
|
||||
setSocket(socket);
|
||||
}
|
||||
|
||||
}, [socket, currentQuestion, quizQuestions]);
|
||||
|
||||
// useEffect(() => {
|
||||
// if (socket) {
|
||||
// const submitAnswerHandler = (answerData: answerSubmissionType) => {
|
||||
// const { answer, idQuestion, username } = answerData;
|
||||
// console.log(`Received answer from ${username} for question ${idQuestion}: ${answer}`);
|
||||
|
||||
// // print the list of current student names
|
||||
// console.log('Current students:');
|
||||
// students.forEach((student) => {
|
||||
// console.log(student.name);
|
||||
// });
|
||||
|
||||
// // Update the students state using the functional form of setStudents
|
||||
// setStudents((prevStudents) => {
|
||||
// let foundStudent = false;
|
||||
// const updatedStudents = prevStudents.map((student) => {
|
||||
// if (student.id === username) {
|
||||
// foundStudent = true;
|
||||
// const updatedAnswers = student.answers.map((ans) => {
|
||||
// const newAnswer: Answer = { answer, isCorrect: checkIfIsCorrect(answer, idQuestion, quizQuestions!), idQuestion };
|
||||
// console.log(`Updating answer for ${student.name} for question ${idQuestion} to ${answer}`);
|
||||
// return (ans.idQuestion === idQuestion ? { ...ans, newAnswer } : ans);
|
||||
// }
|
||||
// );
|
||||
// return { ...student, answers: updatedAnswers };
|
||||
// }
|
||||
// return student;
|
||||
// });
|
||||
// if (!foundStudent) {
|
||||
// console.log(`Student ${username} not found in the list of students in LiveResults`);
|
||||
// }
|
||||
// return updatedStudents;
|
||||
// });
|
||||
|
||||
|
||||
// // 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
|
||||
// // console.log(`Student ${idUser} not found in the list of students in LiveResults`);
|
||||
// // return;
|
||||
// // }
|
||||
|
||||
// // const isCorrect = checkIfIsCorrect(answer, idQuestion);
|
||||
// // const newAnswer: Answer = { answer, isCorrect, idQuestion };
|
||||
// // student.answers.push(newAnswer);
|
||||
// // // print list of answers
|
||||
// // console.log('Answers:');
|
||||
// // student.answers.forEach((answer) => {
|
||||
// // console.log(answer.answer);
|
||||
// // });
|
||||
// // setStudents(updatedStudents); // update the state
|
||||
// };
|
||||
|
||||
// socket.on('submit-answer', submitAnswerHandler);
|
||||
// return () => {
|
||||
// socket.off('submit-answer');
|
||||
// };
|
||||
// }
|
||||
// }, [socket]);
|
||||
|
||||
|
||||
const nextQuestion = () => {
|
||||
if (!quizQuestions || !currentQuestion || !quiz?.content) return;
|
||||
|
|
@ -173,8 +287,12 @@ const ManageRoom: React.FC = () => {
|
|||
|
||||
const launchTeacherMode = () => {
|
||||
const quizQuestions = initializeQuizQuestion();
|
||||
console.log('launchTeacherMode - quizQuestions:', quizQuestions);
|
||||
|
||||
if (!quizQuestions) return;
|
||||
if (!quizQuestions) {
|
||||
console.log('Error launching quiz (launchTeacherMode). No questions found.');
|
||||
return;
|
||||
}
|
||||
|
||||
setCurrentQuestion(quizQuestions[0]);
|
||||
webSocketService.nextQuestion(roomName, quizQuestions[0]);
|
||||
|
|
@ -182,18 +300,20 @@ const ManageRoom: React.FC = () => {
|
|||
|
||||
const launchStudentMode = () => {
|
||||
const quizQuestions = initializeQuizQuestion();
|
||||
console.log('launchStudentMode - quizQuestions:', quizQuestions);
|
||||
|
||||
if (!quizQuestions) {
|
||||
console.log('Error launching quiz (launchStudentMode). No questions found.');
|
||||
return;
|
||||
}
|
||||
|
||||
setQuizQuestions(quizQuestions);
|
||||
webSocketService.launchStudentModeQuiz(roomName, quizQuestions);
|
||||
};
|
||||
|
||||
const launchQuiz = () => {
|
||||
if (!socket || !roomName || !quiz?.content || quiz?.content.length === 0) {
|
||||
// TODO: This error happens when token expires! Need to handle it properly
|
||||
console.log('Error launching quiz. No socket, room name or no questions.');
|
||||
console.log(`Error launching quiz. socket: ${socket}, roomName: ${roomName}, quiz: ${quiz}`);
|
||||
return;
|
||||
}
|
||||
switch (quizMode) {
|
||||
|
|
@ -219,6 +339,75 @@ const ManageRoom: React.FC = () => {
|
|||
navigate('/teacher/dashboard');
|
||||
};
|
||||
|
||||
function checkIfIsCorrect(answer: string | number | boolean, idQuestion: number, questions: QuestionType[]): 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;
|
||||
}
|
||||
|
||||
|
||||
if (!roomName) {
|
||||
return (
|
||||
<div className="center">
|
||||
|
|
@ -295,7 +484,7 @@ const ManageRoom: React.FC = () => {
|
|||
socket={socket}
|
||||
questions={quizQuestions}
|
||||
showSelectedQuestion={showSelectedQuestion}
|
||||
connectedStudents={students}
|
||||
students={students}
|
||||
></LiveResultsComponent>
|
||||
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,22 @@
|
|||
// WebSocketService.tsx
|
||||
import { io, Socket } from 'socket.io-client';
|
||||
|
||||
// Must (manually) sync these types to server/socket/socket.js
|
||||
|
||||
export type AnswerSubmissionToBackendType = {
|
||||
roomName: string;
|
||||
username: string;
|
||||
answer: string | number | boolean;
|
||||
idQuestion: number;
|
||||
};
|
||||
|
||||
export type AnswerReceptionFromBackendType = {
|
||||
idUser: string;
|
||||
username: string;
|
||||
answer: string | number | boolean;
|
||||
idQuestion: number;
|
||||
};
|
||||
|
||||
class WebSocketService {
|
||||
private socket: Socket | null = null;
|
||||
|
||||
|
|
@ -51,19 +67,22 @@ class WebSocketService {
|
|||
}
|
||||
}
|
||||
|
||||
submitAnswer(
|
||||
roomName: string,
|
||||
answer: string | number | boolean,
|
||||
username: string,
|
||||
idQuestion: string
|
||||
submitAnswer(answerData: AnswerSubmissionToBackendType
|
||||
// roomName: string,
|
||||
// answer: string | number | boolean,
|
||||
// username: string,
|
||||
// idQuestion: string
|
||||
) {
|
||||
if (this.socket) {
|
||||
this.socket?.emit('submit-answer', {
|
||||
answer: answer,
|
||||
roomName: roomName,
|
||||
username: username,
|
||||
idQuestion: idQuestion
|
||||
});
|
||||
this.socket?.emit('submit-answer',
|
||||
// {
|
||||
// answer: answer,
|
||||
// roomName: roomName,
|
||||
// username: username,
|
||||
// idQuestion: idQuestion
|
||||
// }
|
||||
answerData
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@ describe("websocket server", () => {
|
|||
answer: "answer1",
|
||||
idQuestion: 1,
|
||||
});
|
||||
teacherSocket.on("submit-answer", (answer) => {
|
||||
teacherSocket.on("submit-answer-room", (answer) => {
|
||||
expect(answer).toEqual({
|
||||
idUser: studentSocket.id,
|
||||
username: "student1",
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ const setupWebsocket = (io) => {
|
|||
});
|
||||
|
||||
socket.on("submit-answer", ({ roomName, username, answer, idQuestion }) => {
|
||||
socket.to(roomName).emit("submit-answer", {
|
||||
socket.to(roomName).emit("submit-answer-room", {
|
||||
idUser: socket.id,
|
||||
username,
|
||||
answer,
|
||||
|
|
|
|||
Loading…
Reference in a new issue