freeCodeCamp/api-server/src/server/utils/exam.js
2024-10-02 08:55:38 -05:00

90 lines
2.9 KiB
JavaScript

import { shuffleArray } from '../../../../shared/utils/shuffle-array';
function filterDeprecated(arr) {
return arr.filter(i => !i.deprecated);
}
function getRandomElement(arr) {
const id = Math.floor(Math.random() * arr.length);
return arr[id];
}
// Used to generate a random exam
export function generateRandomExam(examJson) {
const { numberOfQuestionsInExam, questions } = examJson;
const numberOfAnswersPerQuestion = 5;
const availableQuestions = shuffleArray(filterDeprecated(questions));
const examQuestions = availableQuestions.slice(0, numberOfQuestionsInExam);
const randomizedExam = examQuestions.map(question => {
const { correctAnswers, wrongAnswers } = question;
const availableCorrectAnswers = filterDeprecated(correctAnswers);
const availableWrongAnswers = shuffleArray(filterDeprecated(wrongAnswers));
const correctAnswer = getRandomElement(availableCorrectAnswers);
const answers = shuffleArray([
correctAnswer,
...availableWrongAnswers.slice(0, numberOfAnswersPerQuestion - 1)
]);
return {
id: question.id,
question: question.question,
answers
};
});
return randomizedExam;
}
// Used to evaluate user completed exams
export function createExamResults(userExam, originalExam) {
const { userExamQuestions, examTimeInSeconds } = userExam;
/**
* Potential Bug:
* numberOfQuestionsInExam and passingPercent come from the exam in the database.
* If either changes between the time a camper starts and submits, it could skew
* the scores. The alternative is to send those to the client and then get them
* back from the client - but then they could be manipulated to cheat. So I think
* this is the way to go. They are unlikely to change, as that would be unfair. We
* could get numberOfQuestionsInExam from userExamQuestions.length - so only the
* passingPercent would come from the database. Maybe that would be better.
*/
const {
questions: originalQuestions,
numberOfQuestionsInExam,
passingPercent
} = originalExam;
const numberOfCorrectAnswers = userExamQuestions.reduce(
(count, userQuestion) => {
const originalQuestion = originalQuestions.find(
examQuestion => examQuestion.id === userQuestion.id
);
if (!originalQuestion) {
throw new Error('An error occurred. Could not find exam question.');
}
const isCorrect = originalQuestion.correctAnswers.find(
examAnswer => examAnswer.id === userQuestion.answer.id
);
return isCorrect ? count + 1 : count;
},
0
);
// Percent rounded to one decimal place
const percent = (numberOfCorrectAnswers / numberOfQuestionsInExam) * 100;
const percentCorrect = Math.round(percent * 10) / 10;
const passed = percentCorrect >= passingPercent;
return {
numberOfCorrectAnswers,
numberOfQuestionsInExam,
percentCorrect,
passingPercent,
passed,
examTimeInSeconds
};
}