Module Update
This commit is contained in:
182
src/modules/exams/data-access.ts
Normal file
182
src/modules/exams/data-access.ts
Normal file
@@ -0,0 +1,182 @@
|
||||
import { db } from "@/shared/db"
|
||||
import { exams, examQuestions, examSubmissions, submissionAnswers, users } from "@/shared/db/schema"
|
||||
import { eq, desc, like, and, or } from "drizzle-orm"
|
||||
import { cache } from "react"
|
||||
|
||||
import type { ExamStatus } from "./types"
|
||||
|
||||
export type GetExamsParams = {
|
||||
q?: string
|
||||
status?: string
|
||||
difficulty?: string
|
||||
page?: number
|
||||
pageSize?: number
|
||||
}
|
||||
|
||||
|
||||
export const getExams = cache(async (params: GetExamsParams) => {
|
||||
const conditions = []
|
||||
|
||||
if (params.q) {
|
||||
const search = `%${params.q}%`
|
||||
conditions.push(or(like(exams.title, search), like(exams.description, search)))
|
||||
}
|
||||
|
||||
if (params.status && params.status !== "all") {
|
||||
conditions.push(eq(exams.status, params.status as any))
|
||||
}
|
||||
|
||||
// Note: Difficulty is stored in JSON description field in current schema,
|
||||
// so we might need to filter in memory or adjust schema.
|
||||
// For now, let's fetch and filter in memory if difficulty is needed,
|
||||
// or just ignore strict DB filtering for JSON fields to keep it simple.
|
||||
|
||||
const data = await db.query.exams.findMany({
|
||||
where: conditions.length ? and(...conditions) : undefined,
|
||||
orderBy: [desc(exams.createdAt)],
|
||||
})
|
||||
|
||||
// Transform and Filter (especially for JSON fields)
|
||||
let result = data.map((exam) => {
|
||||
let meta: any = {}
|
||||
try {
|
||||
meta = JSON.parse(exam.description || "{}")
|
||||
} catch { }
|
||||
|
||||
return {
|
||||
id: exam.id,
|
||||
title: exam.title,
|
||||
status: (exam.status as ExamStatus) || "draft",
|
||||
subject: meta.subject || "General",
|
||||
grade: meta.grade || "General",
|
||||
difficulty: meta.difficulty || 1,
|
||||
totalScore: meta.totalScore || 100,
|
||||
durationMin: meta.durationMin || 60,
|
||||
questionCount: meta.questionCount || 0,
|
||||
scheduledAt: exam.startTime?.toISOString(),
|
||||
createdAt: exam.createdAt.toISOString(),
|
||||
tags: meta.tags || [],
|
||||
}
|
||||
})
|
||||
|
||||
if (params.difficulty && params.difficulty !== "all") {
|
||||
const d = parseInt(params.difficulty)
|
||||
result = result.filter((e) => e.difficulty === d)
|
||||
}
|
||||
|
||||
return result
|
||||
})
|
||||
|
||||
export const getExamById = cache(async (id: string) => {
|
||||
const exam = await db.query.exams.findFirst({
|
||||
where: eq(exams.id, id),
|
||||
with: {
|
||||
questions: {
|
||||
orderBy: (examQuestions, { asc }) => [asc(examQuestions.order)],
|
||||
with: {
|
||||
question: true
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if (!exam) return null
|
||||
|
||||
let meta: any = {}
|
||||
try {
|
||||
meta = JSON.parse(exam.description || "{}")
|
||||
} catch { }
|
||||
|
||||
return {
|
||||
id: exam.id,
|
||||
title: exam.title,
|
||||
status: (exam.status as ExamStatus) || "draft",
|
||||
subject: meta.subject || "General",
|
||||
grade: meta.grade || "General",
|
||||
difficulty: meta.difficulty || 1,
|
||||
totalScore: meta.totalScore || 100,
|
||||
durationMin: meta.durationMin || 60,
|
||||
scheduledAt: exam.startTime?.toISOString(),
|
||||
createdAt: exam.createdAt.toISOString(),
|
||||
tags: meta.tags || [],
|
||||
structure: exam.structure as any, // Return structure
|
||||
questions: exam.questions.map(eq => ({
|
||||
id: eq.questionId,
|
||||
score: eq.score,
|
||||
order: eq.order,
|
||||
// ... include question details if needed
|
||||
}))
|
||||
}
|
||||
})
|
||||
|
||||
export const getExamSubmissions = cache(async () => {
|
||||
const data = await db.query.examSubmissions.findMany({
|
||||
orderBy: [desc(examSubmissions.submittedAt)],
|
||||
with: {
|
||||
exam: true,
|
||||
student: true
|
||||
}
|
||||
})
|
||||
|
||||
return data.map(sub => ({
|
||||
id: sub.id,
|
||||
examId: sub.examId,
|
||||
examTitle: sub.exam.title,
|
||||
studentName: sub.student.name || "Unknown",
|
||||
submittedAt: sub.submittedAt ? sub.submittedAt.toISOString() : new Date().toISOString(),
|
||||
score: sub.score || undefined,
|
||||
status: sub.status as "pending" | "graded",
|
||||
}))
|
||||
})
|
||||
|
||||
export const getSubmissionDetails = cache(async (submissionId: string) => {
|
||||
const submission = await db.query.examSubmissions.findFirst({
|
||||
where: eq(examSubmissions.id, submissionId),
|
||||
with: {
|
||||
student: true,
|
||||
exam: true,
|
||||
}
|
||||
})
|
||||
|
||||
if (!submission) return null
|
||||
|
||||
// Fetch answers
|
||||
const answers = await db.query.submissionAnswers.findMany({
|
||||
where: eq(submissionAnswers.submissionId, submissionId),
|
||||
with: {
|
||||
question: true
|
||||
}
|
||||
})
|
||||
|
||||
// Fetch exam questions structure (to know max score and order)
|
||||
const examQ = await db.query.examQuestions.findMany({
|
||||
where: eq(examQuestions.examId, submission.examId),
|
||||
orderBy: [desc(examQuestions.order)],
|
||||
})
|
||||
|
||||
// Map answers with question details
|
||||
const answersWithDetails = answers.map(ans => {
|
||||
const eqRel = examQ.find(q => q.questionId === ans.questionId)
|
||||
return {
|
||||
id: ans.id,
|
||||
questionId: ans.questionId,
|
||||
questionContent: ans.question.content,
|
||||
questionType: ans.question.type,
|
||||
maxScore: eqRel?.score || 0,
|
||||
studentAnswer: ans.answerContent,
|
||||
score: ans.score,
|
||||
feedback: ans.feedback,
|
||||
order: eqRel?.order || 0
|
||||
}
|
||||
}).sort((a, b) => a.order - b.order)
|
||||
|
||||
return {
|
||||
id: submission.id,
|
||||
studentName: submission.student.name || "Unknown",
|
||||
examTitle: submission.exam.title,
|
||||
submittedAt: submission.submittedAt ? submission.submittedAt.toISOString() : null,
|
||||
status: submission.status,
|
||||
totalScore: submission.score,
|
||||
answers: answersWithDetails
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user