@studious-lms/server 1.2.48 → 1.2.50
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/dist/routers/_app.d.ts +194 -2
- package/dist/routers/_app.d.ts.map +1 -1
- package/dist/routers/agenda.d.ts +52 -0
- package/dist/routers/agenda.d.ts.map +1 -1
- package/dist/routers/agenda.js +4 -2
- package/dist/routers/agenda.js.map +1 -1
- package/dist/routers/announcement.d.ts +20 -0
- package/dist/routers/announcement.d.ts.map +1 -1
- package/dist/routers/announcement.js +9 -2
- package/dist/routers/announcement.js.map +1 -1
- package/dist/routers/assignment.d.ts +3 -0
- package/dist/routers/assignment.d.ts.map +1 -1
- package/dist/routers/assignment.js +5 -2
- package/dist/routers/assignment.js.map +1 -1
- package/dist/routers/worksheet.d.ts +22 -1
- package/dist/routers/worksheet.d.ts.map +1 -1
- package/dist/routers/worksheet.js +16 -12
- package/dist/routers/worksheet.js.map +1 -1
- package/dist/server/pipelines/gradeWorksheet.d.ts +17 -2
- package/dist/server/pipelines/gradeWorksheet.d.ts.map +1 -1
- package/dist/server/pipelines/gradeWorksheet.js +157 -75
- package/dist/server/pipelines/gradeWorksheet.js.map +1 -1
- package/package.json +2 -1
- package/src/routers/agenda.ts +2 -0
- package/src/routers/announcement.ts +7 -0
- package/src/routers/assignment.ts +3 -0
- package/src/routers/worksheet.ts +14 -11
- package/src/server/pipelines/gradeWorksheet.ts +188 -83
package/src/routers/agenda.ts
CHANGED
|
@@ -37,6 +37,7 @@ export const agendaRouter = createTRPCRouter({
|
|
|
37
37
|
},
|
|
38
38
|
},
|
|
39
39
|
include: {
|
|
40
|
+
assignmentsAttached: true,
|
|
40
41
|
class: true,
|
|
41
42
|
},
|
|
42
43
|
}),
|
|
@@ -67,6 +68,7 @@ export const agendaRouter = createTRPCRouter({
|
|
|
67
68
|
},
|
|
68
69
|
},
|
|
69
70
|
include: {
|
|
71
|
+
assignmentsAttached: true,
|
|
70
72
|
class: true,
|
|
71
73
|
},
|
|
72
74
|
}),
|
|
@@ -1015,6 +1015,7 @@ export const assignmentRouter = createTRPCRouter({
|
|
|
1015
1015
|
endTime: true,
|
|
1016
1016
|
location: true,
|
|
1017
1017
|
remarks: true,
|
|
1018
|
+
color: true,
|
|
1018
1019
|
}
|
|
1019
1020
|
},
|
|
1020
1021
|
markScheme: {
|
|
@@ -1835,6 +1836,7 @@ export const assignmentRouter = createTRPCRouter({
|
|
|
1835
1836
|
name: true,
|
|
1836
1837
|
startTime: true,
|
|
1837
1838
|
endTime: true,
|
|
1839
|
+
color: true,
|
|
1838
1840
|
}
|
|
1839
1841
|
}
|
|
1840
1842
|
}
|
|
@@ -1969,6 +1971,7 @@ export const assignmentRouter = createTRPCRouter({
|
|
|
1969
1971
|
endTime: true,
|
|
1970
1972
|
location: true,
|
|
1971
1973
|
remarks: true,
|
|
1974
|
+
color: true,
|
|
1972
1975
|
},
|
|
1973
1976
|
orderBy: {
|
|
1974
1977
|
startTime: 'asc'
|
package/src/routers/worksheet.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { z } from "zod";
|
|
|
4
4
|
import { prisma } from "../lib/prisma.js";
|
|
5
5
|
import { GenerationStatus, WorksheetQuestionType } from "@prisma/client";
|
|
6
6
|
import { commentSelect } from "./comment.js";
|
|
7
|
+
import { cancelGradePipeline, regradeWorksheetPipeline } from "../server/pipelines/gradeWorksheet.js";
|
|
7
8
|
|
|
8
9
|
type MCQOptions = {
|
|
9
10
|
id: string;
|
|
@@ -415,22 +416,24 @@ export const worksheetRouter = createTRPCRouter({
|
|
|
415
416
|
|
|
416
417
|
return updatedWorksheetResponse;
|
|
417
418
|
}),
|
|
418
|
-
|
|
419
|
+
cancelGrading: protectedProcedure
|
|
419
420
|
.input(z.object({
|
|
420
421
|
worksheetResponseId: z.string(),
|
|
421
|
-
|
|
422
|
+
progressId: z.string(),
|
|
422
423
|
}))
|
|
423
424
|
.mutation(async ({ ctx, input }) => {
|
|
424
|
-
const { worksheetResponseId,
|
|
425
|
-
|
|
426
|
-
const updatedQuestion = await prisma.studentQuestionProgress.update({
|
|
427
|
-
where: { id: questionId, studentWorksheetResponseId: worksheetResponseId },
|
|
428
|
-
data: {
|
|
429
|
-
status: GenerationStatus.CANCELLED,
|
|
430
|
-
},
|
|
431
|
-
});
|
|
425
|
+
const { worksheetResponseId, progressId } = input;
|
|
432
426
|
|
|
433
|
-
return
|
|
427
|
+
return cancelGradePipeline(worksheetResponseId, progressId);
|
|
428
|
+
}),
|
|
429
|
+
regradeQuestion: protectedProcedure
|
|
430
|
+
.input(z.object({
|
|
431
|
+
worksheetResponseId: z.string(),
|
|
432
|
+
progressId: z.string(),
|
|
433
|
+
}))
|
|
434
|
+
.mutation(async ({ ctx, input }) => {
|
|
435
|
+
const { worksheetResponseId, progressId } = input;
|
|
436
|
+
return regradeWorksheetPipeline(worksheetResponseId, progressId);
|
|
434
437
|
}),
|
|
435
438
|
// Grade a student's answer
|
|
436
439
|
gradeAnswer: protectedProcedure
|
|
@@ -6,6 +6,139 @@ import { inference } from "../../utils/inference.js";
|
|
|
6
6
|
import { getAIUserId } from "../../utils/aiUser.js";
|
|
7
7
|
import { pusher } from "../../lib/pusher.js";
|
|
8
8
|
|
|
9
|
+
|
|
10
|
+
const removeAllPreviousAIComments = async (worksheetQuestionProgressId: string) => {
|
|
11
|
+
await prisma.comment.deleteMany({
|
|
12
|
+
where: {
|
|
13
|
+
studentQuestionProgressId: worksheetQuestionProgressId,
|
|
14
|
+
authorId: getAIUserId(),
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const gradeWorksheetQuestion = async (worksheetResponseId: string) => {
|
|
20
|
+
|
|
21
|
+
const worksheetResponse = await prisma.studentWorksheetResponse.findUnique({
|
|
22
|
+
where: { id: worksheetResponseId },
|
|
23
|
+
include: {
|
|
24
|
+
worksheet: true,
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
if (!worksheetResponse) {
|
|
29
|
+
logger.error('Worksheet response not found');
|
|
30
|
+
throw new Error('Worksheet response not found');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
const studentQuestionProgress = await prisma.studentQuestionProgress.findFirst({
|
|
35
|
+
where: {
|
|
36
|
+
studentWorksheetResponseId: worksheetResponseId,
|
|
37
|
+
status: {
|
|
38
|
+
not: {
|
|
39
|
+
in: DO_NOT_INFERENCE_STATUSES,
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
include: {
|
|
44
|
+
question: true,
|
|
45
|
+
comments: true,
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
if (!studentQuestionProgress) {
|
|
50
|
+
logger.error('Student question progress not found');
|
|
51
|
+
throw new Error('Student question progress not found');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
pusher.trigger(`class-${worksheetResponse.worksheet.classId}-worksheetSubmission-${worksheetResponse.id}`, `status-pending`, {
|
|
55
|
+
id: studentQuestionProgress.id,
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const question = studentQuestionProgress.question;
|
|
59
|
+
const comments = studentQuestionProgress.comments;
|
|
60
|
+
const responseText = studentQuestionProgress.response;
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
try {
|
|
64
|
+
const apiResponse = await inference(
|
|
65
|
+
`Grade the following worksheet response:
|
|
66
|
+
|
|
67
|
+
Question: ${question.question}
|
|
68
|
+
Response: ${responseText}
|
|
69
|
+
|
|
70
|
+
Comments: ${comments.map((comment) => comment.content).join('\n')}
|
|
71
|
+
Mark Scheme: ${JSON.stringify(question.markScheme)}
|
|
72
|
+
|
|
73
|
+
Justify your reasoning by including comment(s) and mark the question please.
|
|
74
|
+
Return ONLY JSON in the following format (fill in the values as per the question):
|
|
75
|
+
{
|
|
76
|
+
"isCorrect": <boolean>,
|
|
77
|
+
"points": <number>,
|
|
78
|
+
"markschemeState": [
|
|
79
|
+
{ "id": <string>, "correct": <boolean> }
|
|
80
|
+
],
|
|
81
|
+
"comments": [<string>, ...]
|
|
82
|
+
}
|
|
83
|
+
`,
|
|
84
|
+
z.object({
|
|
85
|
+
isCorrect: z.boolean(),
|
|
86
|
+
points: z.number(),
|
|
87
|
+
markschemeState: z.array(z.object({
|
|
88
|
+
id: z.string(),
|
|
89
|
+
correct: z.boolean(),
|
|
90
|
+
})), // @note: this has to be converted to [id: string]: correct boolean
|
|
91
|
+
comments: z.array(z.string()),
|
|
92
|
+
}),
|
|
93
|
+
).catch((error) => {
|
|
94
|
+
logger.error('Failed to grade worksheet response', { error });
|
|
95
|
+
throw error;
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
const updatedStudentQuestionProgress = await prisma.studentQuestionProgress.update({
|
|
99
|
+
where: { id: studentQuestionProgress.id, status: {
|
|
100
|
+
not: {
|
|
101
|
+
in: ['CANCELLED'],
|
|
102
|
+
},
|
|
103
|
+
} },
|
|
104
|
+
data: {
|
|
105
|
+
status: GenerationStatus.COMPLETED,
|
|
106
|
+
isCorrect: (apiResponse as { isCorrect: boolean }).isCorrect,
|
|
107
|
+
points: (apiResponse as { points: number }).points,
|
|
108
|
+
markschemeState: (apiResponse as {
|
|
109
|
+
markschemeState: { id: string; correct: boolean }[];
|
|
110
|
+
}).markschemeState.reduce((acc, curr) => {
|
|
111
|
+
acc["item-" + curr.id] = curr.correct;
|
|
112
|
+
return acc;
|
|
113
|
+
}, {} as Record<string, boolean>),
|
|
114
|
+
comments: {
|
|
115
|
+
create: (apiResponse as {
|
|
116
|
+
comments: string[];
|
|
117
|
+
}).comments.map((commentContent) => ({
|
|
118
|
+
content: commentContent,
|
|
119
|
+
authorId: getAIUserId(),
|
|
120
|
+
})),
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
});
|
|
124
|
+
pusher.trigger(`class-${worksheetResponse.worksheet.classId}-worksheetSubmission-${worksheetResponse.id}`, `set-completed`, {
|
|
125
|
+
id: updatedStudentQuestionProgress.id,
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
return updatedStudentQuestionProgress;
|
|
129
|
+
} catch (error) {
|
|
130
|
+
logger.error('Failed to grade worksheet response', { error, worksheetResponseId });
|
|
131
|
+
pusher.trigger(`class-${worksheetResponse.worksheet.classId}-worksheetSubmission-${worksheetResponse.id}`, `set-failed`, {
|
|
132
|
+
id: studentQuestionProgress.id,
|
|
133
|
+
});
|
|
134
|
+
await prisma.studentQuestionProgress.update({
|
|
135
|
+
where: { id: studentQuestionProgress.id },
|
|
136
|
+
data: { status: GenerationStatus.FAILED },
|
|
137
|
+
});
|
|
138
|
+
throw error;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
9
142
|
/**
|
|
10
143
|
* Grades and regrades worksheet (can fixed failed responses)
|
|
11
144
|
* @param worksheetResponseId worksheet response id
|
|
@@ -51,9 +184,6 @@ export const gradeWorksheetPipeline = async (worksheetResponseId: string) => {
|
|
|
51
184
|
// Use for...of instead of forEach to properly handle async operations
|
|
52
185
|
for (const response of worksheetResponse.responses) {
|
|
53
186
|
logger.info('Grading question', { questionId: response.questionId });
|
|
54
|
-
const question = response.question;
|
|
55
|
-
const comments = response.comments;
|
|
56
|
-
const responseText = response.response;
|
|
57
187
|
|
|
58
188
|
const studentQuestionProgress = await prisma.studentQuestionProgress.update({
|
|
59
189
|
where: { id: response.id, status: {
|
|
@@ -68,84 +198,59 @@ export const gradeWorksheetPipeline = async (worksheetResponseId: string) => {
|
|
|
68
198
|
return;
|
|
69
199
|
}
|
|
70
200
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
`Grade the following worksheet response:
|
|
74
|
-
|
|
75
|
-
Question: ${question.question}
|
|
76
|
-
Response: ${responseText}
|
|
77
|
-
|
|
78
|
-
Comments: ${comments.map((comment) => comment.content).join('\n')}
|
|
79
|
-
Mark Scheme: ${JSON.stringify(question.markScheme)}
|
|
80
|
-
|
|
81
|
-
Justify your reasoning by including comment(s) and mark the question please.
|
|
82
|
-
Return ONLY JSON in the following format (fill in the values as per the question):
|
|
83
|
-
{
|
|
84
|
-
"isCorrect": <boolean>,
|
|
85
|
-
"points": <number>,
|
|
86
|
-
"markschemeState": [
|
|
87
|
-
{ "id": <string>, "correct": <boolean> }
|
|
88
|
-
],
|
|
89
|
-
"comments": [<string>, ...]
|
|
90
|
-
}
|
|
91
|
-
`,
|
|
92
|
-
z.object({
|
|
93
|
-
isCorrect: z.boolean(),
|
|
94
|
-
points: z.number(),
|
|
95
|
-
markschemeState: z.array(z.object({
|
|
96
|
-
id: z.string(),
|
|
97
|
-
correct: z.boolean(),
|
|
98
|
-
})), // @note: this has to be converted to [id: string]: correct boolean
|
|
99
|
-
comments: z.array(z.string()),
|
|
100
|
-
}),
|
|
101
|
-
).catch((error) => {
|
|
102
|
-
logger.error('Failed to grade worksheet response', { error });
|
|
103
|
-
throw error;
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
console.log(apiResponse);
|
|
107
|
-
|
|
108
|
-
const updatedStudentQuestionProgress = await prisma.studentQuestionProgress.update({
|
|
109
|
-
where: { id: studentQuestionProgress.id, status: {
|
|
110
|
-
not: {
|
|
111
|
-
in: ['CANCELLED'],
|
|
112
|
-
},
|
|
113
|
-
} },
|
|
114
|
-
data: {
|
|
115
|
-
status: GenerationStatus.COMPLETED,
|
|
116
|
-
isCorrect: (apiResponse as { isCorrect: boolean }).isCorrect,
|
|
117
|
-
points: (apiResponse as { points: number }).points,
|
|
118
|
-
markschemeState: (apiResponse as {
|
|
119
|
-
markschemeState: { id: string; correct: boolean }[];
|
|
120
|
-
}).markschemeState.reduce((acc, curr) => {
|
|
121
|
-
acc["item-" + curr.id] = curr.correct;
|
|
122
|
-
return acc;
|
|
123
|
-
}, {} as Record<string, boolean>),
|
|
124
|
-
comments: {
|
|
125
|
-
create: (apiResponse as {
|
|
126
|
-
comments: string[];
|
|
127
|
-
}).comments.map((commentContent) => ({
|
|
128
|
-
content: commentContent,
|
|
129
|
-
authorId: getAIUserId(),
|
|
130
|
-
})),
|
|
131
|
-
},
|
|
132
|
-
},
|
|
133
|
-
});
|
|
134
|
-
pusher.trigger(`class-${worksheetResponse.worksheet.classId}`, `ai-worksheet-updated-${worksheetResponse.id}`, {
|
|
135
|
-
success: true,
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
return updatedStudentQuestionProgress;
|
|
139
|
-
} catch (error) {
|
|
140
|
-
logger.error('Failed to grade worksheet response', { error, worksheetResponseId });
|
|
141
|
-
pusher.trigger(`class-${worksheetResponse.worksheet.classId}`, `ai-worksheet-updated-${worksheetResponse.id}`, {
|
|
142
|
-
success: false,
|
|
143
|
-
});
|
|
144
|
-
await prisma.studentQuestionProgress.update({
|
|
145
|
-
where: { id: studentQuestionProgress.id },
|
|
146
|
-
data: { status: GenerationStatus.FAILED },
|
|
147
|
-
});
|
|
148
|
-
throw error;
|
|
149
|
-
}
|
|
201
|
+
gradeWorksheetQuestion(response.id);
|
|
202
|
+
|
|
150
203
|
};
|
|
151
|
-
};
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
export const cancelGradePipeline = async (worksheetResponseId: string, worksheetQuestionProgressId: string) => {
|
|
207
|
+
logger.info('Cancelling auto grading', { worksheetResponseId, worksheetQuestionProgressId });
|
|
208
|
+
|
|
209
|
+
const worksheetResponse = await prisma.studentWorksheetResponse.findUnique({
|
|
210
|
+
where: { id: worksheetResponseId },
|
|
211
|
+
include: {
|
|
212
|
+
worksheet: true,
|
|
213
|
+
},
|
|
214
|
+
});
|
|
215
|
+
if (!worksheetResponse) {
|
|
216
|
+
logger.error('Worksheet response not found');
|
|
217
|
+
throw new Error('Worksheet response not found');
|
|
218
|
+
}
|
|
219
|
+
const updatedStudentQuestionProgress = await prisma.studentQuestionProgress.update({
|
|
220
|
+
where: { id: worksheetQuestionProgressId },
|
|
221
|
+
data: { status: GenerationStatus.CANCELLED },
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
await removeAllPreviousAIComments(worksheetQuestionProgressId);
|
|
225
|
+
|
|
226
|
+
pusher.trigger(`class-${worksheetResponse.worksheet.classId}-worksheetSubmission-${worksheetResponse.id}`, `set-cancelled`, {
|
|
227
|
+
id: updatedStudentQuestionProgress.id,
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
return updatedStudentQuestionProgress;
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
export const regradeWorksheetPipeline = async (worksheetResponseId: string, worksheetQuestionProgressId: string) => {
|
|
234
|
+
logger.info('Regrading worksheet response', { worksheetResponseId, worksheetQuestionProgressId });
|
|
235
|
+
|
|
236
|
+
const worksheetResponse = await prisma.studentWorksheetResponse.findUnique({
|
|
237
|
+
where: { id: worksheetResponseId, },
|
|
238
|
+
include: {
|
|
239
|
+
worksheet: true,
|
|
240
|
+
},
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
if (!worksheetResponse) {
|
|
244
|
+
logger.error('Worksheet response not found');
|
|
245
|
+
throw new Error('Worksheet response not found');
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const updatedStudentQuestionProgress = await prisma.studentQuestionProgress.update({
|
|
249
|
+
where: { id: worksheetQuestionProgressId },
|
|
250
|
+
data: { status: GenerationStatus.PENDING },
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
gradeWorksheetQuestion(worksheetQuestionProgressId);
|
|
254
|
+
|
|
255
|
+
return updatedStudentQuestionProgress;
|
|
256
|
+
};
|