@studious-lms/server 1.4.2 → 1.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.coderabbit.yaml +4 -1
- package/.vscode/settings.json +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +68 -29
- package/dist/index.js.map +1 -1
- package/dist/lib/fileUpload.d.ts +10 -2
- package/dist/lib/fileUpload.d.ts.map +1 -1
- package/dist/lib/fileUpload.js +38 -6
- package/dist/lib/fileUpload.js.map +1 -1
- package/dist/middleware/auth.d.ts.map +1 -1
- package/dist/middleware/auth.js +18 -5
- package/dist/middleware/auth.js.map +1 -1
- package/dist/models/auth.d.ts +19 -14
- package/dist/models/auth.d.ts.map +1 -1
- package/dist/models/auth.js +53 -30
- package/dist/models/auth.js.map +1 -1
- package/dist/models/class.d.ts +3 -1
- package/dist/models/class.d.ts.map +1 -1
- package/dist/models/class.js +16 -4
- package/dist/models/class.js.map +1 -1
- package/dist/models/notification.d.ts +11 -10
- package/dist/models/notification.d.ts.map +1 -1
- package/dist/models/notification.js +13 -9
- package/dist/models/notification.js.map +1 -1
- package/dist/models/worksheet.d.ts +379 -7
- package/dist/models/worksheet.d.ts.map +1 -1
- package/dist/models/worksheet.js +168 -2
- package/dist/models/worksheet.js.map +1 -1
- package/dist/pipelines/aiLabChat.d.ts.map +1 -1
- package/dist/pipelines/aiLabChat.js +11 -3
- package/dist/pipelines/aiLabChat.js.map +1 -1
- package/dist/pipelines/aiLabChatContract.d.ts +3 -3
- package/dist/pipelines/aiNewtonChat.d.ts.map +1 -1
- package/dist/pipelines/aiNewtonChat.js +25 -14
- package/dist/pipelines/aiNewtonChat.js.map +1 -1
- package/dist/pipelines/gradeWorksheet.d.ts.map +1 -1
- package/dist/pipelines/gradeWorksheet.js +10 -3
- package/dist/pipelines/gradeWorksheet.js.map +1 -1
- package/dist/pipelines/pdfToWorksheet.d.ts +12 -0
- package/dist/pipelines/pdfToWorksheet.d.ts.map +1 -0
- package/dist/pipelines/pdfToWorksheet.js +155 -0
- package/dist/pipelines/pdfToWorksheet.js.map +1 -0
- package/dist/routers/_app.d.ts +264 -16
- package/dist/routers/_app.d.ts.map +1 -1
- package/dist/routers/class.d.ts +2 -1
- package/dist/routers/class.d.ts.map +1 -1
- package/dist/routers/class.js +3 -3
- package/dist/routers/class.js.map +1 -1
- package/dist/routers/labChat.d.ts +6 -0
- package/dist/routers/labChat.d.ts.map +1 -1
- package/dist/routers/labChat.js +5 -3
- package/dist/routers/labChat.js.map +1 -1
- package/dist/routers/message.d.ts +31 -0
- package/dist/routers/message.d.ts.map +1 -1
- package/dist/routers/message.js +27 -4
- package/dist/routers/message.js.map +1 -1
- package/dist/routers/newtonChat.d.ts +6 -0
- package/dist/routers/newtonChat.d.ts.map +1 -1
- package/dist/routers/newtonChat.js +5 -4
- package/dist/routers/newtonChat.js.map +1 -1
- package/dist/routers/notifications.d.ts +9 -2
- package/dist/routers/notifications.d.ts.map +1 -1
- package/dist/routers/notifications.js +4 -4
- package/dist/routers/notifications.js.map +1 -1
- package/dist/routers/worksheet.d.ts +78 -5
- package/dist/routers/worksheet.d.ts.map +1 -1
- package/dist/routers/worksheet.js +126 -17
- package/dist/routers/worksheet.js.map +1 -1
- package/dist/services/auth.d.ts +1 -1
- package/dist/services/auth.d.ts.map +1 -1
- package/dist/services/auth.js +19 -12
- package/dist/services/auth.js.map +1 -1
- package/dist/services/class.d.ts +1 -1
- package/dist/services/class.d.ts.map +1 -1
- package/dist/services/class.js +6 -3
- package/dist/services/class.js.map +1 -1
- package/dist/services/file.d.ts.map +1 -1
- package/dist/services/file.js +7 -2
- package/dist/services/file.js.map +1 -1
- package/dist/services/labChat.d.ts +6 -0
- package/dist/services/labChat.d.ts.map +1 -1
- package/dist/services/labChat.js +30 -18
- package/dist/services/labChat.js.map +1 -1
- package/dist/services/message.d.ts +30 -0
- package/dist/services/message.d.ts.map +1 -1
- package/dist/services/message.js +97 -21
- package/dist/services/message.js.map +1 -1
- package/dist/services/newtonChat.d.ts +6 -1
- package/dist/services/newtonChat.d.ts.map +1 -1
- package/dist/services/newtonChat.js +36 -18
- package/dist/services/newtonChat.js.map +1 -1
- package/dist/services/notification.d.ts +12 -5
- package/dist/services/notification.d.ts.map +1 -1
- package/dist/services/notification.js +24 -9
- package/dist/services/notification.js.map +1 -1
- package/dist/services/studentProgress.d.ts.map +1 -1
- package/dist/services/studentProgress.js +6 -3
- package/dist/services/studentProgress.js.map +1 -1
- package/dist/services/worksheet.d.ts +36 -15
- package/dist/services/worksheet.d.ts.map +1 -1
- package/dist/services/worksheet.js +186 -54
- package/dist/services/worksheet.js.map +1 -1
- package/dist/trpc.d.ts.map +1 -1
- package/dist/trpc.js +11 -17
- package/dist/trpc.js.map +1 -1
- package/dist/utils/inference.d.ts +10 -1
- package/dist/utils/inference.d.ts.map +1 -1
- package/dist/utils/inference.js +186 -50
- package/dist/utils/inference.js.map +1 -1
- package/package.json +1 -1
- package/prisma/migrations/20260415103500_add_session_type/migration.sql +33 -0
- package/prisma/migrations/20260415133000_add_ai_usage_logs/migration.sql +19 -0
- package/prisma/schema.prisma +27 -1
- package/scripts/test-pre-push.ts +6 -2
- package/src/index.ts +83 -30
- package/src/lib/fileUpload.ts +55 -5
- package/src/middleware/auth.ts +19 -4
- package/src/models/auth.ts +59 -29
- package/src/models/class.ts +15 -4
- package/src/models/notification.ts +13 -7
- package/src/models/worksheet.ts +203 -0
- package/src/pipelines/aiLabChat.ts +9 -1
- package/src/pipelines/aiNewtonChat.ts +24 -14
- package/src/pipelines/gradeWorksheet.ts +8 -0
- package/src/pipelines/pdfToWorksheet.ts +177 -0
- package/src/routers/class.ts +2 -2
- package/src/routers/labChat.ts +3 -1
- package/src/routers/message.ts +38 -1
- package/src/routers/newtonChat.ts +4 -2
- package/src/routers/notifications.ts +2 -2
- package/src/routers/worksheet.ts +174 -19
- package/src/services/auth.ts +26 -12
- package/src/services/class.ts +4 -1
- package/src/services/file.ts +6 -0
- package/src/services/labChat.ts +42 -18
- package/src/services/message.ts +163 -18
- package/src/services/newtonChat.ts +49 -18
- package/src/services/notification.ts +27 -8
- package/src/services/studentProgress.ts +4 -1
- package/src/services/worksheet.ts +252 -51
- package/src/trpc.ts +12 -18
- package/src/utils/inference.ts +280 -56
- package/tests/globalSetup.ts +13 -4
- package/tests/pipelines/pdfToWorksheet.test.ts +176 -0
- package/tests/routers/auth.test.ts +120 -17
- package/tests/routers/class.test.ts +7 -1
- package/tests/routers/message.test.ts +236 -9
- package/tests/routers/newtonChat.test.ts +81 -0
- package/tests/routers/notification.test.ts +12 -0
- package/tests/routers/worksheet.test.ts +194 -0
- package/tests/setup.ts +22 -5
- package/tests/utils/inference.test.ts +255 -0
|
@@ -3,22 +3,47 @@
|
|
|
3
3
|
* Integrates with gradeWorksheet pipeline for auto-grading.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
6
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="2fcbad58-5215-5f4a-a912-82db38a85645")}catch(e){}}();
|
|
7
7
|
import { TRPCError } from "@trpc/server";
|
|
8
8
|
import { GenerationStatus } from "@prisma/client";
|
|
9
9
|
import { prisma } from "../lib/prisma.js";
|
|
10
|
+
import { getFileUrl } from "../lib/fileUpload.js";
|
|
10
11
|
import { cancelGradePipeline, regradeWorksheetPipeline } from "../pipelines/gradeWorksheet.js";
|
|
11
|
-
import {
|
|
12
|
+
import { pdfToWorksheet } from "../pipelines/pdfToWorksheet.js";
|
|
13
|
+
import { findWorksheetByIdMinimal, findWorksheetByIdForClassMember, findWorksheetByIdForTeacher, findWorksheetsByClassId, createWorksheet, updateWorksheet, deleteWorksheet, findQuestionsByWorksheetId, createWorksheetQuestion, updateWorksheetQuestion, deleteWorksheetQuestion, findSubmissionByIdForWorksheetAccess, findOrCreateWorksheetResponse, findWorksheetResponseByIdForTeacher, findWorksheetResponseWithResponsesForStudent, findWorksheetQuestionByIdForTeacher, findWorksheetQuestionByIdInWorksheet, updateStudentQuestionProgress, createStudentQuestionProgress, findStudentQuestionProgress, updateStudentQuestionProgressForGrading, createStudentQuestionProgressForGrading, createComment, findStudentQuestionProgressByIdForTeacher, findStudentQuestionProgressByIdForTeacherAndResponse, } from "../models/worksheet.js";
|
|
14
|
+
const sanitizeQuestionOptionsForStudent = (options) => {
|
|
15
|
+
if (!Array.isArray(options))
|
|
16
|
+
return options;
|
|
17
|
+
return options.map((option) => {
|
|
18
|
+
if (!option || typeof option !== "object")
|
|
19
|
+
return option;
|
|
20
|
+
const { isCorrect: _isCorrect, ...rest } = option;
|
|
21
|
+
return rest;
|
|
22
|
+
});
|
|
23
|
+
};
|
|
24
|
+
const sanitizeWorksheetForStudent = (worksheet) => ({
|
|
25
|
+
...worksheet,
|
|
26
|
+
questions: worksheet.questions.map((question) => ({
|
|
27
|
+
...question,
|
|
28
|
+
answer: "",
|
|
29
|
+
markScheme: null,
|
|
30
|
+
options: sanitizeQuestionOptionsForStudent(question.options),
|
|
31
|
+
})),
|
|
32
|
+
});
|
|
12
33
|
/** Get worksheet with questions and mark schemes. */
|
|
13
|
-
export async function getWorksheet(worksheetId) {
|
|
14
|
-
const
|
|
15
|
-
if (
|
|
34
|
+
export async function getWorksheet(worksheetId, userId) {
|
|
35
|
+
const teacherWorksheet = await findWorksheetByIdForTeacher(worksheetId, userId);
|
|
36
|
+
if (teacherWorksheet) {
|
|
37
|
+
return teacherWorksheet;
|
|
38
|
+
}
|
|
39
|
+
const studentWorksheet = await findWorksheetByIdForClassMember(worksheetId, userId);
|
|
40
|
+
if (!studentWorksheet) {
|
|
16
41
|
throw new TRPCError({
|
|
17
|
-
code: "
|
|
18
|
-
message: "
|
|
42
|
+
code: "FORBIDDEN",
|
|
43
|
+
message: "You do not have access to this worksheet",
|
|
19
44
|
});
|
|
20
45
|
}
|
|
21
|
-
return
|
|
46
|
+
return sanitizeWorksheetForStudent(studentWorksheet);
|
|
22
47
|
}
|
|
23
48
|
/** Check if worksheet exists. */
|
|
24
49
|
export async function worksheetExists(id) {
|
|
@@ -32,21 +57,81 @@ export async function listWorksheets(classId) {
|
|
|
32
57
|
questionCount: w.questions.length,
|
|
33
58
|
}));
|
|
34
59
|
}
|
|
35
|
-
export async function updateWorksheetRecord(worksheetId, data) {
|
|
60
|
+
export async function updateWorksheetRecord(worksheetId, userId, data) {
|
|
61
|
+
const worksheet = await findWorksheetByIdForTeacher(worksheetId, userId);
|
|
62
|
+
if (!worksheet) {
|
|
63
|
+
throw new TRPCError({
|
|
64
|
+
code: "FORBIDDEN",
|
|
65
|
+
message: "Only class teachers can update worksheets",
|
|
66
|
+
});
|
|
67
|
+
}
|
|
36
68
|
return updateWorksheet(worksheetId, data);
|
|
37
69
|
}
|
|
38
|
-
export async function deleteWorksheetRecord(worksheetId) {
|
|
70
|
+
export async function deleteWorksheetRecord(worksheetId, userId) {
|
|
71
|
+
const worksheet = await findWorksheetByIdForTeacher(worksheetId, userId);
|
|
72
|
+
if (!worksheet) {
|
|
73
|
+
throw new TRPCError({
|
|
74
|
+
code: "FORBIDDEN",
|
|
75
|
+
message: "Only class teachers can delete worksheets",
|
|
76
|
+
});
|
|
77
|
+
}
|
|
39
78
|
return deleteWorksheet(worksheetId);
|
|
40
79
|
}
|
|
41
80
|
export async function createWorksheetRecord(classId, name) {
|
|
42
81
|
return createWorksheet({ classId, name });
|
|
43
82
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
83
|
+
/**
|
|
84
|
+
* Create a worksheet by converting a PDF (URL) into structured questions.
|
|
85
|
+
* Uses the `pdfToWorksheet` pipeline to do inference + persistence.
|
|
86
|
+
*/
|
|
87
|
+
export async function createWorksheetFromPdf(classId, url) {
|
|
88
|
+
const created = await pdfToWorksheet(url, classId);
|
|
89
|
+
if (!created) {
|
|
90
|
+
throw new TRPCError({
|
|
91
|
+
code: "INTERNAL_SERVER_ERROR",
|
|
92
|
+
message: "Failed to create worksheet from PDF",
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
return created;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Create a worksheet from a PDF that was uploaded through the direct-upload flow.
|
|
99
|
+
*/
|
|
100
|
+
export async function createWorksheetFromUploadedPdf(classId, fileId, userId) {
|
|
101
|
+
const file = await prisma.file.findFirst({
|
|
102
|
+
where: {
|
|
103
|
+
id: fileId,
|
|
104
|
+
userId,
|
|
105
|
+
uploadStatus: "COMPLETED",
|
|
106
|
+
},
|
|
107
|
+
select: {
|
|
108
|
+
id: true,
|
|
109
|
+
path: true,
|
|
110
|
+
type: true,
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
if (!file) {
|
|
47
114
|
throw new TRPCError({
|
|
48
115
|
code: "NOT_FOUND",
|
|
49
|
-
message: "
|
|
116
|
+
message: "Uploaded file not found or not ready",
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
const isPdf = file.type === "application/pdf" || file.path.toLowerCase().endsWith(".pdf");
|
|
120
|
+
if (!isPdf) {
|
|
121
|
+
throw new TRPCError({
|
|
122
|
+
code: "BAD_REQUEST",
|
|
123
|
+
message: "Only PDF files can be converted into worksheets",
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
const signedUrl = await getFileUrl(file.path);
|
|
127
|
+
return createWorksheetFromPdf(classId, signedUrl);
|
|
128
|
+
}
|
|
129
|
+
export async function addQuestionToWorksheet(worksheetId, userId, data) {
|
|
130
|
+
const worksheet = await findWorksheetByIdForTeacher(worksheetId, userId);
|
|
131
|
+
if (!worksheet) {
|
|
132
|
+
throw new TRPCError({
|
|
133
|
+
code: "FORBIDDEN",
|
|
134
|
+
message: "Only class teachers can modify worksheet questions",
|
|
50
135
|
});
|
|
51
136
|
}
|
|
52
137
|
return createWorksheetQuestion({
|
|
@@ -54,12 +139,12 @@ export async function addQuestionToWorksheet(worksheetId, data) {
|
|
|
54
139
|
...data,
|
|
55
140
|
});
|
|
56
141
|
}
|
|
57
|
-
export async function reorderWorksheetQuestions(worksheetId, movedId, position, targetId) {
|
|
58
|
-
const worksheet = await
|
|
142
|
+
export async function reorderWorksheetQuestions(worksheetId, userId, movedId, position, targetId) {
|
|
143
|
+
const worksheet = await findWorksheetByIdForTeacher(worksheetId, userId);
|
|
59
144
|
if (!worksheet) {
|
|
60
145
|
throw new TRPCError({
|
|
61
|
-
code: "
|
|
62
|
-
message: "
|
|
146
|
+
code: "FORBIDDEN",
|
|
147
|
+
message: "Only class teachers can reorder worksheet questions",
|
|
63
148
|
});
|
|
64
149
|
}
|
|
65
150
|
const questions = await findQuestionsByWorksheetId(worksheetId);
|
|
@@ -99,51 +184,64 @@ export async function reorderWorksheetQuestions(worksheetId, movedId, position,
|
|
|
99
184
|
})));
|
|
100
185
|
return next;
|
|
101
186
|
}
|
|
102
|
-
export async function updateWorksheetQuestionRecord(worksheetId, questionId, data) {
|
|
103
|
-
const
|
|
104
|
-
if (!
|
|
187
|
+
export async function updateWorksheetQuestionRecord(worksheetId, questionId, userId, data) {
|
|
188
|
+
const question = await findWorksheetQuestionByIdForTeacher(questionId, userId);
|
|
189
|
+
if (!question || question.worksheetId !== worksheetId) {
|
|
105
190
|
throw new TRPCError({
|
|
106
|
-
code: "
|
|
107
|
-
message: "
|
|
191
|
+
code: "FORBIDDEN",
|
|
192
|
+
message: "Only class teachers can update worksheet questions",
|
|
108
193
|
});
|
|
109
194
|
}
|
|
110
195
|
return updateWorksheetQuestion(questionId, data);
|
|
111
196
|
}
|
|
112
|
-
export async function deleteWorksheetQuestionRecord(worksheetId, questionId) {
|
|
113
|
-
const
|
|
114
|
-
if (!
|
|
197
|
+
export async function deleteWorksheetQuestionRecord(worksheetId, questionId, userId) {
|
|
198
|
+
const question = await findWorksheetQuestionByIdForTeacher(questionId, userId);
|
|
199
|
+
if (!question || question.worksheetId !== worksheetId) {
|
|
115
200
|
throw new TRPCError({
|
|
116
|
-
code: "
|
|
117
|
-
message: "
|
|
201
|
+
code: "FORBIDDEN",
|
|
202
|
+
message: "Only class teachers can delete worksheet questions",
|
|
118
203
|
});
|
|
119
204
|
}
|
|
120
205
|
return deleteWorksheetQuestion(questionId);
|
|
121
206
|
}
|
|
122
|
-
export async function getWorksheetSubmission(worksheetId, submissionId) {
|
|
123
|
-
const
|
|
207
|
+
export async function getWorksheetSubmission(worksheetId, submissionId, userId) {
|
|
208
|
+
const worksheet = await findWorksheetByIdForClassMember(worksheetId, userId);
|
|
209
|
+
if (!worksheet) {
|
|
210
|
+
throw new TRPCError({
|
|
211
|
+
code: "FORBIDDEN",
|
|
212
|
+
message: "You do not have access to this worksheet",
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
const submission = await findSubmissionByIdForWorksheetAccess(submissionId, worksheetId, worksheet.classId, userId);
|
|
124
216
|
if (!submission) {
|
|
125
217
|
throw new TRPCError({
|
|
126
|
-
code: "
|
|
127
|
-
message: "
|
|
218
|
+
code: "FORBIDDEN",
|
|
219
|
+
message: "You do not have access to this worksheet submission",
|
|
128
220
|
});
|
|
129
221
|
}
|
|
130
222
|
return findOrCreateWorksheetResponse(submissionId, worksheetId, submission.studentId);
|
|
131
223
|
}
|
|
132
224
|
export async function answerWorksheetQuestion(worksheetResponseId, questionId, response, userId) {
|
|
133
|
-
const worksheetResponse = await
|
|
225
|
+
const worksheetResponse = await findWorksheetResponseWithResponsesForStudent(worksheetResponseId, userId, questionId);
|
|
134
226
|
if (!worksheetResponse) {
|
|
227
|
+
throw new TRPCError({
|
|
228
|
+
code: "FORBIDDEN",
|
|
229
|
+
message: "You can only answer your own worksheet responses",
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
const question = await findWorksheetQuestionByIdInWorksheet(questionId, worksheetResponse.worksheetId);
|
|
233
|
+
if (!question) {
|
|
135
234
|
throw new TRPCError({
|
|
136
235
|
code: "NOT_FOUND",
|
|
137
|
-
message: "Worksheet
|
|
236
|
+
message: "Worksheet question not found",
|
|
138
237
|
});
|
|
139
238
|
}
|
|
140
|
-
const
|
|
141
|
-
const
|
|
142
|
-
const marksAwardedIfCorrect = question?.points || 0;
|
|
239
|
+
const isMarkableByAlgo = question.type === "MULTIPLE_CHOICE" || question.type === "TRUE_FALSE";
|
|
240
|
+
const marksAwardedIfCorrect = question.points || 0;
|
|
143
241
|
const correctAnswer = isMarkableByAlgo
|
|
144
|
-
? question
|
|
145
|
-
? question
|
|
146
|
-
: question
|
|
242
|
+
? question.type === "MULTIPLE_CHOICE"
|
|
243
|
+
? question.options?.find((o) => o.isCorrect)?.id
|
|
244
|
+
: question.answer?.toString()
|
|
147
245
|
: null;
|
|
148
246
|
const existingResponse = worksheetResponse.responses[0];
|
|
149
247
|
if (existingResponse) {
|
|
@@ -178,14 +276,51 @@ export async function answerWorksheetQuestion(worksheetResponseId, questionId, r
|
|
|
178
276
|
});
|
|
179
277
|
return updated;
|
|
180
278
|
}
|
|
181
|
-
export async function cancelGrading(worksheetResponseId, progressId) {
|
|
279
|
+
export async function cancelGrading(worksheetResponseId, progressId, userId) {
|
|
280
|
+
const progress = await findStudentQuestionProgressByIdForTeacherAndResponse(progressId, worksheetResponseId, userId);
|
|
281
|
+
if (!progress) {
|
|
282
|
+
throw new TRPCError({
|
|
283
|
+
code: "FORBIDDEN",
|
|
284
|
+
message: "Only class teachers can cancel worksheet grading",
|
|
285
|
+
});
|
|
286
|
+
}
|
|
182
287
|
return cancelGradePipeline(worksheetResponseId, progressId);
|
|
183
288
|
}
|
|
184
|
-
export async function regradeQuestion(worksheetResponseId, progressId) {
|
|
289
|
+
export async function regradeQuestion(worksheetResponseId, progressId, userId) {
|
|
290
|
+
const progress = await findStudentQuestionProgressByIdForTeacherAndResponse(progressId, worksheetResponseId, userId);
|
|
291
|
+
if (!progress) {
|
|
292
|
+
throw new TRPCError({
|
|
293
|
+
code: "FORBIDDEN",
|
|
294
|
+
message: "Only class teachers can regrade worksheet answers",
|
|
295
|
+
});
|
|
296
|
+
}
|
|
185
297
|
return regradeWorksheetPipeline(worksheetResponseId, progressId);
|
|
186
298
|
}
|
|
187
|
-
export async function gradeAnswer(questionId, studentWorksheetResponseId, data) {
|
|
299
|
+
export async function gradeAnswer(questionId, studentWorksheetResponseId, userId, data) {
|
|
300
|
+
const worksheetResponse = await findWorksheetResponseByIdForTeacher(studentWorksheetResponseId, userId);
|
|
301
|
+
if (!worksheetResponse) {
|
|
302
|
+
throw new TRPCError({
|
|
303
|
+
code: "FORBIDDEN",
|
|
304
|
+
message: "Only class teachers can grade worksheet answers",
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
const question = await findWorksheetQuestionByIdInWorksheet(questionId, worksheetResponse.worksheetId);
|
|
308
|
+
if (!question) {
|
|
309
|
+
throw new TRPCError({
|
|
310
|
+
code: "NOT_FOUND",
|
|
311
|
+
message: "Worksheet question not found",
|
|
312
|
+
});
|
|
313
|
+
}
|
|
188
314
|
if (data.responseId) {
|
|
315
|
+
const existingProgress = await findStudentQuestionProgressByIdForTeacher(data.responseId, userId);
|
|
316
|
+
if (!existingProgress ||
|
|
317
|
+
existingProgress.studentWorksheetResponseId !== studentWorksheetResponseId ||
|
|
318
|
+
existingProgress.questionId !== questionId) {
|
|
319
|
+
throw new TRPCError({
|
|
320
|
+
code: "FORBIDDEN",
|
|
321
|
+
message: "Only class teachers can grade worksheet answers",
|
|
322
|
+
});
|
|
323
|
+
}
|
|
189
324
|
return updateStudentQuestionProgressForGrading(data.responseId, {
|
|
190
325
|
isCorrect: data.isCorrect,
|
|
191
326
|
feedback: data.feedback,
|
|
@@ -193,16 +328,6 @@ export async function gradeAnswer(questionId, studentWorksheetResponseId, data)
|
|
|
193
328
|
points: data.points,
|
|
194
329
|
});
|
|
195
330
|
}
|
|
196
|
-
const worksheetResponse = await prisma.studentWorksheetResponse.findUnique({
|
|
197
|
-
where: { id: studentWorksheetResponseId },
|
|
198
|
-
select: { studentId: true },
|
|
199
|
-
});
|
|
200
|
-
if (!worksheetResponse) {
|
|
201
|
-
throw new TRPCError({
|
|
202
|
-
code: "NOT_FOUND",
|
|
203
|
-
message: "Student worksheet response not found",
|
|
204
|
-
});
|
|
205
|
-
}
|
|
206
331
|
const existing = await findStudentQuestionProgress(worksheetResponse.studentId, questionId, studentWorksheetResponseId);
|
|
207
332
|
if (existing) {
|
|
208
333
|
return updateStudentQuestionProgressForGrading(existing.id, {
|
|
@@ -225,6 +350,13 @@ export async function gradeAnswer(questionId, studentWorksheetResponseId, data)
|
|
|
225
350
|
});
|
|
226
351
|
}
|
|
227
352
|
export async function addCommentToResponse(responseId, comment, authorId) {
|
|
353
|
+
const progress = await findStudentQuestionProgressByIdForTeacher(responseId, authorId);
|
|
354
|
+
if (!progress) {
|
|
355
|
+
throw new TRPCError({
|
|
356
|
+
code: "FORBIDDEN",
|
|
357
|
+
message: "Only class teachers can comment on worksheet responses",
|
|
358
|
+
});
|
|
359
|
+
}
|
|
228
360
|
return createComment({
|
|
229
361
|
studentQuestionProgressId: responseId,
|
|
230
362
|
content: comment,
|
|
@@ -232,4 +364,4 @@ export async function addCommentToResponse(responseId, comment, authorId) {
|
|
|
232
364
|
});
|
|
233
365
|
}
|
|
234
366
|
//# sourceMappingURL=worksheet.js.map
|
|
235
|
-
//# debugId=
|
|
367
|
+
//# debugId=2fcbad58-5215-5f4a-a912-82db38a85645
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worksheet.js","sources":["services/worksheet.ts"],"sourceRoot":"/","sourcesContent":["/**\n * Worksheet service – CRUD for worksheets, questions, submissions, and AI grading.\n * Integrates with gradeWorksheet pipeline for auto-grading.\n */\nimport { TRPCError } from \"@trpc/server\";\nimport { GenerationStatus, type WorksheetQuestionType } from \"@prisma/client\";\nimport { prisma } from \"../lib/prisma.js\";\nimport { cancelGradePipeline, regradeWorksheetPipeline } from \"../pipelines/gradeWorksheet.js\";\nimport {\n findWorksheetById,\n findWorksheetByIdMinimal,\n findWorksheetsByClassId,\n createWorksheet,\n updateWorksheet,\n deleteWorksheet,\n findQuestionsByWorksheetId,\n createWorksheetQuestion,\n updateWorksheetQuestion,\n deleteWorksheetQuestion,\n findSubmissionById,\n findOrCreateWorksheetResponse,\n findWorksheetResponseWithResponses,\n findWorksheetQuestionById,\n updateStudentQuestionProgress,\n createStudentQuestionProgress,\n findStudentQuestionProgress,\n updateStudentQuestionProgressForGrading,\n createStudentQuestionProgressForGrading,\n createComment,\n} from \"../models/worksheet.js\";\n\ntype MCQOptions = { id: string; text: string; isCorrect: boolean }[];\n\n/** Get worksheet with questions and mark schemes. */\nexport async function getWorksheet(worksheetId: string) {\n const worksheet = await findWorksheetById(worksheetId);\n if (!worksheet) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Worksheet not found\",\n });\n }\n return worksheet;\n}\n\n/** Check if worksheet exists. */\nexport async function worksheetExists(id: string) {\n const worksheet = await findWorksheetByIdMinimal(id);\n return !!worksheet;\n}\n\nexport async function listWorksheets(classId: string) {\n const worksheets = await findWorksheetsByClassId(classId);\n return worksheets.map((w) => ({\n ...w,\n questionCount: w.questions.length,\n }));\n}\n\nexport async function updateWorksheetRecord(\n worksheetId: string,\n data: { name?: string }\n) {\n return updateWorksheet(worksheetId, data);\n}\n\nexport async function deleteWorksheetRecord(worksheetId: string) {\n return deleteWorksheet(worksheetId);\n}\n\nexport async function createWorksheetRecord(classId: string, name: string) {\n return createWorksheet({ classId, name });\n}\n\nexport async function addQuestionToWorksheet(\n worksheetId: string,\n data: {\n question: string;\n answer: string;\n points?: number;\n options?: object;\n markScheme?: object;\n type: WorksheetQuestionType;\n }\n) {\n const worksheet = await findWorksheetByIdMinimal(worksheetId);\n if (!worksheet) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Worksheet not found\",\n });\n }\n return createWorksheetQuestion({\n worksheetId,\n ...data,\n });\n}\n\nexport async function reorderWorksheetQuestions(\n worksheetId: string,\n movedId: string,\n position: \"before\" | \"after\",\n targetId: string\n) {\n const worksheet = await findWorksheetByIdMinimal(worksheetId);\n if (!worksheet) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Worksheet not found\",\n });\n }\n\n const questions = await findQuestionsByWorksheetId(worksheetId);\n const movedIdx = questions.findIndex((q) => q.id === movedId);\n if (movedIdx === -1) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Moved question not found\",\n });\n }\n const targetIdx = questions.findIndex((q) => q.id === targetId);\n if (targetIdx === -1) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Target question not found\",\n });\n }\n\n const withoutMoved = questions.filter((q) => q.id !== movedId);\n const next =\n position === \"before\"\n ? [\n ...withoutMoved.slice(0, targetIdx).map((item) => ({ id: item.id })),\n { id: movedId },\n ...withoutMoved.slice(targetIdx).map((item) => ({ id: item.id })),\n ]\n : [\n ...withoutMoved\n .slice(0, targetIdx + 1)\n .map((item) => ({ id: item.id })),\n { id: movedId },\n ...withoutMoved\n .slice(targetIdx + 1)\n .map((item) => ({ id: item.id })),\n ];\n\n await prisma.$transaction(\n next.map((item, index) =>\n prisma.worksheetQuestion.update({\n where: { id: item.id },\n data: { order: index },\n })\n )\n );\n return next;\n}\n\nexport async function updateWorksheetQuestionRecord(\n worksheetId: string,\n questionId: string,\n data: {\n question?: string;\n answer?: string;\n points?: number;\n options?: object;\n markScheme?: object;\n type?: WorksheetQuestionType;\n }\n) {\n const worksheet = await findWorksheetByIdMinimal(worksheetId);\n if (!worksheet) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Worksheet not found\",\n });\n }\n return updateWorksheetQuestion(questionId, data);\n}\n\nexport async function deleteWorksheetQuestionRecord(\n worksheetId: string,\n questionId: string\n) {\n const worksheet = await findWorksheetByIdMinimal(worksheetId);\n if (!worksheet) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Worksheet not found\",\n });\n }\n return deleteWorksheetQuestion(questionId);\n}\n\nexport async function getWorksheetSubmission(\n worksheetId: string,\n submissionId: string\n) {\n const submission = await findSubmissionById(submissionId);\n if (!submission) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Submission not found\",\n });\n }\n return findOrCreateWorksheetResponse(\n submissionId,\n worksheetId,\n submission.studentId\n );\n}\n\nexport async function answerWorksheetQuestion(\n worksheetResponseId: string,\n questionId: string,\n response: string,\n userId: string\n) {\n const worksheetResponse = await findWorksheetResponseWithResponses(\n worksheetResponseId,\n questionId\n );\n if (!worksheetResponse) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Worksheet response not found\",\n });\n }\n\n const question = await findWorksheetQuestionById(questionId);\n const isMarkableByAlgo =\n question?.type === \"MULTIPLE_CHOICE\" || question?.type === \"TRUE_FALSE\";\n const marksAwardedIfCorrect = question?.points || 0;\n const correctAnswer = isMarkableByAlgo\n ? question?.type === \"MULTIPLE_CHOICE\"\n ? (question?.options as MCQOptions)?.find((o) => o.isCorrect)?.id\n : question?.answer?.toString()\n : null;\n\n const existingResponse = worksheetResponse.responses[0];\n\n if (existingResponse) {\n await updateStudentQuestionProgress(existingResponse.id, {\n response,\n ...(isMarkableByAlgo && {\n isCorrect: response === correctAnswer,\n }),\n ...(isMarkableByAlgo && {\n points: response === correctAnswer ? marksAwardedIfCorrect : 0,\n }),\n status: GenerationStatus.NOT_STARTED,\n });\n } else {\n await createStudentQuestionProgress({\n studentId: worksheetResponse.studentId,\n questionId,\n response,\n studentWorksheetResponseId: worksheetResponseId,\n ...(isMarkableByAlgo && {\n isCorrect: response === correctAnswer,\n }),\n ...(isMarkableByAlgo && {\n points: response === correctAnswer ? marksAwardedIfCorrect : 0,\n }),\n });\n }\n\n const updated = await prisma.studentWorksheetResponse.findUnique({\n where: { id: worksheetResponseId },\n include: { responses: true },\n });\n return updated!;\n}\n\nexport async function cancelGrading(\n worksheetResponseId: string,\n progressId: string\n) {\n return cancelGradePipeline(worksheetResponseId, progressId);\n}\n\nexport async function regradeQuestion(\n worksheetResponseId: string,\n progressId: string\n) {\n return regradeWorksheetPipeline(worksheetResponseId, progressId);\n}\n\nexport async function gradeAnswer(\n questionId: string,\n studentWorksheetResponseId: string,\n data: {\n responseId?: string;\n response?: string;\n isCorrect: boolean;\n feedback?: string;\n markschemeState?: object;\n points?: number;\n }\n) {\n if (data.responseId) {\n return updateStudentQuestionProgressForGrading(data.responseId, {\n isCorrect: data.isCorrect,\n feedback: data.feedback,\n markschemeState: data.markschemeState,\n points: data.points,\n });\n }\n\n const worksheetResponse = await prisma.studentWorksheetResponse.findUnique({\n where: { id: studentWorksheetResponseId },\n select: { studentId: true },\n });\n if (!worksheetResponse) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Student worksheet response not found\",\n });\n }\n\n const existing = await findStudentQuestionProgress(\n worksheetResponse.studentId,\n questionId,\n studentWorksheetResponseId\n );\n\n if (existing) {\n return updateStudentQuestionProgressForGrading(existing.id, {\n isCorrect: data.isCorrect,\n response: data.response,\n feedback: data.feedback,\n markschemeState: data.markschemeState,\n points: data.points,\n });\n }\n\n return createStudentQuestionProgressForGrading({\n studentId: worksheetResponse.studentId,\n questionId,\n studentWorksheetResponseId,\n response: data.response || \"\",\n isCorrect: data.isCorrect,\n feedback: data.feedback,\n markschemeState: data.markschemeState,\n points: data.points || 0,\n });\n}\n\nexport async function addCommentToResponse(\n responseId: string,\n comment: string,\n authorId: string\n) {\n return createComment({\n studentQuestionProgressId: responseId,\n content: comment,\n authorId,\n });\n}\n"],"names":[],"mappings":"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","debug_id":"cffbcf20-cef6-5fd4-979f-670d50592d0a"}
|
|
1
|
+
{"version":3,"file":"worksheet.js","sources":["services/worksheet.ts"],"sourceRoot":"/","sourcesContent":["/**\n * Worksheet service – CRUD for worksheets, questions, submissions, and AI grading.\n * Integrates with gradeWorksheet pipeline for auto-grading.\n */\nimport { TRPCError } from \"@trpc/server\";\nimport { GenerationStatus, type WorksheetQuestionType } from \"@prisma/client\";\nimport { prisma } from \"../lib/prisma.js\";\nimport { getFileUrl } from \"../lib/fileUpload.js\";\nimport { cancelGradePipeline, regradeWorksheetPipeline } from \"../pipelines/gradeWorksheet.js\";\nimport { pdfToWorksheet } from \"../pipelines/pdfToWorksheet.js\";\nimport {\n findWorksheetByIdMinimal,\n findWorksheetByIdForClassMember,\n findWorksheetByIdForTeacher,\n findWorksheetsByClassId,\n createWorksheet,\n updateWorksheet,\n deleteWorksheet,\n findQuestionsByWorksheetId,\n createWorksheetQuestion,\n updateWorksheetQuestion,\n deleteWorksheetQuestion,\n findSubmissionByIdForWorksheetAccess,\n findOrCreateWorksheetResponse,\n findWorksheetResponseByIdForTeacher,\n findWorksheetResponseWithResponsesForStudent,\n findWorksheetQuestionByIdForTeacher,\n findWorksheetQuestionByIdInWorksheet,\n updateStudentQuestionProgress,\n createStudentQuestionProgress,\n findStudentQuestionProgress,\n updateStudentQuestionProgressForGrading,\n createStudentQuestionProgressForGrading,\n createComment,\n findStudentQuestionProgressByIdForTeacher,\n findStudentQuestionProgressByIdForTeacherAndResponse,\n} from \"../models/worksheet.js\";\n\ntype MCQOptions = { id: string; text: string; isCorrect: boolean }[];\n\nconst sanitizeQuestionOptionsForStudent = (options: unknown) => {\n if (!Array.isArray(options)) return options;\n\n return options.map((option) => {\n if (!option || typeof option !== \"object\") return option;\n\n const { isCorrect: _isCorrect, ...rest } = option as Record<string, unknown>;\n return rest;\n });\n};\n\nconst sanitizeWorksheetForStudent = <T extends {\n questions: Array<Record<string, unknown>>;\n}>(worksheet: T) => ({\n ...worksheet,\n questions: worksheet.questions.map((question) => ({\n ...question,\n answer: \"\",\n markScheme: null,\n options: sanitizeQuestionOptionsForStudent(question.options),\n })),\n});\n\n/** Get worksheet with questions and mark schemes. */\nexport async function getWorksheet(worksheetId: string, userId: string) {\n const teacherWorksheet = await findWorksheetByIdForTeacher(worksheetId, userId);\n if (teacherWorksheet) {\n return teacherWorksheet;\n }\n\n const studentWorksheet = await findWorksheetByIdForClassMember(worksheetId, userId);\n if (!studentWorksheet) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"You do not have access to this worksheet\",\n });\n }\n\n return sanitizeWorksheetForStudent(studentWorksheet);\n}\n\n/** Check if worksheet exists. */\nexport async function worksheetExists(id: string) {\n const worksheet = await findWorksheetByIdMinimal(id);\n return !!worksheet;\n}\n\nexport async function listWorksheets(classId: string) {\n const worksheets = await findWorksheetsByClassId(classId);\n return worksheets.map((w) => ({\n ...w,\n questionCount: w.questions.length,\n }));\n}\n\nexport async function updateWorksheetRecord(\n worksheetId: string,\n userId: string,\n data: { name?: string }\n) {\n const worksheet = await findWorksheetByIdForTeacher(worksheetId, userId);\n if (!worksheet) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Only class teachers can update worksheets\",\n });\n }\n return updateWorksheet(worksheetId, data);\n}\n\nexport async function deleteWorksheetRecord(worksheetId: string, userId: string) {\n const worksheet = await findWorksheetByIdForTeacher(worksheetId, userId);\n if (!worksheet) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Only class teachers can delete worksheets\",\n });\n }\n return deleteWorksheet(worksheetId);\n}\n\nexport async function createWorksheetRecord(classId: string, name: string) {\n return createWorksheet({ classId, name });\n}\n\n/**\n * Create a worksheet by converting a PDF (URL) into structured questions.\n * Uses the `pdfToWorksheet` pipeline to do inference + persistence.\n */\nexport async function createWorksheetFromPdf(\n classId: string,\n url: string\n) {\n const created = await pdfToWorksheet(url, classId);\n if (!created) {\n throw new TRPCError({\n code: \"INTERNAL_SERVER_ERROR\",\n message: \"Failed to create worksheet from PDF\",\n });\n }\n return created;\n}\n\n/**\n * Create a worksheet from a PDF that was uploaded through the direct-upload flow.\n */\nexport async function createWorksheetFromUploadedPdf(\n classId: string,\n fileId: string,\n userId: string\n) {\n const file = await prisma.file.findFirst({\n where: {\n id: fileId,\n userId,\n uploadStatus: \"COMPLETED\",\n },\n select: {\n id: true,\n path: true,\n type: true,\n },\n });\n\n if (!file) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Uploaded file not found or not ready\",\n });\n }\n\n const isPdf =\n file.type === \"application/pdf\" || file.path.toLowerCase().endsWith(\".pdf\");\n if (!isPdf) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"Only PDF files can be converted into worksheets\",\n });\n }\n\n const signedUrl = await getFileUrl(file.path);\n return createWorksheetFromPdf(classId, signedUrl);\n}\n\nexport async function addQuestionToWorksheet(\n worksheetId: string,\n userId: string,\n data: {\n question: string;\n answer: string;\n points?: number;\n options?: object;\n markScheme?: object;\n type: WorksheetQuestionType;\n }\n) {\n const worksheet = await findWorksheetByIdForTeacher(worksheetId, userId);\n if (!worksheet) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Only class teachers can modify worksheet questions\",\n });\n }\n return createWorksheetQuestion({\n worksheetId,\n ...data,\n });\n}\n\nexport async function reorderWorksheetQuestions(\n worksheetId: string,\n userId: string,\n movedId: string,\n position: \"before\" | \"after\",\n targetId: string\n) {\n const worksheet = await findWorksheetByIdForTeacher(worksheetId, userId);\n if (!worksheet) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Only class teachers can reorder worksheet questions\",\n });\n }\n\n const questions = await findQuestionsByWorksheetId(worksheetId);\n const movedIdx = questions.findIndex((q) => q.id === movedId);\n if (movedIdx === -1) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Moved question not found\",\n });\n }\n const targetIdx = questions.findIndex((q) => q.id === targetId);\n if (targetIdx === -1) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Target question not found\",\n });\n }\n\n const withoutMoved = questions.filter((q) => q.id !== movedId);\n const next =\n position === \"before\"\n ? [\n ...withoutMoved.slice(0, targetIdx).map((item) => ({ id: item.id })),\n { id: movedId },\n ...withoutMoved.slice(targetIdx).map((item) => ({ id: item.id })),\n ]\n : [\n ...withoutMoved\n .slice(0, targetIdx + 1)\n .map((item) => ({ id: item.id })),\n { id: movedId },\n ...withoutMoved\n .slice(targetIdx + 1)\n .map((item) => ({ id: item.id })),\n ];\n\n await prisma.$transaction(\n next.map((item, index) =>\n prisma.worksheetQuestion.update({\n where: { id: item.id },\n data: { order: index },\n })\n )\n );\n return next;\n}\n\nexport async function updateWorksheetQuestionRecord(\n worksheetId: string,\n questionId: string,\n userId: string,\n data: {\n question?: string;\n answer?: string;\n points?: number;\n options?: object;\n markScheme?: object;\n type?: WorksheetQuestionType;\n }\n) {\n const question = await findWorksheetQuestionByIdForTeacher(questionId, userId);\n if (!question || question.worksheetId !== worksheetId) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Only class teachers can update worksheet questions\",\n });\n }\n return updateWorksheetQuestion(questionId, data);\n}\n\nexport async function deleteWorksheetQuestionRecord(\n worksheetId: string,\n questionId: string,\n userId: string\n) {\n const question = await findWorksheetQuestionByIdForTeacher(questionId, userId);\n if (!question || question.worksheetId !== worksheetId) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Only class teachers can delete worksheet questions\",\n });\n }\n return deleteWorksheetQuestion(questionId);\n}\n\nexport async function getWorksheetSubmission(\n worksheetId: string,\n submissionId: string,\n userId: string\n) {\n const worksheet = await findWorksheetByIdForClassMember(worksheetId, userId);\n if (!worksheet) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"You do not have access to this worksheet\",\n });\n }\n\n const submission = await findSubmissionByIdForWorksheetAccess(\n submissionId,\n worksheetId,\n worksheet.classId,\n userId\n );\n if (!submission) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"You do not have access to this worksheet submission\",\n });\n }\n return findOrCreateWorksheetResponse(\n submissionId,\n worksheetId,\n submission.studentId\n );\n}\n\nexport async function answerWorksheetQuestion(\n worksheetResponseId: string,\n questionId: string,\n response: string,\n userId: string\n) {\n const worksheetResponse = await findWorksheetResponseWithResponsesForStudent(\n worksheetResponseId,\n userId,\n questionId\n );\n if (!worksheetResponse) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"You can only answer your own worksheet responses\",\n });\n }\n\n const question = await findWorksheetQuestionByIdInWorksheet(\n questionId,\n worksheetResponse.worksheetId\n );\n if (!question) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Worksheet question not found\",\n });\n }\n\n const isMarkableByAlgo =\n question.type === \"MULTIPLE_CHOICE\" || question.type === \"TRUE_FALSE\";\n const marksAwardedIfCorrect = question.points || 0;\n const correctAnswer = isMarkableByAlgo\n ? question.type === \"MULTIPLE_CHOICE\"\n ? (question.options as MCQOptions)?.find((o) => o.isCorrect)?.id\n : question.answer?.toString()\n : null;\n\n const existingResponse = worksheetResponse.responses[0];\n\n if (existingResponse) {\n await updateStudentQuestionProgress(existingResponse.id, {\n response,\n ...(isMarkableByAlgo && {\n isCorrect: response === correctAnswer,\n }),\n ...(isMarkableByAlgo && {\n points: response === correctAnswer ? marksAwardedIfCorrect : 0,\n }),\n status: GenerationStatus.NOT_STARTED,\n });\n } else {\n await createStudentQuestionProgress({\n studentId: worksheetResponse.studentId,\n questionId,\n response,\n studentWorksheetResponseId: worksheetResponseId,\n ...(isMarkableByAlgo && {\n isCorrect: response === correctAnswer,\n }),\n ...(isMarkableByAlgo && {\n points: response === correctAnswer ? marksAwardedIfCorrect : 0,\n }),\n });\n }\n\n const updated = await prisma.studentWorksheetResponse.findUnique({\n where: { id: worksheetResponseId },\n include: { responses: true },\n });\n return updated!;\n}\n\nexport async function cancelGrading(\n worksheetResponseId: string,\n progressId: string,\n userId: string\n) {\n const progress = await findStudentQuestionProgressByIdForTeacherAndResponse(\n progressId,\n worksheetResponseId,\n userId\n );\n if (!progress) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Only class teachers can cancel worksheet grading\",\n });\n }\n return cancelGradePipeline(worksheetResponseId, progressId);\n}\n\nexport async function regradeQuestion(\n worksheetResponseId: string,\n progressId: string,\n userId: string\n) {\n const progress = await findStudentQuestionProgressByIdForTeacherAndResponse(\n progressId,\n worksheetResponseId,\n userId\n );\n if (!progress) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Only class teachers can regrade worksheet answers\",\n });\n }\n return regradeWorksheetPipeline(worksheetResponseId, progressId);\n}\n\nexport async function gradeAnswer(\n questionId: string,\n studentWorksheetResponseId: string,\n userId: string,\n data: {\n responseId?: string;\n response?: string;\n isCorrect: boolean;\n feedback?: string;\n markschemeState?: object;\n points?: number;\n }\n) {\n const worksheetResponse = await findWorksheetResponseByIdForTeacher(\n studentWorksheetResponseId,\n userId\n );\n if (!worksheetResponse) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Only class teachers can grade worksheet answers\",\n });\n }\n\n const question = await findWorksheetQuestionByIdInWorksheet(\n questionId,\n worksheetResponse.worksheetId\n );\n if (!question) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Worksheet question not found\",\n });\n }\n\n if (data.responseId) {\n const existingProgress = await findStudentQuestionProgressByIdForTeacher(\n data.responseId,\n userId\n );\n if (\n !existingProgress ||\n existingProgress.studentWorksheetResponseId !== studentWorksheetResponseId ||\n existingProgress.questionId !== questionId\n ) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Only class teachers can grade worksheet answers\",\n });\n }\n\n return updateStudentQuestionProgressForGrading(data.responseId, {\n isCorrect: data.isCorrect,\n feedback: data.feedback,\n markschemeState: data.markschemeState,\n points: data.points,\n });\n }\n\n const existing = await findStudentQuestionProgress(\n worksheetResponse.studentId,\n questionId,\n studentWorksheetResponseId\n );\n\n if (existing) {\n return updateStudentQuestionProgressForGrading(existing.id, {\n isCorrect: data.isCorrect,\n response: data.response,\n feedback: data.feedback,\n markschemeState: data.markschemeState,\n points: data.points,\n });\n }\n\n return createStudentQuestionProgressForGrading({\n studentId: worksheetResponse.studentId,\n questionId,\n studentWorksheetResponseId,\n response: data.response || \"\",\n isCorrect: data.isCorrect,\n feedback: data.feedback,\n markschemeState: data.markschemeState,\n points: data.points || 0,\n });\n}\n\nexport async function addCommentToResponse(\n responseId: string,\n comment: string,\n authorId: string\n) {\n const progress = await findStudentQuestionProgressByIdForTeacher(\n responseId,\n authorId\n );\n if (!progress) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Only class teachers can comment on worksheet responses\",\n });\n }\n\n return createComment({\n studentQuestionProgressId: responseId,\n content: comment,\n authorId,\n });\n}\n"],"names":[],"mappings":"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","debug_id":"2fcbad58-5215-5f4a-a912-82db38a85645"}
|
package/dist/trpc.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"trpc.d.ts","sourceRoot":"/","sources":["trpc.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,SAAS,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"trpc.d.ts","sourceRoot":"/","sources":["trpc.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,SAAS,EAAE,MAAM,cAAc,CAAC;AAKnD,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAqB,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAGnF,UAAU,oBAAoB;IAC5B,GAAG,EAAE,OAAO,CAAC;IACb,GAAG,EAAE,QAAQ,CAAC;CACf;AAED,MAAM,MAAM,OAAO,GAAG;IACpB,GAAG,EAAE,OAAO,CAAC;IACb,GAAG,EAAE,QAAQ,CAAC;IACd,IAAI,EAAE;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAC5B,IAAI,CAAC,EAAE;QACL,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,aAAa,CAAC,EAAE,MAAM,CAAC;KACxB,CAAC;CACH,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAU,MAAM,oBAAoB,KAAG,OAAO,CAAC,OAAO,CAmBnF,CAAC;AAEF,eAAO,MAAM,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA8BZ,CAAC;AAOH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;EAAW,CAAC;AACzC,eAAO,MAAM,eAAe,yOAAqC,CAAC;AAIlE,eAAO,MAAM,kBAAkB,yOAAgC,CAAC;AAEhE,eAAO,MAAM,6BAA6B;;;;uHAEnB,CAAC;AAExB,eAAO,MAAM,yBAAyB;;;;uHAEd,CAAC;AAIzB,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;EAAwB,CAAC"}
|
package/dist/trpc.js
CHANGED
|
@@ -1,29 +1,23 @@
|
|
|
1
1
|
|
|
2
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
2
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="6b11f8e8-b33f-5cae-aab3-08d27c3e81e9")}catch(e){}}();
|
|
3
3
|
import { initTRPC } from '@trpc/server';
|
|
4
4
|
import { ZodError } from 'zod';
|
|
5
5
|
import { logger } from './utils/logger.js';
|
|
6
|
-
import { prisma } from './lib/prisma.js';
|
|
7
6
|
import { createLoggingMiddleware } from './middleware/logging.js';
|
|
8
7
|
import { createAuthMiddleware } from './middleware/auth.js';
|
|
9
8
|
import { z } from 'zod';
|
|
10
9
|
import { handlePrismaError } from './utils/prismaErrorHandler.js';
|
|
10
|
+
import { findUserBySessionToken } from './models/auth.js';
|
|
11
11
|
export const createTRPCContext = async (opts) => {
|
|
12
12
|
const { req, res } = opts;
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
},
|
|
22
|
-
},
|
|
23
|
-
select: {
|
|
24
|
-
id: true,
|
|
25
|
-
}
|
|
26
|
-
}) : null;
|
|
13
|
+
const userHeader = req.headers["x-user"];
|
|
14
|
+
const tokenFromUserHeader = typeof userHeader === "string" ? userHeader : userHeader?.[0];
|
|
15
|
+
const authorizationHeader = req.headers.authorization;
|
|
16
|
+
const tokenFromAuthorization = typeof authorizationHeader === "string" && authorizationHeader.startsWith("Bearer ")
|
|
17
|
+
? authorizationHeader.slice(7)
|
|
18
|
+
: null;
|
|
19
|
+
const token = tokenFromUserHeader ?? tokenFromAuthorization;
|
|
20
|
+
const user = token ? await findUserBySessionToken(token) : null;
|
|
27
21
|
return {
|
|
28
22
|
req,
|
|
29
23
|
res,
|
|
@@ -77,4 +71,4 @@ export const protectedTeacherProcedure = protectedProcedure
|
|
|
77
71
|
// Create caller factory
|
|
78
72
|
export const createCallerFactory = t.createCallerFactory;
|
|
79
73
|
//# sourceMappingURL=trpc.js.map
|
|
80
|
-
//# debugId=
|
|
74
|
+
//# debugId=6b11f8e8-b33f-5cae-aab3-08d27c3e81e9
|
package/dist/trpc.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"trpc.js","sources":["trpc.ts"],"sourceRoot":"/","sourcesContent":["import { initTRPC, TRPCError } from '@trpc/server';\nimport { ZodError } from 'zod';\nimport { logger } from './utils/logger.js';\nimport {
|
|
1
|
+
{"version":3,"file":"trpc.js","sources":["trpc.ts"],"sourceRoot":"/","sourcesContent":["import { initTRPC, TRPCError } from '@trpc/server';\nimport { ZodError } from 'zod';\nimport { logger } from './utils/logger.js';\nimport { createLoggingMiddleware } from './middleware/logging.js';\nimport { createAuthMiddleware } from './middleware/auth.js';\nimport { Request, Response } from 'express';\nimport { z } from 'zod';\nimport { handlePrismaError, PrismaErrorInfo } from './utils/prismaErrorHandler.js';\nimport { findUserBySessionToken } from './models/auth.js';\n\ninterface CreateContextOptions {\n req: Request;\n res: Response;\n}\n\nexport type Context = {\n req: Request;\n res: Response;\n user: { id: string } | null;\n meta?: {\n classId?: string;\n institutionId?: string;\n };\n};\n\nexport const createTRPCContext = async (opts: CreateContextOptions): Promise<Context> => {\n const { req, res } = opts;\n\n const userHeader = req.headers[\"x-user\"];\n const tokenFromUserHeader = typeof userHeader === \"string\" ? userHeader : userHeader?.[0];\n const authorizationHeader = req.headers.authorization;\n const tokenFromAuthorization =\n typeof authorizationHeader === \"string\" && authorizationHeader.startsWith(\"Bearer \")\n ? authorizationHeader.slice(7)\n : null;\n const token = tokenFromUserHeader ?? tokenFromAuthorization;\n const user = token ? await findUserBySessionToken(token) : null;\n\n return {\n req,\n res,\n user,\n meta: {},\n };\n};\n\nexport const t = initTRPC.context<Context>().create({\n errorFormatter({ shape, error }) {\n // Handle Prisma errors specifically\n let prismaErrorInfo: PrismaErrorInfo | null = null;\n if (error.cause) {\n try {\n prismaErrorInfo = handlePrismaError(error.cause);\n } catch (e) {\n // If Prisma error handling fails, continue with normal error handling\n }\n }\n\n logger.error('tRPC Error', {\n code: shape.code,\n message: error.message,\n cause: error.cause,\n stack: error.stack,\n prismaError: prismaErrorInfo,\n });\n\n return {\n ...shape,\n data: {\n ...shape.data,\n zodError:\n error.cause instanceof ZodError ? error.cause.flatten() : null,\n prismaError: prismaErrorInfo,\n },\n };\n },\n});\n\n// Create middleware\nconst loggingMiddleware = createLoggingMiddleware(t);\nconst { isAuthed, isMemberInClass, isTeacherInClass } = createAuthMiddleware(t);\n\n// Base procedures\nexport const createTRPCRouter = t.router;\nexport const publicProcedure = t.procedure.use(loggingMiddleware);\n\n\n// Protected procedures\nexport const protectedProcedure = publicProcedure.use(isAuthed);\n\nexport const protectedClassMemberProcedure = protectedProcedure\n .input(z.object({ classId: z.string() }).passthrough())\n .use(isMemberInClass);\n\nexport const protectedTeacherProcedure = protectedProcedure\n .input(z.object({ classId: z.string() }).passthrough())\n .use(isTeacherInClass);\n\n\n// Create caller factory\nexport const createCallerFactory = t.createCallerFactory; "],"names":[],"mappings":"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","debug_id":"6b11f8e8-b33f-5cae-aab3-08d27c3e81e9"}
|
|
@@ -2,9 +2,18 @@
|
|
|
2
2
|
* Inference util – OpenAI client, inference() for structured output, sendAIMessage for chat.
|
|
3
3
|
*/
|
|
4
4
|
import OpenAI from 'openai';
|
|
5
|
+
import type { ResponseInput } from "openai/resources/responses/responses.js";
|
|
6
|
+
import { Prisma } from '@prisma/client';
|
|
5
7
|
import { ZodSchema } from 'zod';
|
|
6
8
|
export declare const inferenceClient: OpenAI;
|
|
7
9
|
export declare const openAIClient: OpenAI;
|
|
10
|
+
export interface InferenceOptions {
|
|
11
|
+
reason?: string;
|
|
12
|
+
metadata?: Prisma.InputJsonValue;
|
|
13
|
+
model?: string;
|
|
14
|
+
temperature?: number;
|
|
15
|
+
maxTokens?: number;
|
|
16
|
+
}
|
|
8
17
|
export interface LabChatContext {
|
|
9
18
|
subject: string;
|
|
10
19
|
topic: string;
|
|
@@ -46,7 +55,7 @@ export declare function sendAIMessage(content: string, conversationId: string, o
|
|
|
46
55
|
createdAt: Date;
|
|
47
56
|
meta?: Record<string, any>;
|
|
48
57
|
}>;
|
|
49
|
-
export declare function inference<T>(content: string | OpenAI.Chat.Completions.ChatCompletionMessageParam[], format?: ZodSchema): Promise<T>;
|
|
58
|
+
export declare function inference<T>(content: string | OpenAI.Chat.Completions.ChatCompletionMessageParam[] | ResponseInput, format?: ZodSchema, options?: InferenceOptions): Promise<T>;
|
|
50
59
|
/**
|
|
51
60
|
* Get available inference models (for admin/config purposes)
|
|
52
61
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inference.d.ts","sourceRoot":"/","sources":["utils/inference.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,MAAM,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"inference.d.ts","sourceRoot":"/","sources":["utils/inference.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,MAAM,MAAM,QAAQ,CAAC;AAC5B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yCAAyC,CAAC;AAM7E,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AACxC,OAAO,EAAE,SAAS,EAAE,MAAM,KAAK,CAAC;AAGhC,eAAO,MAAM,eAAe,QAG1B,CAAC;AAEH,eAAO,MAAM,YAAY,QAAe,CAAC;AASzC,MAAM,WAAW,gBAAgB;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC,cAAc,CAAC;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAoBD,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,UAAU,GAAG,cAAc,GAAG,UAAU,CAAC;IACrD,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,QAAQ,CAAC,EAAE,GAAG,EAAE,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;CACtB;AAgHD;;;GAGG;AACH,wBAAsB,aAAa,CACjC,OAAO,EAAE,MAAM,EACf,cAAc,EAAE,MAAM,EACtB,OAAO,GAAE;IACP,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE;QACZ,OAAO,EAAE;YAAE,EAAE,EAAE,MAAM,CAAA;SAAE,EAAE,CAAC;KAC3B,CAAC;IACF,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC3B,YAAY,CAAC,EAAE;QACb,WAAW,EAAE,MAAM,CAAC;QACpB,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KAChC,CAAC;CACE,GACL,OAAO,CAAC;IACT,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,IAAI,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC5B,CAAC,CAoED;AAsCD,wBAAsB,SAAS,CAAC,CAAC,EAC/B,OAAO,EACH,MAAM,GACN,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,0BAA0B,EAAE,GACpD,aAAa,EACjB,MAAM,CAAC,EAAE,SAAS,EAClB,OAAO,CAAC,EAAE,gBAAgB,GACzB,OAAO,CAAC,CAAC,CAAC,CA6GZ;AAED;;GAEG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAW5D;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEvD"}
|