add check quizz for student
This commit is contained in:
parent
76f8ab120a
commit
c982ab2c05
3 changed files with 232 additions and 0 deletions
|
|
@ -21,6 +21,8 @@ import {
|
|||
GetVideoProgressResponse,
|
||||
CompleteLessonInput,
|
||||
CompleteLessonResponse,
|
||||
SubmitQuizInput,
|
||||
SubmitQuizResponse,
|
||||
} from "../types/CoursesStudent.types";
|
||||
import { getPresignedUrl, listObjects, getVideoFolder, getAttachmentsFolder } from '../config/minio';
|
||||
|
||||
|
|
@ -461,6 +463,7 @@ export class CoursesStudentService {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
async checkAccessLesson(input: CheckLessonAccessInput): Promise<CheckLessonAccessResponse> {
|
||||
try {
|
||||
const { token, course_id, lesson_id } = input;
|
||||
|
|
@ -993,4 +996,156 @@ export class CoursesStudentService {
|
|||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ส่งคำตอบ Quiz และคำนวณคะแนน
|
||||
* Submit quiz answers and calculate score
|
||||
*/
|
||||
async submitQuiz(input: SubmitQuizInput): Promise<SubmitQuizResponse> {
|
||||
try {
|
||||
const { token, course_id, lesson_id, answers } = input;
|
||||
const decoded = jwt.verify(token, config.jwt.secret) as { id: number };
|
||||
|
||||
// Check enrollment
|
||||
const enrollment = await prisma.enrollment.findUnique({
|
||||
where: {
|
||||
unique_enrollment: {
|
||||
user_id: decoded.id,
|
||||
course_id,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!enrollment) {
|
||||
throw new ForbiddenError('You are not enrolled in this course');
|
||||
}
|
||||
|
||||
// Get lesson and verify it's a QUIZ type
|
||||
const lesson = await prisma.lesson.findUnique({
|
||||
where: { id: lesson_id },
|
||||
include: {
|
||||
quiz: {
|
||||
include: {
|
||||
questions: {
|
||||
include: {
|
||||
choices: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
chapter: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!lesson) {
|
||||
throw new NotFoundError('Lesson not found');
|
||||
}
|
||||
|
||||
if (lesson.type !== 'QUIZ') {
|
||||
throw new ValidationError('This lesson is not a quiz');
|
||||
}
|
||||
|
||||
if (!lesson.quiz) {
|
||||
throw new NotFoundError('Quiz not found for this lesson');
|
||||
}
|
||||
|
||||
// Verify lesson belongs to the course
|
||||
if (lesson.chapter.course_id !== course_id) {
|
||||
throw new NotFoundError('Lesson not found in this course');
|
||||
}
|
||||
|
||||
const quiz = lesson.quiz;
|
||||
|
||||
// Get previous attempt count
|
||||
const previousAttempts = await prisma.quizAttempt.count({
|
||||
where: {
|
||||
user_id: decoded.id,
|
||||
quiz_id: quiz.id,
|
||||
},
|
||||
});
|
||||
const attemptNumber = previousAttempts + 1;
|
||||
|
||||
// Calculate score
|
||||
let totalScore = 0;
|
||||
let earnedScore = 0;
|
||||
let correctAnswers = 0;
|
||||
const answersReview: {
|
||||
question_id: number;
|
||||
selected_choice_id: number;
|
||||
correct_choice_id: number;
|
||||
is_correct: boolean;
|
||||
score: number;
|
||||
}[] = [];
|
||||
|
||||
for (const question of quiz.questions) {
|
||||
totalScore += question.score;
|
||||
|
||||
// Find the correct choice for this question
|
||||
const correctChoice = question.choices.find(c => c.is_correct);
|
||||
const correctChoiceId = correctChoice?.id ?? 0;
|
||||
|
||||
// Find student's answer for this question
|
||||
const studentAnswer = answers.find(a => a.question_id === question.id);
|
||||
const selectedChoiceId = studentAnswer?.choice_id ?? 0;
|
||||
|
||||
// Check if answer is correct
|
||||
const isCorrect = selectedChoiceId === correctChoiceId;
|
||||
if (isCorrect) {
|
||||
earnedScore += question.score;
|
||||
correctAnswers++;
|
||||
}
|
||||
|
||||
answersReview.push({
|
||||
question_id: question.id,
|
||||
selected_choice_id: selectedChoiceId,
|
||||
correct_choice_id: correctChoiceId,
|
||||
is_correct: isCorrect,
|
||||
score: isCorrect ? question.score : 0,
|
||||
});
|
||||
}
|
||||
|
||||
// Calculate percentage and check if passed
|
||||
const scorePercentage = totalScore > 0 ? (earnedScore / totalScore) * 100 : 0;
|
||||
const isPassed = scorePercentage >= quiz.passing_score;
|
||||
|
||||
// Create quiz attempt record
|
||||
const now = new Date();
|
||||
const quizAttempt = await prisma.quizAttempt.create({
|
||||
data: {
|
||||
user_id: decoded.id,
|
||||
quiz_id: quiz.id,
|
||||
score: earnedScore,
|
||||
total_questions: quiz.questions.length,
|
||||
correct_answers: correctAnswers,
|
||||
is_passed: isPassed,
|
||||
attempt_number: attemptNumber,
|
||||
answers: answers as any, // Store student answers as JSON
|
||||
started_at: now,
|
||||
completed_at: now,
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
code: 200,
|
||||
message: isPassed ? 'Quiz passed!' : 'Quiz completed',
|
||||
data: {
|
||||
attempt_id: quizAttempt.id,
|
||||
quiz_id: quiz.id,
|
||||
score: earnedScore,
|
||||
total_score: totalScore,
|
||||
total_questions: quiz.questions.length,
|
||||
correct_answers: correctAnswers,
|
||||
is_passed: isPassed,
|
||||
passing_score: quiz.passing_score,
|
||||
attempt_number: attemptNumber,
|
||||
started_at: quizAttempt.started_at,
|
||||
completed_at: quizAttempt.completed_at!,
|
||||
answers_review: quiz.show_answers_after_completion ? answersReview : undefined,
|
||||
},
|
||||
};
|
||||
} catch (error) {
|
||||
logger.error(error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue