fix: quiz bugs fix

grading ui improvs
This commit is contained in:
swve 2024-08-28 20:54:15 +02:00
parent 62fd5e0a3a
commit 494620d2d6

View file

@ -16,7 +16,7 @@ type QuizSchema = {
text: string;
fileID: string;
type: 'text' | 'image' | 'audio' | 'video';
correct: boolean;
assigned_right_answer: boolean;
}[];
};
@ -25,6 +25,7 @@ type QuizSubmitSchema = {
submissions: {
questionUUID: string;
optionUUID: string;
answer: boolean
}[];
};
@ -34,6 +35,12 @@ type TaskQuizObjectProps = {
assignmentTaskUUID?: string;
};
type Submission = {
questionUUID: string;
optionUUID: string;
answer: boolean;
};
function TaskQuizObject({ view, assignmentTaskUUID, user_id }: TaskQuizObjectProps) {
const session = useLHSession() as any;
const access_token = session?.data?.tokens?.access_token;
@ -44,7 +51,7 @@ function TaskQuizObject({ view, assignmentTaskUUID, user_id }: TaskQuizObjectPro
/* TEACHER VIEW CODE */
const [questions, setQuestions] = useState<QuizSchema[]>([
{ questionText: '', questionUUID: 'question_' + uuidv4(), options: [{ text: '', fileID: '', type: 'text', correct: false, optionUUID: 'option_' + uuidv4() }] },
{ questionText: '', questionUUID: 'question_' + uuidv4(), options: [{ text: '', fileID: '', type: 'text', assigned_right_answer: false, optionUUID: 'option_' + uuidv4() }] },
]);
const handleQuestionChange = (index: number, value: string) => {
@ -61,7 +68,7 @@ function TaskQuizObject({ view, assignmentTaskUUID, user_id }: TaskQuizObjectPro
const addOption = (qIndex: number) => {
const updatedQuestions = [...questions];
updatedQuestions[qIndex].options.push({ text: '', fileID: '', type: 'text', correct: false, optionUUID: 'option_' + uuidv4() });
updatedQuestions[qIndex].options.push({ text: '', fileID: '', type: 'text', assigned_right_answer: false, optionUUID: 'option_' + uuidv4() });
setQuestions(updatedQuestions);
};
@ -72,7 +79,7 @@ function TaskQuizObject({ view, assignmentTaskUUID, user_id }: TaskQuizObjectPro
};
const addQuestion = () => {
setQuestions([...questions, { questionText: '', questionUUID: 'question_' + uuidv4(), options: [{ text: '', fileID: '', type: 'text', correct: false, optionUUID: 'option_' + uuidv4() }] }]);
setQuestions([...questions, { questionText: '', questionUUID: 'question_' + uuidv4(), options: [{ text: '', fileID: '', type: 'text', assigned_right_answer: false, optionUUID: 'option_' + uuidv4() }] }]);
};
const removeQuestion = (qIndex: number) => {
@ -81,12 +88,12 @@ function TaskQuizObject({ view, assignmentTaskUUID, user_id }: TaskQuizObjectPro
setQuestions(updatedQuestions);
};
const toggleCorrectOption = (qIndex: number, oIndex: number) => {
const toggleOption = (qIndex: number, oIndex: number) => {
const updatedQuestions = [...questions];
// Find the option to toggle
const optionToToggle = updatedQuestions[qIndex].options[oIndex];
// Toggle the 'correct' property of the option
optionToToggle.correct = !optionToToggle.correct;
optionToToggle.assigned_right_answer = !optionToToggle.assigned_right_answer;
setQuestions(updatedQuestions);
};
@ -123,20 +130,24 @@ function TaskQuizObject({ view, assignmentTaskUUID, user_id }: TaskQuizObjectPro
async function chooseOption(qIndex: number, oIndex: number) {
const updatedSubmissions = [...userSubmissions.submissions];
const questionUUID = questions[qIndex].questionUUID;
const optionUUID = questions[qIndex].options[oIndex].optionUUID;
const question = questions[qIndex];
const option = question?.options[oIndex];
// Check if this question already has a submission with the selected option
const existingSubmissionIndex = updatedSubmissions.findIndex(
if (!question || !option) return;
const questionUUID = question.questionUUID;
const optionUUID = option.optionUUID;
if (!questionUUID || !optionUUID) return;
const submissionIndex = updatedSubmissions.findIndex(
(submission) => submission.questionUUID === questionUUID && submission.optionUUID === optionUUID
);
if (existingSubmissionIndex === -1 && optionUUID && questionUUID) {
// If the selected option is not already chosen, add it to the submissions
updatedSubmissions.push({ questionUUID, optionUUID });
if (submissionIndex === -1) {
updatedSubmissions.push({ questionUUID, optionUUID, answer: true });
} else {
// If the selected option is already chosen, remove it from the submissions
updatedSubmissions.splice(existingSubmissionIndex, 1);
updatedSubmissions[submissionIndex].answer = !updatedSubmissions[submissionIndex].answer;
}
setUserSubmissions({
@ -176,12 +187,34 @@ function TaskQuizObject({ view, assignmentTaskUUID, user_id }: TaskQuizObjectPro
const submitFC = async () => {
// Ensure all questions and options have submissions
const updatedSubmissions: Submission[] = questions.flatMap(question => {
return question.options.map(option => {
const existingSubmission = userSubmissions.submissions.find(
submission => submission.questionUUID === question.questionUUID && submission.optionUUID === option.optionUUID
);
return existingSubmission || {
questionUUID: question.questionUUID || '',
optionUUID: option.optionUUID || '',
answer: false // Mark unsubmitted options as false
};
});
});
// Update userSubmissions with the complete set of submissions
const updatedUserSubmissions: QuizSubmitSchema = {
...userSubmissions,
submissions: updatedSubmissions
};
// Save the quiz to the server
const values = {
task_submission: userSubmissions,
task_submission: updatedUserSubmissions,
grade: 0,
task_submission_grade_feedback: '',
};
if (assignmentTaskUUID) {
const res = await handleAssignmentTaskSubmission(values, assignmentTaskUUID, assignment.assignment_object.assignment_uuid, access_token);
if (res) {
@ -190,6 +223,7 @@ function TaskQuizObject({ view, assignmentTaskUUID, user_id }: TaskQuizObjectPro
});
toast.success('Task saved successfully');
setShowSavingDisclaimer(false);
setUserSubmissions(updatedUserSubmissions);
} else {
toast.error('Error saving task, please retry later.');
}
@ -214,30 +248,22 @@ function TaskQuizObject({ view, assignmentTaskUUID, user_id }: TaskQuizObjectPro
async function gradeFC() {
if (assignmentTaskUUID) {
// Ensure maxPoints is defined
const maxPoints = assignmentTaskOutsideProvider?.max_grade_value || 100; // Default to 100 if not defined
const maxPoints = assignmentTaskOutsideProvider?.max_grade_value || 100;
const totalOptions = questions.reduce((total, question) => total + question.options.length, 0);
let correctAnswers = 0;
// Ensure userSubmissions.questions are set
const totalQuestions = questions.length;
let correctQuestions = 0;
let incorrectQuestions = 0;
userSubmissions.submissions.forEach((submission) => {
const question = questions.find((q) => q.questionUUID === submission.questionUUID);
const option = question?.options.find((o) => o.optionUUID === submission.optionUUID);
if (option?.correct) {
correctQuestions++;
} else {
incorrectQuestions++;
}
questions.forEach((question) => {
question.options.forEach((option) => {
const submission = userSubmissions.submissions.find(
(sub) => sub.questionUUID === question.questionUUID && sub.optionUUID === option.optionUUID
);
if (submission?.answer === option.assigned_right_answer) {
correctAnswers++;
}
});
});
// Calculate grade with penalties for incorrect answers
const pointsPerQuestion = maxPoints / totalQuestions;
const rawGrade = (correctQuestions - incorrectQuestions) * pointsPerQuestion;
// Ensure the grade is within the valid range
const finalGrade = Math.max(0, Math.min(rawGrade, maxPoints));
const finalGrade = Math.round((correctAnswers / totalOptions) * maxPoints);
// Save the grade to the server
const values = {
@ -337,15 +363,15 @@ function TaskQuizObject({ view, assignmentTaskUUID, user_id }: TaskQuizObjectPro
{view === 'teacher' && (
<>
<div
className={`w-fit flex-none flex text-xs px-2 py-0.5 space-x-1 items-center h-fit rounded-lg ${option.correct ? 'bg-lime-200 text-lime-600' : 'bg-rose-200/60 text-rose-500'
className={`w-fit flex-none flex text-xs px-2 py-0.5 space-x-1 items-center h-fit rounded-lg ${option.assigned_right_answer ? 'bg-lime-200 text-lime-600' : 'bg-rose-200/60 text-rose-500'
} hover:bg-lime-300 text-sm transition-all ease-linear cursor-pointer`}
onClick={() => toggleCorrectOption(qIndex, oIndex)}
onClick={() => toggleOption(qIndex, oIndex)}
>
{option.correct ? <Check size={12} className="mx-auto" /> : <X size={12} className="mx-auto" />}
{option.correct ? (
<p className="mx-auto font-bold text-xs">Correct</p>
{option.assigned_right_answer ? <Check size={12} className="mx-auto" /> : <X size={12} className="mx-auto" />}
{option.assigned_right_answer ? (
<p className="mx-auto font-bold text-xs">True</p>
) : (
<p className="mx-auto font-bold text-xs">Incorrect</p>
<p className="mx-auto font-bold text-xs">False</p>
)}
</div>
<div
@ -359,30 +385,38 @@ function TaskQuizObject({ view, assignmentTaskUUID, user_id }: TaskQuizObjectPro
{view === 'grading' && (
<>
<div
className={`w-fit flex-none flex text-xs px-2 py-0.5 space-x-1 items-center h-fit rounded-lg ${option.correct ? 'bg-lime-200 text-lime-600' : 'bg-rose-200/60 text-rose-500'
className={`w-fit flex-none flex text-xs px-2 py-0.5 space-x-1 items-center h-fit rounded-lg ${option.assigned_right_answer ? 'bg-lime-200 text-lime-600' : 'bg-rose-200/60 text-rose-500'
} hover:bg-lime-300 text-sm transition-all ease-linear cursor-pointer`}
>
{option.correct ? <Check size={12} className="mx-auto" /> : <X size={12} className="mx-auto" />}
{option.correct ? (
<p className="mx-auto font-bold text-xs">Marked as Correct</p>
{option.assigned_right_answer ? <Check size={12} className="mx-auto" /> : <X size={12} className="mx-auto" />}
{option.assigned_right_answer ? (
<p className="mx-auto font-bold text-xs">Marked as True</p>
) : (
<p className="mx-auto font-bold text-xs">Marked as Incorrect</p>
<p className="mx-auto font-bold text-xs">Marked as False</p>
)}
</div>
</>
)}
{view === 'student' && (
<div className={`w-[20px] flex-none flex items-center h-[20px] rounded-lg ${userSubmissions.submissions.find(
(submission) =>
submission.questionUUID === question.questionUUID && submission.optionUUID === option.optionUUID
)
? "bg-green-200/60 text-green-500 hover:bg-green-300" // Selected state colors
: "bg-slate-200/60 text-slate-500 hover:bg-slate-300" // Default state colors
} text-sm transition-all ease-linear cursor-pointer`}>
<div
className={`w-[20px] flex-none flex items-center h-[20px] rounded-lg ${
userSubmissions.submissions.find(
(submission) =>
submission.questionUUID === question.questionUUID &&
submission.optionUUID === option.optionUUID &&
submission.answer
)
? "bg-green-200/60 text-green-500 hover:bg-green-300"
: "bg-slate-200/60 text-slate-500 hover:bg-slate-300"
} text-sm transition-all ease-linear cursor-pointer`}
onClick={() => chooseOption(qIndex, oIndex)}
>
{userSubmissions.submissions.find(
(submission) =>
submission.questionUUID === question.questionUUID && submission.optionUUID === option.optionUUID
submission.questionUUID === question.questionUUID &&
submission.optionUUID === option.optionUUID &&
submission.answer
) ? (
<Check size={12} className="mx-auto" />
) : (
@ -391,22 +425,30 @@ function TaskQuizObject({ view, assignmentTaskUUID, user_id }: TaskQuizObjectPro
</div>
)}
{view === 'grading' && (
<div className={`w-[20px] flex-none flex items-center h-[20px] rounded-lg ${userSubmissions.submissions.find(
(submission) =>
submission.questionUUID === question.questionUUID && submission.optionUUID === option.optionUUID
)
? "bg-green-200/60 text-green-500 hover:bg-green-300" // Selected state colors
: "bg-slate-200/60 text-slate-500 hover:bg-slate-300" // Default state colors
} text-sm transition-all ease-linear cursor-pointer`}>
{userSubmissions.submissions.find(
(submission) =>
submission.questionUUID === question.questionUUID && submission.optionUUID === option.optionUUID
) ? (
<Check size={12} className="mx-auto" />
) : (
<X size={12} className="mx-auto" />
)}
</div>
<>
<div className={`w-[20px] flex-none flex items-center h-[20px] rounded-lg ${
userSubmissions.submissions.find(
(submission) =>
submission.questionUUID === question.questionUUID &&
submission.optionUUID === option.optionUUID &&
submission.answer
)
? "bg-green-200/60 text-green-500"
: "bg-slate-200/60 text-slate-500"
} text-sm`}>
{userSubmissions.submissions.find(
(submission) =>
submission.questionUUID === question.questionUUID &&
submission.optionUUID === option.optionUUID &&
submission.answer
) ? (
<Check size={12} className="mx-auto" />
) : (
<X size={12} className="mx-auto" />
)}
</div>
</>
)}
</div>