Files
Edu_helper/backend/src/routes/tests.ts
T

163 lines
5.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { Router, Response } from "express";
import prisma from "../lib/prisma";
import { getDeepSeekClient, getPrompt } from "../lib/deepseek";
import type { AuthRequest } from "../middleware/auth";
const router = Router();
const DEFAULT_TEST_PROMPT = `Ты — составитель тестов. Сгенерируй тест из РОВНО 10 вопросов.
Требования к вопросам:
- Вопросы должны проверять ПОНИМАНИЕ темы, а не запоминание фактов
- Сложность: 3 лёгких, 4 средних, 3 сложных
- Каждый вопрос имеет 4 варианта ответа (a, b, c, d) и ровно один правильный
- Неправильные варианты должны быть правдоподобными (не абсурдными)
- Вопросы на русском языке
ВАЖНО: Верни ТОЛЬКО JSON-массив, без markdown-обёрток (\`\`\`json), без пояснений, ЧИСТЫЙ JSON:
[{"question":"текст вопроса","options":{"a":"вариант А","b":"вариант Б","c":"вариант В","d":"вариант Г"},"correct":"a"}]`;
/** Контекст пары: id назначенного ученика (общие тесты для наставника и ученика). */
function pairStudentScopeId(req: AuthRequest): number {
return req.studentId!;
}
const authorSelect = { username: true, displayName: true } as const;
router.get("/", async (req: AuthRequest, res: Response) => {
const scopeId = pairStudentScopeId(req);
const tests = await prisma.test.findMany({
where: { studentId: scopeId },
orderBy: { createdAt: "desc" },
include: {
author: { select: authorSelect },
results: {
where: { studentId: scopeId },
orderBy: { createdAt: "asc" },
},
},
});
res.json(tests);
});
router.get("/:id", async (req: AuthRequest, res: Response) => {
const id = parseInt(req.params.id as string, 10);
const scopeId = pairStudentScopeId(req);
const test = await prisma.test.findFirst({
where: { id, studentId: scopeId },
include: {
author: { select: authorSelect },
results: {
where: { studentId: scopeId },
orderBy: { createdAt: "asc" },
},
},
});
if (!test) {
res.status(404).json({ error: "Test not found" });
return;
}
res.json(test);
});
router.post("/generate", async (req: AuthRequest, res: Response) => {
const { topic, fromQuestions } = req.body;
const scopeId = pairStudentScopeId(req);
const questionBankStudentId = req.studentId!;
const client = await getDeepSeekClient();
const systemPrompt = await getPrompt("prompt_test", DEFAULT_TEST_PROMPT);
let userMessage: string;
if (fromQuestions) {
const questions = await prisma.question.findMany({
where: { studentId: questionBankStudentId },
orderBy: { createdAt: "desc" },
take: 20,
});
const questionTexts = questions.map((q: { text: string }) => q.text).join("\n");
userMessage = `Составь тест на основе этих вопросов, которые задавал пользователь ранее:\n${questionTexts}`;
} else {
if (!topic) {
res.status(400).json({ error: "Topic is required" });
return;
}
userMessage = `Тема: ${topic}`;
}
try {
const response = await client.chat.completions.create({
model: "deepseek-chat",
messages: [
{ role: "system", content: systemPrompt },
{ role: "user", content: userMessage },
],
});
const raw = response.choices[0]?.message?.content || "[]";
const jsonMatch = raw.match(/\[[\s\S]*\]/);
const questionsJson = jsonMatch ? jsonMatch[0] : "[]";
const test = await prisma.test.create({
data: {
topic: topic || "По прошлым вопросам",
questions: questionsJson,
studentId: scopeId,
authorId: req.user!.id,
},
include: {
author: { select: authorSelect },
results: true,
},
});
res.json(test);
} catch (err: any) {
res.status(500).json({ error: err.message });
}
});
router.post("/:id/submit", async (req: AuthRequest, res: Response) => {
const id = parseInt(req.params.id as string, 10);
const scopeId = pairStudentScopeId(req);
const { answers } = req.body as { answers: Record<string, string> };
const test = await prisma.test.findFirst({ where: { id, studentId: scopeId } });
if (!test) {
res.status(404).json({ error: "Test not found" });
return;
}
const questions = JSON.parse(test.questions);
let score = 0;
const total = questions.length;
for (const q of questions as { question: string; correct: string }[]) {
if (answers[q.question] === q.correct) {
score++;
}
}
if (req.user!.role === "TUTOR") {
res.json({ score, total, persisted: false as const });
return;
}
const result = await prisma.testResult.create({
data: {
testId: id,
studentId: req.user!.id,
answers: JSON.stringify(answers),
score,
total,
},
});
res.json({ ...result, persisted: true as const });
});
export default router;