|
|
|
@ -17,27 +17,41 @@ const DEFAULT_TEST_PROMPT = `Ты — составитель тестов. Сг |
|
|
|
ВАЖНО: Верни ТОЛЬКО JSON-массив, без markdown-обёрток (\`\`\`json), без пояснений, ЧИСТЫЙ JSON:
|
|
|
|
ВАЖНО: Верни ТОЛЬКО JSON-массив, без markdown-обёрток (\`\`\`json), без пояснений, ЧИСТЫЙ JSON:
|
|
|
|
[{"question":"текст вопроса","options":{"a":"вариант А","b":"вариант Б","c":"вариант В","d":"вариант Г"},"correct":"a"}]`;
|
|
|
|
[{"question":"текст вопроса","options":{"a":"вариант А","b":"вариант Б","c":"вариант В","d":"вариант Г"},"correct":"a"}]`;
|
|
|
|
|
|
|
|
|
|
|
|
/** Тесты привязаны к текущему пользователю (ученик — свои; наставник — свои черновые, не к ученику). */ |
|
|
|
/** Контекст пары: id назначенного ученика (общие тесты для наставника и ученика). */ |
|
|
|
function testOwnerId(req: AuthRequest): number { |
|
|
|
function pairStudentScopeId(req: AuthRequest): number { |
|
|
|
return req.user!.id; |
|
|
|
return req.studentId!; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const authorSelect = { username: true, displayName: true } as const; |
|
|
|
|
|
|
|
|
|
|
|
router.get("/", async (req: AuthRequest, res: Response) => { |
|
|
|
router.get("/", async (req: AuthRequest, res: Response) => { |
|
|
|
const ownerId = testOwnerId(req); |
|
|
|
const scopeId = pairStudentScopeId(req); |
|
|
|
const tests = await prisma.test.findMany({ |
|
|
|
const tests = await prisma.test.findMany({ |
|
|
|
where: { studentId: ownerId }, |
|
|
|
where: { studentId: scopeId }, |
|
|
|
orderBy: { createdAt: "desc" }, |
|
|
|
orderBy: { createdAt: "desc" }, |
|
|
|
include: { results: true }, |
|
|
|
include: { |
|
|
|
|
|
|
|
author: { select: authorSelect }, |
|
|
|
|
|
|
|
results: { |
|
|
|
|
|
|
|
where: { studentId: scopeId }, |
|
|
|
|
|
|
|
orderBy: { createdAt: "asc" }, |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
}, |
|
|
|
}); |
|
|
|
}); |
|
|
|
res.json(tests); |
|
|
|
res.json(tests); |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
router.get("/:id", async (req: AuthRequest, res: Response) => { |
|
|
|
router.get("/:id", async (req: AuthRequest, res: Response) => { |
|
|
|
const id = parseInt(req.params.id as string, 10); |
|
|
|
const id = parseInt(req.params.id as string, 10); |
|
|
|
const ownerId = testOwnerId(req); |
|
|
|
const scopeId = pairStudentScopeId(req); |
|
|
|
const test = await prisma.test.findFirst({ |
|
|
|
const test = await prisma.test.findFirst({ |
|
|
|
where: { id, studentId: ownerId }, |
|
|
|
where: { id, studentId: scopeId }, |
|
|
|
include: { results: true }, |
|
|
|
include: { |
|
|
|
|
|
|
|
author: { select: authorSelect }, |
|
|
|
|
|
|
|
results: { |
|
|
|
|
|
|
|
where: { studentId: scopeId }, |
|
|
|
|
|
|
|
orderBy: { createdAt: "asc" }, |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
}, |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
if (!test) { |
|
|
|
if (!test) { |
|
|
|
@ -50,8 +64,7 @@ router.get("/:id", async (req: AuthRequest, res: Response) => { |
|
|
|
|
|
|
|
|
|
|
|
router.post("/generate", async (req: AuthRequest, res: Response) => { |
|
|
|
router.post("/generate", async (req: AuthRequest, res: Response) => { |
|
|
|
const { topic, fromQuestions } = req.body; |
|
|
|
const { topic, fromQuestions } = req.body; |
|
|
|
const ownerId = testOwnerId(req); |
|
|
|
const scopeId = pairStudentScopeId(req); |
|
|
|
/** Вопросы «по моим вопросам» берутся из базы назначенного ученика (для наставника — его ученик). */ |
|
|
|
|
|
|
|
const questionBankStudentId = req.studentId!; |
|
|
|
const questionBankStudentId = req.studentId!; |
|
|
|
|
|
|
|
|
|
|
|
const client = await getDeepSeekClient(); |
|
|
|
const client = await getDeepSeekClient(); |
|
|
|
@ -92,7 +105,12 @@ router.post("/generate", async (req: AuthRequest, res: Response) => { |
|
|
|
data: { |
|
|
|
data: { |
|
|
|
topic: topic || "По прошлым вопросам", |
|
|
|
topic: topic || "По прошлым вопросам", |
|
|
|
questions: questionsJson, |
|
|
|
questions: questionsJson, |
|
|
|
studentId: ownerId, |
|
|
|
studentId: scopeId, |
|
|
|
|
|
|
|
authorId: req.user!.id, |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
include: { |
|
|
|
|
|
|
|
author: { select: authorSelect }, |
|
|
|
|
|
|
|
results: true, |
|
|
|
}, |
|
|
|
}, |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
@ -104,10 +122,10 @@ router.post("/generate", async (req: AuthRequest, res: Response) => { |
|
|
|
|
|
|
|
|
|
|
|
router.post("/:id/submit", async (req: AuthRequest, res: Response) => { |
|
|
|
router.post("/:id/submit", async (req: AuthRequest, res: Response) => { |
|
|
|
const id = parseInt(req.params.id as string, 10); |
|
|
|
const id = parseInt(req.params.id as string, 10); |
|
|
|
const ownerId = testOwnerId(req); |
|
|
|
const scopeId = pairStudentScopeId(req); |
|
|
|
const { answers } = req.body as { answers: Record<string, string> }; |
|
|
|
const { answers } = req.body as { answers: Record<string, string> }; |
|
|
|
|
|
|
|
|
|
|
|
const test = await prisma.test.findFirst({ where: { id, studentId: ownerId } }); |
|
|
|
const test = await prisma.test.findFirst({ where: { id, studentId: scopeId } }); |
|
|
|
if (!test) { |
|
|
|
if (!test) { |
|
|
|
res.status(404).json({ error: "Test not found" }); |
|
|
|
res.status(404).json({ error: "Test not found" }); |
|
|
|
return; |
|
|
|
return; |
|
|
|
@ -131,6 +149,7 @@ router.post("/:id/submit", async (req: AuthRequest, res: Response) => { |
|
|
|
const result = await prisma.testResult.create({ |
|
|
|
const result = await prisma.testResult.create({ |
|
|
|
data: { |
|
|
|
data: { |
|
|
|
testId: id, |
|
|
|
testId: id, |
|
|
|
|
|
|
|
studentId: req.user!.id, |
|
|
|
answers: JSON.stringify(answers), |
|
|
|
answers: JSON.stringify(answers), |
|
|
|
score, |
|
|
|
score, |
|
|
|
total, |
|
|
|
total, |
|
|
|
|