Files

57 lines
1.8 KiB
JavaScript

"use strict";
/**
* Grades a submission against the stored correct answers.
*
* The breakdown returned here is what gets sent back to the client, so it
* deliberately never includes which option(s) were correct — only whether
* the user's own answer for each question was right or wrong. Explanation
* text is included only when the question was answered correctly, since
* showing it for a wrong answer would effectively reveal the correct one.
*
* @param {Array} questions - QuizQuestion rows w/ .options (incl. is_correct), already fetched
* @param {Object} answers - { [question_id]: optionId | optionId[] } submitted by the client
*/
function gradeSubmission(questions, answers = {}) {
let totalPoints = 0;
let earnedPoints = 0;
const breakdown = questions.map((q) => {
const points = q.points ?? 1;
totalPoints += points;
const correctIds = (q.options ?? [])
.filter((o) => o.is_correct)
.map((o) => o.option_id);
const submitted = answers[q.question_id];
const submittedIds = Array.isArray(submitted)
? submitted
: (submitted !== undefined && submitted !== null ? [submitted] : []);
const isCorrect =
submittedIds.length === correctIds.length &&
correctIds.every((id) => submittedIds.includes(id));
if (isCorrect) earnedPoints += points;
return {
question_id: q.question_id,
type: q.type,
question: q.question,
points,
is_correct: isCorrect,
selected_option_ids: submittedIds,
explanation: isCorrect ? (q.explanation ?? null) : null,
options: (q.options ?? []).map((o) => ({
option_id: o.option_id,
text: o.text,
})),
};
});
const score = totalPoints > 0 ? Math.round((earnedPoints / totalPoints) * 100) : 0;
return { totalPoints, earnedPoints, score, breakdown };
}
module.exports = { gradeSubmission };