Refactor Study Desk application structure
This commit is contained in:
parent
faaccf8a7e
commit
089439ed90
145 changed files with 8087 additions and 3412 deletions
|
|
@ -1,65 +1,40 @@
|
|||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { getQuizSetWithQuestions, createQuizAttempt, listQuizAttempts } from "@/services/quizService";
|
||||
import { scoreQuiz } from "@/lib/scoring";
|
||||
import {
|
||||
QuizAttemptValidationError,
|
||||
QuizNotFoundError,
|
||||
listQuizAttempts,
|
||||
submitQuizAttempt,
|
||||
} from "@/services/quizService";
|
||||
import { quizAttemptSchema } from "@/lib/validation/attemptSchemas";
|
||||
|
||||
export async function POST(
|
||||
request: NextRequest,
|
||||
ctx: RouteContext<"/api/quizzes/[id]/attempt">
|
||||
) {
|
||||
const { id } = await ctx.params;
|
||||
const body = await request.json().catch(() => null);
|
||||
|
||||
if (!body || !body.answersJson) {
|
||||
const parsed = quizAttemptSchema.safeParse(
|
||||
await request.json().catch(() => null)
|
||||
);
|
||||
if (!parsed.success) {
|
||||
return NextResponse.json(
|
||||
{ error: "answersJson is required" },
|
||||
{ error: parsed.error.issues[0]?.message ?? "Invalid attempt" },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
const quizSet = await getQuizSetWithQuestions(id);
|
||||
if (!quizSet) {
|
||||
return NextResponse.json({ error: "Quiz not found" }, { status: 404 });
|
||||
}
|
||||
|
||||
// Parse answers
|
||||
let answers: Record<string, string[]>;
|
||||
try {
|
||||
answers = JSON.parse(body.answersJson);
|
||||
} catch {
|
||||
return NextResponse.json({ error: "Invalid answers JSON" }, { status: 400 });
|
||||
return NextResponse.json(await submitQuizAttempt(id, parsed.data), {
|
||||
status: 201,
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof QuizNotFoundError) {
|
||||
return NextResponse.json({ error: error.message }, { status: 404 });
|
||||
}
|
||||
if (error instanceof QuizAttemptValidationError) {
|
||||
return NextResponse.json({ error: error.message }, { status: 400 });
|
||||
}
|
||||
console.error("Failed to submit quiz attempt", error);
|
||||
return NextResponse.json({ error: "Failed to submit attempt" }, { status: 500 });
|
||||
}
|
||||
|
||||
// Determine which questions were included in this attempt
|
||||
const isPartialRetake = body.isPartialRetake === true;
|
||||
|
||||
// If partial retake, we only score the questions that actually had answers provided
|
||||
// or that were explicitly passed in a questionIds array.
|
||||
// For simplicity, we filter the quizSet questions down to what's in the answers object
|
||||
// if it's a partial retake, though the client will only show those anyway.
|
||||
let questionsToScore = quizSet.questions;
|
||||
if (isPartialRetake) {
|
||||
const answeredIds = Object.keys(answers);
|
||||
questionsToScore = quizSet.questions.filter(q => answeredIds.includes(q.id));
|
||||
}
|
||||
|
||||
// Format questions for the scoring utility
|
||||
const formattedQuestions = questionsToScore.map((q) => ({
|
||||
id: q.id,
|
||||
type: q.type as "MULTIPLE_CHOICE" | "SATA",
|
||||
options: q.options.map((o) => ({ id: o.id, isCorrect: o.isCorrect })),
|
||||
}));
|
||||
|
||||
const { total, maxScore } = scoreQuiz(formattedQuestions, answers);
|
||||
|
||||
const attempt = await createQuizAttempt({
|
||||
quizSetId: id,
|
||||
score: total,
|
||||
maxScore: maxScore,
|
||||
answersJson: body.answersJson,
|
||||
isPartialRetake,
|
||||
});
|
||||
|
||||
return NextResponse.json(attempt, { status: 201 });
|
||||
}
|
||||
|
||||
export async function GET(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue