@studious-lms/server 1.12.0 → 1.13.0

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.
Files changed (74) hide show
  1. package/dist/models/class.d.ts +10 -0
  2. package/dist/models/class.d.ts.map +1 -1
  3. package/dist/models/class.js +9 -3
  4. package/dist/models/class.js.map +1 -1
  5. package/dist/models/comment.d.ts +7 -0
  6. package/dist/models/comment.d.ts.map +1 -1
  7. package/dist/models/comment.js +3 -2
  8. package/dist/models/comment.js.map +1 -1
  9. package/dist/models/conversation.d.ts +17 -0
  10. package/dist/models/conversation.d.ts.map +1 -1
  11. package/dist/models/conversation.js +13 -2
  12. package/dist/models/conversation.js.map +1 -1
  13. package/dist/models/worksheet.d.ts +4 -0
  14. package/dist/models/worksheet.d.ts.map +1 -1
  15. package/dist/models/worksheet.js +13 -7
  16. package/dist/models/worksheet.js.map +1 -1
  17. package/dist/pipelines/gradeWorksheet.d.ts.map +1 -1
  18. package/dist/pipelines/gradeWorksheet.js +59 -14
  19. package/dist/pipelines/gradeWorksheet.js.map +1 -1
  20. package/dist/routers/_app.d.ts +124 -0
  21. package/dist/routers/_app.d.ts.map +1 -1
  22. package/dist/routers/announcement.d.ts +4 -0
  23. package/dist/routers/announcement.d.ts.map +1 -1
  24. package/dist/routers/announcement.js +6 -5
  25. package/dist/routers/announcement.js.map +1 -1
  26. package/dist/routers/assignment.d.ts +8 -0
  27. package/dist/routers/assignment.d.ts.map +1 -1
  28. package/dist/routers/assignment.js +4 -4
  29. package/dist/routers/assignment.js.map +1 -1
  30. package/dist/routers/class.d.ts +23 -0
  31. package/dist/routers/class.d.ts.map +1 -1
  32. package/dist/routers/comment.d.ts +24 -0
  33. package/dist/routers/comment.d.ts.map +1 -1
  34. package/dist/routers/comment.js +6 -3
  35. package/dist/routers/comment.js.map +1 -1
  36. package/dist/routers/worksheet.d.ts +3 -0
  37. package/dist/routers/worksheet.d.ts.map +1 -1
  38. package/dist/routers/worksheet.js +4 -2
  39. package/dist/routers/worksheet.js.map +1 -1
  40. package/dist/services/assignment.d.ts.map +1 -1
  41. package/dist/services/assignment.js +10 -8
  42. package/dist/services/assignment.js.map +1 -1
  43. package/dist/services/class.d.ts +23 -0
  44. package/dist/services/class.d.ts.map +1 -1
  45. package/dist/services/class.js +8 -3
  46. package/dist/services/class.js.map +1 -1
  47. package/dist/services/comment.d.ts +18 -0
  48. package/dist/services/comment.d.ts.map +1 -1
  49. package/dist/services/comment.js +23 -2
  50. package/dist/services/comment.js.map +1 -1
  51. package/dist/services/conversation.d.ts.map +1 -1
  52. package/dist/services/conversation.js +9 -7
  53. package/dist/services/conversation.js.map +1 -1
  54. package/dist/services/worksheet.d.ts +3 -0
  55. package/dist/services/worksheet.d.ts.map +1 -1
  56. package/dist/services/worksheet.js +2 -2
  57. package/dist/services/worksheet.js.map +1 -1
  58. package/package.json +1 -1
  59. package/prisma/schema.prisma +1 -0
  60. package/src/models/class.ts +7 -1
  61. package/src/models/comment.ts +1 -0
  62. package/src/models/conversation.ts +12 -0
  63. package/src/models/worksheet.ts +11 -5
  64. package/src/pipelines/gradeWorksheet.ts +66 -15
  65. package/src/routers/announcement.ts +4 -3
  66. package/src/routers/assignment.ts +2 -2
  67. package/src/routers/comment.ts +5 -0
  68. package/src/routers/worksheet.ts +2 -0
  69. package/src/services/assignment.ts +8 -7
  70. package/src/services/class.ts +10 -1
  71. package/src/services/comment.ts +22 -0
  72. package/src/services/conversation.ts +9 -4
  73. package/src/services/worksheet.ts +1 -0
  74. package/tests/integration/routers/conversation.test.ts +9 -7
@@ -1,5 +1,5 @@
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]="45642fec-d35a-58c5-a250-59ad437d6604")}catch(e){}}();
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]="a1e3405c-0bf6-5b87-8576-7f4928149b18")}catch(e){}}();
3
3
  import { prisma } from "../lib/prisma.js";
4
4
  import { classMemberOrSchoolAdminWhere, classTeacherOrSchoolAdminWhere, } from "./access.js";
5
5
  const worksheetClassMemberWhere = (userId) => ({
@@ -13,7 +13,7 @@ export function findWorksheetById(id) {
13
13
  return prisma.worksheet.findUnique({
14
14
  where: { id },
15
15
  include: {
16
- questions: { orderBy: { createdAt: "asc" } },
16
+ questions: { orderBy: { order: "asc" } },
17
17
  class: true,
18
18
  },
19
19
  });
@@ -32,7 +32,7 @@ export function findWorksheetByIdForClassMember(id, userId) {
32
32
  ...worksheetClassMemberWhere(userId),
33
33
  },
34
34
  include: {
35
- questions: { orderBy: { createdAt: "asc" } },
35
+ questions: { orderBy: { order: "asc" } },
36
36
  class: true,
37
37
  },
38
38
  });
@@ -45,7 +45,7 @@ export function findWorksheetByIdForTeacher(id, userId) {
45
45
  ...worksheetTeacherWhere(userId),
46
46
  },
47
47
  include: {
48
- questions: { orderBy: { createdAt: "asc" } },
48
+ questions: { orderBy: { order: "asc" } },
49
49
  class: true,
50
50
  },
51
51
  });
@@ -203,7 +203,10 @@ export function findSubmissionByIdForWorksheetAccess(id, worksheetId, worksheetC
203
203
  const worksheetResponseInclude = {
204
204
  responses: {
205
205
  include: {
206
- comments: { select: { id: true } },
206
+ comments: {
207
+ select: { id: true, isPinned: true },
208
+ orderBy: [{ isPinned: "desc" }, { createdAt: "asc" }],
209
+ },
207
210
  },
208
211
  },
209
212
  };
@@ -236,7 +239,10 @@ export function createWorksheetResponse(data) {
236
239
  include: {
237
240
  responses: {
238
241
  include: {
239
- comments: { select: { id: true } },
242
+ comments: {
243
+ select: { id: true, isPinned: true },
244
+ orderBy: [{ isPinned: "desc" }, { createdAt: "asc" }],
245
+ },
240
246
  },
241
247
  },
242
248
  },
@@ -412,4 +418,4 @@ export function createComment(data) {
412
418
  });
413
419
  }
414
420
  //# sourceMappingURL=worksheet.js.map
415
- //# debugId=45642fec-d35a-58c5-a250-59ad437d6604
421
+ //# debugId=a1e3405c-0bf6-5b87-8576-7f4928149b18
@@ -1 +1 @@
1
- {"version":3,"file":"worksheet.js","sources":["models/worksheet.ts"],"sourceRoot":"/","sourcesContent":["/**\n * Worksheet model – worksheets, questions, submissions, student progress, grading.\n */\nimport type { Prisma } from \"@prisma/client\";\nimport { prisma } from \"../lib/prisma.js\";\nimport type { WorksheetQuestionType, JobStatus } from \"@prisma/client\";\nimport {\n classMemberOrSchoolAdminWhere,\n classTeacherOrSchoolAdminWhere,\n} from \"./access.js\";\n\nconst worksheetClassMemberWhere = (userId: string) => ({\n class: classMemberOrSchoolAdminWhere(userId),\n});\n\nconst worksheetTeacherWhere = (userId: string) => ({\n class: classTeacherOrSchoolAdminWhere(userId),\n});\n\n/** @returns Worksheet with questions and class. */\nexport function findWorksheetById(id: string) {\n return prisma.worksheet.findUnique({\n where: { id },\n include: {\n questions: { orderBy: { createdAt: \"asc\" as const } },\n class: true,\n },\n });\n}\n\n/** @returns Worksheet by ID (minimal). */\nexport function findWorksheetByIdMinimal(id: string) {\n return prisma.worksheet.findUnique({\n where: { id },\n });\n}\n\n/** @returns Worksheet by ID for an authorized class member. */\nexport function findWorksheetByIdForClassMember(id: string, userId: string) {\n return prisma.worksheet.findFirst({\n where: {\n id,\n ...worksheetClassMemberWhere(userId),\n },\n include: {\n questions: { orderBy: { createdAt: \"asc\" as const } },\n class: true,\n },\n });\n}\n\n/** @returns Worksheet by ID for an authorized teacher. */\nexport function findWorksheetByIdForTeacher(id: string, userId: string) {\n return prisma.worksheet.findFirst({\n where: {\n id,\n ...worksheetTeacherWhere(userId),\n },\n include: {\n questions: { orderBy: { createdAt: \"asc\" as const } },\n class: true,\n },\n });\n}\n\n/** @returns Worksheets for a class. */\nexport function findWorksheetsByClassId(classId: string) {\n return prisma.worksheet.findMany({\n where: { classId },\n include: {\n questions: { select: { id: true } },\n },\n orderBy: { createdAt: \"desc\" },\n });\n}\n\n/** @returns Worksheet question by ID for an authorized teacher. */\nexport function findWorksheetQuestionByIdForTeacher(id: string, userId: string) {\n return prisma.worksheetQuestion.findFirst({\n where: {\n id,\n worksheet: worksheetTeacherWhere(userId),\n },\n });\n}\n\n/** @returns Worksheet question scoped to a worksheet. */\nexport function findWorksheetQuestionByIdInWorksheet(\n id: string,\n worksheetId: string\n) {\n return prisma.worksheetQuestion.findFirst({\n where: { id, worksheetId },\n });\n}\n\n/** Create a worksheet. */\nexport function createWorksheet(data: { classId: string; name: string }) {\n return prisma.worksheet.create({\n data,\n });\n}\n\n/** Update worksheet name. */\nexport function updateWorksheet(id: string, data: { name?: string }) {\n return prisma.worksheet.update({\n where: { id },\n data,\n });\n}\n\n/** Delete a worksheet. */\nexport function deleteWorksheet(id: string) {\n return prisma.worksheet.delete({\n where: { id },\n });\n}\n\n/**\n * Clone a worksheet and its questions into another class with a new name.\n * Does not copy student responses or assignment links.\n */\nexport async function duplicateWorksheetWithClient(\n tx: Prisma.TransactionClient,\n sourceWorksheetId: string,\n targetClassId: string,\n name: string\n) {\n const source = await tx.worksheet.findUnique({\n where: { id: sourceWorksheetId },\n include: {\n questions: { orderBy: { order: \"asc\" } },\n },\n });\n\n if (!source) {\n return null;\n }\n\n const created = await tx.worksheet.create({\n data: { classId: targetClassId, name },\n });\n\n if (source.questions.length > 0) {\n await tx.worksheetQuestion.createMany({\n data: source.questions.map((q) => ({\n worksheetId: created.id,\n type: q.type,\n question: q.question,\n answer: q.answer,\n points: q.points,\n options: q.options ?? undefined,\n markScheme: q.markScheme ?? undefined,\n order: q.order,\n })),\n });\n }\n\n return created;\n}\n\nexport async function duplicateWorksheet(\n sourceWorksheetId: string,\n targetClassId: string,\n name: string\n) {\n return prisma.$transaction((tx) =>\n duplicateWorksheetWithClient(tx, sourceWorksheetId, targetClassId, name)\n );\n}\n\n/** @returns Questions for a worksheet. */\nexport function findQuestionsByWorksheetId(worksheetId: string) {\n return prisma.worksheetQuestion.findMany({\n where: { worksheetId },\n orderBy: { order: \"asc\" },\n });\n}\n\n/** Create a worksheet question. */\nexport function createWorksheetQuestion(data: {\n worksheetId: string;\n type: WorksheetQuestionType;\n question: string;\n answer: string;\n points?: number;\n options?: object;\n markScheme?: object;\n order?: number;\n}) {\n return prisma.worksheetQuestion.create({\n data,\n });\n}\n\n/** Update question order. */\nexport function updateWorksheetQuestionOrder(id: string, order: number) {\n return prisma.worksheetQuestion.update({\n where: { id },\n data: { order },\n });\n}\n\n/** Update a worksheet question. */\nexport function updateWorksheetQuestion(\n id: string,\n data: {\n question?: string;\n answer?: string;\n points?: number;\n options?: object;\n markScheme?: object;\n type?: WorksheetQuestionType;\n }\n) {\n return prisma.worksheetQuestion.update({\n where: { id },\n data,\n });\n}\n\n/** Delete a worksheet question. */\nexport function deleteWorksheetQuestion(id: string) {\n return prisma.worksheetQuestion.delete({\n where: { id },\n });\n}\n\n/** @returns Submission by ID. */\nexport function findSubmissionById(id: string) {\n return prisma.submission.findUnique({\n where: { id },\n });\n}\n\n/** @returns Submission if requester is the owning student or a class teacher for the worksheet. */\nexport function findSubmissionByIdForWorksheetAccess(\n id: string,\n worksheetId: string,\n worksheetClassId: string,\n userId: string\n) {\n return prisma.submission.findFirst({\n where: {\n id,\n assignment: {\n classId: worksheetClassId,\n worksheets: { some: { id: worksheetId } },\n },\n OR: [\n {\n studentId: userId,\n assignment: {\n class: {\n students: { some: { id: userId } },\n },\n },\n },\n {\n assignment: {\n class: classTeacherOrSchoolAdminWhere(userId),\n },\n },\n ],\n },\n select: {\n id: true,\n studentId: true,\n },\n });\n}\n\nconst worksheetResponseInclude = {\n responses: {\n include: {\n comments: { select: { id: true } },\n },\n },\n};\n\n/** @returns Worksheet response for submission and worksheet. */\nexport function findWorksheetResponseBySubmissionAndWorksheet(\n submissionId: string,\n worksheetId: string\n) {\n return prisma.studentWorksheetResponse.findFirst({\n where: { submissionId, worksheetId },\n include: worksheetResponseInclude,\n });\n}\n\n/** Get or create worksheet response for student. */\nexport async function findOrCreateWorksheetResponse(\n submissionId: string,\n worksheetId: string,\n studentId: string\n) {\n return prisma.$transaction(async (tx) => {\n const existing = await tx.studentWorksheetResponse.findFirst({\n where: { submissionId, worksheetId },\n include: worksheetResponseInclude,\n });\n if (existing) return existing;\n return tx.studentWorksheetResponse.create({\n data: { worksheetId, submissionId, studentId },\n include: worksheetResponseInclude,\n });\n });\n}\n\n/** Create a worksheet response. */\nexport function createWorksheetResponse(data: {\n worksheetId: string;\n submissionId: string;\n studentId: string;\n}) {\n return prisma.studentWorksheetResponse.create({\n data,\n include: {\n responses: {\n include: {\n comments: { select: { id: true } },\n },\n },\n },\n });\n}\n\n/** @returns Worksheet response by ID. */\nexport function findWorksheetResponseById(id: string) {\n return prisma.studentWorksheetResponse.findUnique({\n where: { id },\n include: { responses: true },\n });\n}\n\n/** @returns Worksheet response if requester is the owning student. */\nexport function findWorksheetResponseByIdForStudent(id: string, userId: string) {\n return prisma.studentWorksheetResponse.findFirst({\n where: {\n id,\n studentId: userId,\n worksheet: worksheetClassMemberWhere(userId),\n },\n include: { responses: true, worksheet: true },\n });\n}\n\n/** @returns Worksheet response if requester is a class teacher. */\nexport function findWorksheetResponseByIdForTeacher(id: string, userId: string) {\n return prisma.studentWorksheetResponse.findFirst({\n where: {\n id,\n worksheet: worksheetTeacherWhere(userId),\n },\n include: { responses: true, worksheet: true },\n });\n}\n\n/** @returns Worksheet response with question responses. */\nexport function findWorksheetResponseWithResponses(\n id: string,\n questionId?: string\n) {\n return prisma.studentWorksheetResponse.findUnique({\n where: { id },\n include: {\n responses: questionId\n ? { where: { questionId } }\n : true,\n },\n });\n}\n\n/** @returns Worksheet response with scoped responses if requester is the owning student. */\nexport function findWorksheetResponseWithResponsesForStudent(\n id: string,\n userId: string,\n questionId?: string\n) {\n return prisma.studentWorksheetResponse.findFirst({\n where: {\n id,\n studentId: userId,\n worksheet: worksheetClassMemberWhere(userId),\n },\n include: {\n worksheet: true,\n responses: questionId ? { where: { questionId } } : true,\n },\n });\n}\n\n/** @returns Worksheet response with scoped responses if requester is a class teacher. */\nexport function findWorksheetResponseWithResponsesForTeacher(\n id: string,\n userId: string,\n questionId?: string\n) {\n return prisma.studentWorksheetResponse.findFirst({\n where: {\n id,\n worksheet: worksheetTeacherWhere(userId),\n },\n include: {\n worksheet: true,\n responses: questionId ? { where: { questionId } } : true,\n },\n });\n}\n\n/** @returns Worksheet question by ID. */\nexport function findWorksheetQuestionById(id: string) {\n return prisma.worksheetQuestion.findUnique({\n where: { id },\n });\n}\n\n/** @returns Student question progress if requester is a class teacher. */\nexport function findStudentQuestionProgressByIdForTeacher(\n id: string,\n userId: string\n) {\n return prisma.studentQuestionProgress.findFirst({\n where: {\n id,\n question: {\n worksheet: worksheetTeacherWhere(userId),\n },\n },\n include: {\n question: true,\n studentWorksheetResponse: true,\n },\n });\n}\n\n/** @returns Student question progress by id and worksheet response if requester is a class teacher. */\nexport function findStudentQuestionProgressByIdForTeacherAndResponse(\n id: string,\n studentWorksheetResponseId: string,\n userId: string\n) {\n return prisma.studentQuestionProgress.findFirst({\n where: {\n id,\n studentWorksheetResponseId,\n question: {\n worksheet: worksheetTeacherWhere(userId),\n },\n },\n include: {\n question: true,\n studentWorksheetResponse: true,\n },\n });\n}\n\n/**\n * @returns Student question progress if requester is the answering student (class member) or a\n * class teacher / school admin for that worksheet.\n */\nexport function findStudentQuestionProgressByIdForStudentOrTeacher(\n id: string,\n userId: string\n) {\n return prisma.studentQuestionProgress.findFirst({\n where: {\n id,\n OR: [\n {\n studentId: userId,\n question: { worksheet: worksheetClassMemberWhere(userId) },\n },\n {\n question: { worksheet: worksheetTeacherWhere(userId) },\n },\n ],\n },\n include: {\n question: true,\n studentWorksheetResponse: true,\n },\n });\n}\n\n/** Update student's question progress. */\nexport function updateStudentQuestionProgress(\n id: string,\n data: {\n response?: string;\n isCorrect?: boolean;\n points?: number;\n status?: JobStatus;\n }\n) {\n return prisma.studentQuestionProgress.update({\n where: { id },\n data,\n });\n}\n\n/** Create student question progress. */\nexport function createStudentQuestionProgress(data: {\n studentId: string;\n questionId: string;\n studentWorksheetResponseId: string;\n response: string;\n isCorrect?: boolean;\n points?: number;\n}) {\n return prisma.studentQuestionProgress.create({\n data,\n });\n}\n\n/** @returns Student's progress for a question. */\nexport function findStudentQuestionProgress(\n studentId: string,\n questionId: string,\n studentWorksheetResponseId: string\n) {\n return prisma.studentQuestionProgress.findFirst({\n where: {\n studentId,\n questionId,\n studentWorksheetResponseId,\n },\n });\n}\n\n/** Update progress for grading (AI feedback, marks). */\nexport function updateStudentQuestionProgressForGrading(\n id: string,\n data: {\n isCorrect?: boolean;\n feedback?: string;\n markschemeState?: object;\n points?: number;\n response?: string;\n }\n) {\n return prisma.studentQuestionProgress.update({\n where: { id },\n data,\n });\n}\n\n/** Create progress record for grading pipeline. */\nexport function createStudentQuestionProgressForGrading(data: {\n studentId: string;\n questionId: string;\n studentWorksheetResponseId: string;\n response: string;\n isCorrect: boolean;\n feedback?: string;\n markschemeState?: object;\n points?: number;\n}) {\n return prisma.studentQuestionProgress.create({\n data,\n });\n}\n\n/** Create a comment on a worksheet response. */\nexport function createComment(data: {\n studentQuestionProgressId: string;\n content: string;\n authorId: string;\n}) {\n return prisma.comment.create({\n data,\n });\n}\n"],"names":[],"mappings":"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","debug_id":"45642fec-d35a-58c5-a250-59ad437d6604"}
1
+ {"version":3,"file":"worksheet.js","sources":["models/worksheet.ts"],"sourceRoot":"/","sourcesContent":["/**\n * Worksheet model – worksheets, questions, submissions, student progress, grading.\n */\nimport type { Prisma } from \"@prisma/client\";\nimport { prisma } from \"../lib/prisma.js\";\nimport type { WorksheetQuestionType, JobStatus } from \"@prisma/client\";\nimport {\n classMemberOrSchoolAdminWhere,\n classTeacherOrSchoolAdminWhere,\n} from \"./access.js\";\n\nconst worksheetClassMemberWhere = (userId: string) => ({\n class: classMemberOrSchoolAdminWhere(userId),\n});\n\nconst worksheetTeacherWhere = (userId: string) => ({\n class: classTeacherOrSchoolAdminWhere(userId),\n});\n\n/** @returns Worksheet with questions and class. */\nexport function findWorksheetById(id: string) {\n return prisma.worksheet.findUnique({\n where: { id },\n include: {\n questions: { orderBy: { order: \"asc\" as const } },\n class: true,\n },\n });\n}\n\n/** @returns Worksheet by ID (minimal). */\nexport function findWorksheetByIdMinimal(id: string) {\n return prisma.worksheet.findUnique({\n where: { id },\n });\n}\n\n/** @returns Worksheet by ID for an authorized class member. */\nexport function findWorksheetByIdForClassMember(id: string, userId: string) {\n return prisma.worksheet.findFirst({\n where: {\n id,\n ...worksheetClassMemberWhere(userId),\n },\n include: {\n questions: { orderBy: { order: \"asc\" as const } },\n class: true,\n },\n });\n}\n\n/** @returns Worksheet by ID for an authorized teacher. */\nexport function findWorksheetByIdForTeacher(id: string, userId: string) {\n return prisma.worksheet.findFirst({\n where: {\n id,\n ...worksheetTeacherWhere(userId),\n },\n include: {\n questions: { orderBy: { order: \"asc\" as const } },\n class: true,\n },\n });\n}\n\n/** @returns Worksheets for a class. */\nexport function findWorksheetsByClassId(classId: string) {\n return prisma.worksheet.findMany({\n where: { classId },\n include: {\n questions: { select: { id: true } },\n },\n orderBy: { createdAt: \"desc\" },\n });\n}\n\n/** @returns Worksheet question by ID for an authorized teacher. */\nexport function findWorksheetQuestionByIdForTeacher(id: string, userId: string) {\n return prisma.worksheetQuestion.findFirst({\n where: {\n id,\n worksheet: worksheetTeacherWhere(userId),\n },\n });\n}\n\n/** @returns Worksheet question scoped to a worksheet. */\nexport function findWorksheetQuestionByIdInWorksheet(\n id: string,\n worksheetId: string\n) {\n return prisma.worksheetQuestion.findFirst({\n where: { id, worksheetId },\n });\n}\n\n/** Create a worksheet. */\nexport function createWorksheet(data: { classId: string; name: string }) {\n return prisma.worksheet.create({\n data,\n });\n}\n\n/** Update worksheet name. */\nexport function updateWorksheet(id: string, data: { name?: string }) {\n return prisma.worksheet.update({\n where: { id },\n data,\n });\n}\n\n/** Delete a worksheet. */\nexport function deleteWorksheet(id: string) {\n return prisma.worksheet.delete({\n where: { id },\n });\n}\n\n/**\n * Clone a worksheet and its questions into another class with a new name.\n * Does not copy student responses or assignment links.\n */\nexport async function duplicateWorksheetWithClient(\n tx: Prisma.TransactionClient,\n sourceWorksheetId: string,\n targetClassId: string,\n name: string\n) {\n const source = await tx.worksheet.findUnique({\n where: { id: sourceWorksheetId },\n include: {\n questions: { orderBy: { order: \"asc\" } },\n },\n });\n\n if (!source) {\n return null;\n }\n\n const created = await tx.worksheet.create({\n data: { classId: targetClassId, name },\n });\n\n if (source.questions.length > 0) {\n await tx.worksheetQuestion.createMany({\n data: source.questions.map((q) => ({\n worksheetId: created.id,\n type: q.type,\n question: q.question,\n answer: q.answer,\n points: q.points,\n options: q.options ?? undefined,\n markScheme: q.markScheme ?? undefined,\n order: q.order,\n })),\n });\n }\n\n return created;\n}\n\nexport async function duplicateWorksheet(\n sourceWorksheetId: string,\n targetClassId: string,\n name: string\n) {\n return prisma.$transaction((tx) =>\n duplicateWorksheetWithClient(tx, sourceWorksheetId, targetClassId, name)\n );\n}\n\n/** @returns Questions for a worksheet. */\nexport function findQuestionsByWorksheetId(worksheetId: string) {\n return prisma.worksheetQuestion.findMany({\n where: { worksheetId },\n orderBy: { order: \"asc\" },\n });\n}\n\n/** Create a worksheet question. */\nexport function createWorksheetQuestion(data: {\n worksheetId: string;\n type: WorksheetQuestionType;\n question: string;\n answer: string;\n points?: number;\n options?: object;\n markScheme?: object;\n order?: number;\n}) {\n return prisma.worksheetQuestion.create({\n data,\n });\n}\n\n/** Update question order. */\nexport function updateWorksheetQuestionOrder(id: string, order: number) {\n return prisma.worksheetQuestion.update({\n where: { id },\n data: { order },\n });\n}\n\n/** Update a worksheet question. */\nexport function updateWorksheetQuestion(\n id: string,\n data: {\n question?: string;\n answer?: string;\n points?: number;\n options?: object;\n markScheme?: object;\n type?: WorksheetQuestionType;\n }\n) {\n return prisma.worksheetQuestion.update({\n where: { id },\n data,\n });\n}\n\n/** Delete a worksheet question. */\nexport function deleteWorksheetQuestion(id: string) {\n return prisma.worksheetQuestion.delete({\n where: { id },\n });\n}\n\n/** @returns Submission by ID. */\nexport function findSubmissionById(id: string) {\n return prisma.submission.findUnique({\n where: { id },\n });\n}\n\n/** @returns Submission if requester is the owning student or a class teacher for the worksheet. */\nexport function findSubmissionByIdForWorksheetAccess(\n id: string,\n worksheetId: string,\n worksheetClassId: string,\n userId: string\n) {\n return prisma.submission.findFirst({\n where: {\n id,\n assignment: {\n classId: worksheetClassId,\n worksheets: { some: { id: worksheetId } },\n },\n OR: [\n {\n studentId: userId,\n assignment: {\n class: {\n students: { some: { id: userId } },\n },\n },\n },\n {\n assignment: {\n class: classTeacherOrSchoolAdminWhere(userId),\n },\n },\n ],\n },\n select: {\n id: true,\n studentId: true,\n },\n });\n}\n\nconst worksheetResponseInclude = {\n responses: {\n include: {\n comments: {\n select: { id: true, isPinned: true },\n orderBy: [{ isPinned: \"desc\" as const }, { createdAt: \"asc\" as const }],\n },\n },\n },\n};\n\n/** @returns Worksheet response for submission and worksheet. */\nexport function findWorksheetResponseBySubmissionAndWorksheet(\n submissionId: string,\n worksheetId: string\n) {\n return prisma.studentWorksheetResponse.findFirst({\n where: { submissionId, worksheetId },\n include: worksheetResponseInclude,\n });\n}\n\n/** Get or create worksheet response for student. */\nexport async function findOrCreateWorksheetResponse(\n submissionId: string,\n worksheetId: string,\n studentId: string\n) {\n return prisma.$transaction(async (tx) => {\n const existing = await tx.studentWorksheetResponse.findFirst({\n where: { submissionId, worksheetId },\n include: worksheetResponseInclude,\n });\n if (existing) return existing;\n return tx.studentWorksheetResponse.create({\n data: { worksheetId, submissionId, studentId },\n include: worksheetResponseInclude,\n });\n });\n}\n\n/** Create a worksheet response. */\nexport function createWorksheetResponse(data: {\n worksheetId: string;\n submissionId: string;\n studentId: string;\n}) {\n return prisma.studentWorksheetResponse.create({\n data,\n include: {\n responses: {\n include: {\n comments: {\n select: { id: true, isPinned: true },\n orderBy: [{ isPinned: \"desc\" as const }, { createdAt: \"asc\" as const }],\n },\n },\n },\n },\n });\n}\n\n/** @returns Worksheet response by ID. */\nexport function findWorksheetResponseById(id: string) {\n return prisma.studentWorksheetResponse.findUnique({\n where: { id },\n include: { responses: true },\n });\n}\n\n/** @returns Worksheet response if requester is the owning student. */\nexport function findWorksheetResponseByIdForStudent(id: string, userId: string) {\n return prisma.studentWorksheetResponse.findFirst({\n where: {\n id,\n studentId: userId,\n worksheet: worksheetClassMemberWhere(userId),\n },\n include: { responses: true, worksheet: true },\n });\n}\n\n/** @returns Worksheet response if requester is a class teacher. */\nexport function findWorksheetResponseByIdForTeacher(id: string, userId: string) {\n return prisma.studentWorksheetResponse.findFirst({\n where: {\n id,\n worksheet: worksheetTeacherWhere(userId),\n },\n include: { responses: true, worksheet: true },\n });\n}\n\n/** @returns Worksheet response with question responses. */\nexport function findWorksheetResponseWithResponses(\n id: string,\n questionId?: string\n) {\n return prisma.studentWorksheetResponse.findUnique({\n where: { id },\n include: {\n responses: questionId\n ? { where: { questionId } }\n : true,\n },\n });\n}\n\n/** @returns Worksheet response with scoped responses if requester is the owning student. */\nexport function findWorksheetResponseWithResponsesForStudent(\n id: string,\n userId: string,\n questionId?: string\n) {\n return prisma.studentWorksheetResponse.findFirst({\n where: {\n id,\n studentId: userId,\n worksheet: worksheetClassMemberWhere(userId),\n },\n include: {\n worksheet: true,\n responses: questionId ? { where: { questionId } } : true,\n },\n });\n}\n\n/** @returns Worksheet response with scoped responses if requester is a class teacher. */\nexport function findWorksheetResponseWithResponsesForTeacher(\n id: string,\n userId: string,\n questionId?: string\n) {\n return prisma.studentWorksheetResponse.findFirst({\n where: {\n id,\n worksheet: worksheetTeacherWhere(userId),\n },\n include: {\n worksheet: true,\n responses: questionId ? { where: { questionId } } : true,\n },\n });\n}\n\n/** @returns Worksheet question by ID. */\nexport function findWorksheetQuestionById(id: string) {\n return prisma.worksheetQuestion.findUnique({\n where: { id },\n });\n}\n\n/** @returns Student question progress if requester is a class teacher. */\nexport function findStudentQuestionProgressByIdForTeacher(\n id: string,\n userId: string\n) {\n return prisma.studentQuestionProgress.findFirst({\n where: {\n id,\n question: {\n worksheet: worksheetTeacherWhere(userId),\n },\n },\n include: {\n question: true,\n studentWorksheetResponse: true,\n },\n });\n}\n\n/** @returns Student question progress by id and worksheet response if requester is a class teacher. */\nexport function findStudentQuestionProgressByIdForTeacherAndResponse(\n id: string,\n studentWorksheetResponseId: string,\n userId: string\n) {\n return prisma.studentQuestionProgress.findFirst({\n where: {\n id,\n studentWorksheetResponseId,\n question: {\n worksheet: worksheetTeacherWhere(userId),\n },\n },\n include: {\n question: true,\n studentWorksheetResponse: true,\n },\n });\n}\n\n/**\n * @returns Student question progress if requester is the answering student (class member) or a\n * class teacher / school admin for that worksheet.\n */\nexport function findStudentQuestionProgressByIdForStudentOrTeacher(\n id: string,\n userId: string\n) {\n return prisma.studentQuestionProgress.findFirst({\n where: {\n id,\n OR: [\n {\n studentId: userId,\n question: { worksheet: worksheetClassMemberWhere(userId) },\n },\n {\n question: { worksheet: worksheetTeacherWhere(userId) },\n },\n ],\n },\n include: {\n question: true,\n studentWorksheetResponse: true,\n },\n });\n}\n\n/** Update student's question progress. */\nexport function updateStudentQuestionProgress(\n id: string,\n data: {\n response?: string;\n isCorrect?: boolean;\n points?: number;\n status?: JobStatus;\n }\n) {\n return prisma.studentQuestionProgress.update({\n where: { id },\n data,\n });\n}\n\n/** Create student question progress. */\nexport function createStudentQuestionProgress(data: {\n studentId: string;\n questionId: string;\n studentWorksheetResponseId: string;\n response: string;\n isCorrect?: boolean;\n points?: number;\n}) {\n return prisma.studentQuestionProgress.create({\n data,\n });\n}\n\n/** @returns Student's progress for a question. */\nexport function findStudentQuestionProgress(\n studentId: string,\n questionId: string,\n studentWorksheetResponseId: string\n) {\n return prisma.studentQuestionProgress.findFirst({\n where: {\n studentId,\n questionId,\n studentWorksheetResponseId,\n },\n });\n}\n\n/** Update progress for grading (AI feedback, marks). */\nexport function updateStudentQuestionProgressForGrading(\n id: string,\n data: {\n isCorrect?: boolean;\n feedback?: string;\n markschemeState?: object;\n points?: number;\n response?: string;\n }\n) {\n return prisma.studentQuestionProgress.update({\n where: { id },\n data,\n });\n}\n\n/** Create progress record for grading pipeline. */\nexport function createStudentQuestionProgressForGrading(data: {\n studentId: string;\n questionId: string;\n studentWorksheetResponseId: string;\n response: string;\n isCorrect: boolean;\n feedback?: string;\n markschemeState?: object;\n points?: number;\n}) {\n return prisma.studentQuestionProgress.create({\n data,\n });\n}\n\n/** Create a comment on a worksheet response. */\nexport function createComment(data: {\n studentQuestionProgressId: string;\n content: string;\n authorId: string;\n}) {\n return prisma.comment.create({\n data,\n });\n}\n"],"names":[],"mappings":"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","debug_id":"a1e3405c-0bf6-5b87-8576-7f4928149b18"}
@@ -1 +1 @@
1
- {"version":3,"file":"gradeWorksheet.d.ts","sourceRoot":"/","sources":["pipelines/gradeWorksheet.ts"],"names":[],"mappings":"AA8KA,eAAO,MAAM,sBAAsB,GAAU,qBAAqB,MAAM,kBAyDvE,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAAU,qBAAqB,MAAM,EAAE,6BAA6B,MAAM;;;;;;;;;;;;;;EAyBzG,CAAC;AAEF,eAAO,MAAM,wBAAwB,GAAU,qBAAqB,MAAM,EAAE,6BAA6B,MAAM;;;;;;;;;;;;;;SAuE9G,CAAC"}
1
+ {"version":3,"file":"gradeWorksheet.d.ts","sourceRoot":"/","sources":["pipelines/gradeWorksheet.ts"],"names":[],"mappings":"AAgMA,eAAO,MAAM,sBAAsB,GAAU,qBAAqB,MAAM,kBA4FvE,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAAU,qBAAqB,MAAM,EAAE,6BAA6B,MAAM;;;;;;;;;;;;;;EAyBzG,CAAC;AAEF,eAAO,MAAM,wBAAwB,GAAU,qBAAqB,MAAM,EAAE,6BAA6B,MAAM;;;;;;;;;;;;;;SAqE9G,CAAC"}
@@ -3,7 +3,7 @@
3
3
  * Grades questions via inference API, broadcasts status via Pusher (pending/completed/failed/cancelled).
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]="f57921a6-ccf6-53ed-9099-e9095fd0f801")}catch(e){}}();
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]="e408bc40-2745-52a0-b3ee-d97502bc2313")}catch(e){}}();
7
7
  import { JobStatus, WorksheetQuestionType } from "@prisma/client";
8
8
  import { prisma } from "../lib/prisma.js";
9
9
  import { logger } from "../utils/logger.js";
@@ -20,6 +20,7 @@ const removeAllPreviousAIComments = async (worksheetQuestionProgressId) => {
20
20
  });
21
21
  };
22
22
  const gradeWorksheetQuestion = async (worksheetResponseId, worksheetQuestionProgressId) => {
23
+ await removeAllPreviousAIComments(worksheetQuestionProgressId);
23
24
  const worksheetResponse = await prisma.studentWorksheetResponse.findUnique({
24
25
  where: { id: worksheetResponseId },
25
26
  include: {
@@ -40,23 +41,39 @@ const gradeWorksheetQuestion = async (worksheetResponseId, worksheetQuestionProg
40
41
  },
41
42
  });
42
43
  if (!studentQuestionProgress) {
43
- const updatedStudentQuestionProgress = await prisma.studentQuestionProgress.create({
44
+ logger.error('Student question progress not found', { worksheetQuestionProgressId });
45
+ throw new Error('Student question progress not found');
46
+ }
47
+ pusher.trigger(worksheetChannel(worksheetResponse.id), `set-pending`, {
48
+ id: studentQuestionProgress.id,
49
+ });
50
+ const question = studentQuestionProgress.question;
51
+ const responseText = studentQuestionProgress.response;
52
+ // Short-circuit for empty responses: award 0 points without calling inference
53
+ if (!responseText || responseText.trim().length === 0) {
54
+ const updatedProgress = await prisma.studentQuestionProgress.update({
55
+ where: { id: studentQuestionProgress.id, status: {
56
+ not: { in: [JobStatus.CANCELLED] },
57
+ } },
44
58
  data: {
45
- studentId: worksheetResponse.studentId,
46
- questionId: worksheetQuestionProgressId,
47
- response: '',
59
+ status: JobStatus.COMPLETED,
48
60
  isCorrect: false,
61
+ points: 0,
49
62
  markschemeState: {},
63
+ comments: {
64
+ create: [{
65
+ content: 'No answer provided.',
66
+ authorId: getAIUserId(),
67
+ }],
68
+ },
50
69
  },
51
70
  });
52
- return updatedStudentQuestionProgress;
71
+ pusher.trigger(worksheetChannel(worksheetResponse.id), `set-completed`, {
72
+ id: updatedProgress.id,
73
+ });
74
+ return updatedProgress;
53
75
  }
54
- pusher.trigger(worksheetChannel(worksheetResponse.id), `set-pending`, {
55
- id: studentQuestionProgress.id,
56
- });
57
- const question = studentQuestionProgress.question;
58
76
  const comments = studentQuestionProgress.comments;
59
- const responseText = studentQuestionProgress.response;
60
77
  try {
61
78
  const apiResponse = await inference(`Grade the following worksheet response:
62
79
 
@@ -182,7 +199,36 @@ export const gradeWorksheetPipeline = async (worksheetResponseId) => {
182
199
  logger.error('Worksheet response not found');
183
200
  throw new Error('Worksheet response not found');
184
201
  }
185
- // Use for...of instead of forEach to properly handle async operations
202
+ // Find worksheet questions that have no StudentQuestionProgress for this response
203
+ // and create empty progress records so they get graded (with 0 points).
204
+ const allQuestions = await prisma.worksheetQuestion.findMany({
205
+ where: {
206
+ worksheetId: worksheetResponse.worksheetId,
207
+ type: {
208
+ notIn: [WorksheetQuestionType.MULTIPLE_CHOICE, WorksheetQuestionType.TRUE_FALSE],
209
+ },
210
+ },
211
+ select: { id: true },
212
+ });
213
+ const existingQuestionIds = new Set(worksheetResponse.responses.map((r) => r.questionId));
214
+ for (const q of allQuestions) {
215
+ if (existingQuestionIds.has(q.id))
216
+ continue;
217
+ const created = await prisma.studentQuestionProgress.create({
218
+ data: {
219
+ studentId: worksheetResponse.studentId,
220
+ questionId: q.id,
221
+ studentWorksheetResponseId: worksheetResponseId,
222
+ response: '',
223
+ isCorrect: false,
224
+ },
225
+ });
226
+ worksheetResponse.responses.push({
227
+ ...created,
228
+ question: { id: q.id },
229
+ comments: [],
230
+ });
231
+ }
186
232
  for (const response of worksheetResponse.responses) {
187
233
  logger.info('Grading question', { questionId: response.questionId });
188
234
  const { count } = await prisma.studentQuestionProgress.updateMany({
@@ -255,7 +301,6 @@ export const regradeWorksheetPipeline = async (worksheetResponseId, worksheetQue
255
301
  const updatedStudentQuestionProgress = await prisma.studentQuestionProgress.findUnique({
256
302
  where: { id: worksheetQuestionProgressId },
257
303
  });
258
- await removeAllPreviousAIComments(worksheetQuestionProgressId);
259
304
  await gradeWorksheetQuestion(worksheetResponseId, worksheetQuestionProgressId);
260
305
  return updatedStudentQuestionProgress;
261
306
  }
@@ -292,4 +337,4 @@ export const regradeWorksheetPipeline = async (worksheetResponseId, worksheetQue
292
337
  }
293
338
  };
294
339
  //# sourceMappingURL=gradeWorksheet.js.map
295
- //# debugId=f57921a6-ccf6-53ed-9099-e9095fd0f801
340
+ //# debugId=e408bc40-2745-52a0-b3ee-d97502bc2313
@@ -1 +1 @@
1
- {"version":3,"file":"gradeWorksheet.js","sources":["pipelines/gradeWorksheet.ts"],"sourceRoot":"/","sourcesContent":["/**\n * Grade worksheet pipeline – AI-powered grading of worksheet responses.\n * Grades questions via inference API, broadcasts status via Pusher (pending/completed/failed/cancelled).\n */\nimport { JobStatus, WorksheetQuestionType } from \"@prisma/client\";\nimport { prisma } from \"../lib/prisma.js\";\nimport { logger } from \"../utils/logger.js\";\nimport z from \"zod\";\nimport { inference } from \"../utils/inference.js\";\nimport { getAIUserId } from \"../utils/aiUser.js\";\nimport { pusher, worksheetChannel } from \"../lib/pusher.js\";\n\n\nconst removeAllPreviousAIComments = async (worksheetQuestionProgressId: string) => {\n await prisma.comment.deleteMany({\n where: {\n studentQuestionProgressId: worksheetQuestionProgressId,\n authorId: getAIUserId(),\n },\n });\n};\n\nconst gradeWorksheetQuestion = async (worksheetResponseId: string, worksheetQuestionProgressId: string) => {\n\n const worksheetResponse = await prisma.studentWorksheetResponse.findUnique({\n where: { id: worksheetResponseId },\n include: {\n worksheet: true,\n },\n });\n\n if (!worksheetResponse) {\n logger.error('Worksheet response not found');\n throw new Error('Worksheet response not found');\n } \n\n const studentQuestionProgress = await prisma.studentQuestionProgress.findFirst({\n where: {\n id: worksheetQuestionProgressId,\n },\n include: {\n question: true,\n comments: true,\n },\n });\n\n if (!studentQuestionProgress) {\n const updatedStudentQuestionProgress = await prisma.studentQuestionProgress.create({\n data: {\n studentId: worksheetResponse.studentId,\n questionId: worksheetQuestionProgressId,\n response: '',\n isCorrect: false,\n markschemeState: {},\n },\n });\n\n return updatedStudentQuestionProgress;\n }\n\n pusher.trigger(worksheetChannel(worksheetResponse.id), `set-pending`, {\n id: studentQuestionProgress.id,\n });\n\n const question = studentQuestionProgress.question;\n const comments = studentQuestionProgress.comments;\n const responseText = studentQuestionProgress.response;\n\n\n try {\n const apiResponse = await inference(\n `Grade the following worksheet response:\n \n Question: ${question.question}\n Response: ${responseText}\n\n Comments: ${comments.map((comment) => comment.content).join('\\n')}\n Mark Scheme: ${JSON.stringify(question.markScheme)}\n \n Justify your reasoning by including comment(s) and mark the question please. \n Return ONLY JSON in the following format (fill in the values as per the question):\n {\n \"isCorrect\": <boolean>,\n \"points\": <number>,\n \"markschemeState\": [\n { \"id\": <string>, \"correct\": <boolean> }\n ],\n \"comments\": [<string>, ...]\n }\n `,\n z.object({\n isCorrect: z.boolean(),\n points: z.number(),\n markschemeState: z.array(z.object({\n id: z.string(),\n correct: z.boolean(),\n })), // @note: this has to be converted to [id: string]: correct boolean\n comments: z.array(z.string()),\n }),\n {\n reason: 'worksheet_autograde',\n metadata: {\n worksheetResponseId: worksheetResponse.id,\n questionId: worksheetQuestionProgressId,\n studentId: worksheetResponse.studentId,\n },\n },\n ).catch((error) => {\n logger.error('Failed to grade worksheet response', { error });\n throw error;\n });\n\n const updatedStudentQuestionProgress = await prisma.studentQuestionProgress.update({\n where: { id: studentQuestionProgress.id, status: {\n not: {\n in: [JobStatus.CANCELLED],\n },\n } },\n data: {\n status: JobStatus.COMPLETED,\n isCorrect: (apiResponse as { isCorrect: boolean }).isCorrect,\n points: (apiResponse as { points: number }).points,\n markschemeState: (apiResponse as {\n markschemeState: { id: string; correct: boolean }[];\n }).markschemeState.reduce((acc, curr) => {\n acc[\"item-\" + curr.id] = curr.correct;\n return acc;\n }, {} as Record<string, boolean>),\n comments: {\n create: (apiResponse as {\n comments: string[];\n }).comments.map((commentContent) => ({\n content: commentContent,\n authorId: getAIUserId(),\n })),\n },\n },\n });\n pusher.trigger(worksheetChannel(worksheetResponse.id), `set-completed`, {\n id: updatedStudentQuestionProgress.id,\n });\n\n return updatedStudentQuestionProgress;\n } catch (error) {\n logger.error('Failed to grade worksheet response', { error, worksheetResponseId });\n const { count } = await prisma.studentQuestionProgress.updateMany({\n where: {\n id: studentQuestionProgress.id,\n status: { not: JobStatus.CANCELLED },\n },\n data: { status: JobStatus.FAILED },\n });\n if (count === 0) {\n logger.info('Skipping failed status update for cancelled worksheet question', {\n worksheetResponseId,\n worksheetQuestionProgressId,\n });\n return;\n }\n pusher.trigger(worksheetChannel(worksheetResponse.id), `set-failed`, {\n id: studentQuestionProgress.id,\n });\n throw error;\n }\n}\n\n/**\n * Grades and regrades worksheet (can fixed failed responses)\n * @param worksheetResponseId worksheet response id\n * @returns updated worksheet response\n */\n\nconst DO_NOT_INFERENCE_STATUSES = [JobStatus.CANCELLED, JobStatus.PENDING, JobStatus.COMPLETED];\n\nexport const gradeWorksheetPipeline = async (worksheetResponseId: string) => {\n logger.info('Grading worksheet response', { worksheetResponseId });\n const worksheetResponse = await prisma.studentWorksheetResponse.findUnique({\n where: { id: worksheetResponseId },\n include: {\n worksheet: true,\n responses: {\n where: {\n status: {\n not: {\n in: DO_NOT_INFERENCE_STATUSES,\n },\n },\n question: {\n type: {\n not: {\n in: [WorksheetQuestionType.MULTIPLE_CHOICE, WorksheetQuestionType.TRUE_FALSE],\n }\n },\n },\n },\n include: {\n question: true,\n comments: true,\n },\n },\n },\n });\n\n if (!worksheetResponse) {\n logger.error('Worksheet response not found');\n throw new Error('Worksheet response not found');\n }\n\n // Use for...of instead of forEach to properly handle async operations\n for (const response of worksheetResponse.responses) {\n logger.info('Grading question', { questionId: response.questionId });\n\n const { count } = await prisma.studentQuestionProgress.updateMany({\n where: {\n id: response.id,\n status: {\n not: {\n in: DO_NOT_INFERENCE_STATUSES,\n },\n },\n },\n data: { status: JobStatus.PENDING },\n });\n\n if (count === 0) {\n continue;\n }\n\n await gradeWorksheetQuestion(worksheetResponseId, response.id);\n\n };\n};\n\nexport const cancelGradePipeline = async (worksheetResponseId: string, worksheetQuestionProgressId: string) => {\n logger.info('Cancelling auto grading', { worksheetResponseId, worksheetQuestionProgressId });\n\n const worksheetResponse = await prisma.studentWorksheetResponse.findUnique({\n where: { id: worksheetResponseId },\n include: {\n worksheet: true,\n },\n });\n if (!worksheetResponse) {\n logger.error('Worksheet response not found');\n throw new Error('Worksheet response not found');\n }\n const updatedStudentQuestionProgress = await prisma.studentQuestionProgress.update({\n where: { id: worksheetQuestionProgressId },\n data: { status: JobStatus.CANCELLED },\n });\n\n await removeAllPreviousAIComments(worksheetQuestionProgressId);\n\n pusher.trigger(worksheetChannel(worksheetResponse.id), `set-cancelled`, {\n id: updatedStudentQuestionProgress.id,\n });\n\n return updatedStudentQuestionProgress;\n};\n\nexport const regradeWorksheetPipeline = async (worksheetResponseId: string, worksheetQuestionProgressId: string) => {\n logger.info('Regrading worksheet response', { worksheetResponseId, worksheetQuestionProgressId });\n try {\n const worksheetResponse = await prisma.studentWorksheetResponse.findUnique({\n where: { id: worksheetResponseId, },\n include: {\n worksheet: true,\n },\n });\n \n if (!worksheetResponse) {\n logger.error('Worksheet response not found');\n throw new Error('Worksheet response not found');\n }\n\n const { count } = await prisma.studentQuestionProgress.updateMany({\n where: {\n id: worksheetQuestionProgressId,\n status: { not: JobStatus.CANCELLED },\n },\n data: { status: JobStatus.PENDING },\n });\n\n if (count === 0) {\n logger.info('Skipping regrade for cancelled worksheet question', {\n worksheetResponseId,\n worksheetQuestionProgressId,\n });\n return null;\n }\n\n const updatedStudentQuestionProgress = await prisma.studentQuestionProgress.findUnique({\n where: { id: worksheetQuestionProgressId },\n });\n\n await removeAllPreviousAIComments(worksheetQuestionProgressId);\n\n await gradeWorksheetQuestion(worksheetResponseId, worksheetQuestionProgressId);\n\n return updatedStudentQuestionProgress;\n } catch (error) {\n const { count } = await prisma.studentQuestionProgress.updateMany({\n where: {\n id: worksheetQuestionProgressId,\n status: { not: JobStatus.CANCELLED },\n },\n data: { status: JobStatus.FAILED },\n });\n if (count === 0) {\n logger.info('Skipping failed status update for cancelled worksheet question regrade', {\n worksheetResponseId,\n worksheetQuestionProgressId,\n });\n return null;\n }\n const worksheetResponse = await prisma.studentWorksheetResponse.findUnique({\n where: { id: worksheetResponseId, },\n include: {\n worksheet: true,\n },\n });\n if (!worksheetResponse) {\n logger.error('Worksheet response not found');\n throw new Error('Worksheet response not found');\n }\n pusher.trigger(worksheetChannel(worksheetResponse.id), `set-failed`, {\n id: worksheetQuestionProgressId,\n });\n logger.error('Failed to regrade worksheet response', { error, worksheetResponseId, worksheetQuestionProgressId });\n throw error;\n }\n};\n"],"names":[],"mappings":"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","debug_id":"f57921a6-ccf6-53ed-9099-e9095fd0f801"}
1
+ {"version":3,"file":"gradeWorksheet.js","sources":["pipelines/gradeWorksheet.ts"],"sourceRoot":"/","sourcesContent":["/**\n * Grade worksheet pipeline – AI-powered grading of worksheet responses.\n * Grades questions via inference API, broadcasts status via Pusher (pending/completed/failed/cancelled).\n */\nimport { JobStatus, WorksheetQuestionType } from \"@prisma/client\";\nimport { prisma } from \"../lib/prisma.js\";\nimport { logger } from \"../utils/logger.js\";\nimport z from \"zod\";\nimport { inference } from \"../utils/inference.js\";\nimport { getAIUserId } from \"../utils/aiUser.js\";\nimport { pusher, worksheetChannel } from \"../lib/pusher.js\";\n\n\nconst removeAllPreviousAIComments = async (worksheetQuestionProgressId: string) => {\n await prisma.comment.deleteMany({\n where: {\n studentQuestionProgressId: worksheetQuestionProgressId,\n authorId: getAIUserId(),\n },\n });\n};\n\nconst gradeWorksheetQuestion = async (worksheetResponseId: string, worksheetQuestionProgressId: string) => {\n\n await removeAllPreviousAIComments(worksheetQuestionProgressId);\n\n const worksheetResponse = await prisma.studentWorksheetResponse.findUnique({\n where: { id: worksheetResponseId },\n include: {\n worksheet: true,\n },\n });\n\n if (!worksheetResponse) {\n logger.error('Worksheet response not found');\n throw new Error('Worksheet response not found');\n } \n\n const studentQuestionProgress = await prisma.studentQuestionProgress.findFirst({\n where: {\n id: worksheetQuestionProgressId,\n },\n include: {\n question: true,\n comments: true,\n },\n });\n\n if (!studentQuestionProgress) {\n logger.error('Student question progress not found', { worksheetQuestionProgressId });\n throw new Error('Student question progress not found');\n }\n\n pusher.trigger(worksheetChannel(worksheetResponse.id), `set-pending`, {\n id: studentQuestionProgress.id,\n });\n\n const question = studentQuestionProgress.question;\n const responseText = studentQuestionProgress.response;\n\n // Short-circuit for empty responses: award 0 points without calling inference\n if (!responseText || responseText.trim().length === 0) {\n const updatedProgress = await prisma.studentQuestionProgress.update({\n where: { id: studentQuestionProgress.id, status: {\n not: { in: [JobStatus.CANCELLED] },\n } },\n data: {\n status: JobStatus.COMPLETED,\n isCorrect: false,\n points: 0,\n markschemeState: {},\n comments: {\n create: [{\n content: 'No answer provided.',\n authorId: getAIUserId(),\n }],\n },\n },\n });\n pusher.trigger(worksheetChannel(worksheetResponse.id), `set-completed`, {\n id: updatedProgress.id,\n });\n return updatedProgress;\n }\n\n const comments = studentQuestionProgress.comments;\n\n try {\n const apiResponse = await inference(\n `Grade the following worksheet response:\n \n Question: ${question.question}\n Response: ${responseText}\n\n Comments: ${comments.map((comment) => comment.content).join('\\n')}\n Mark Scheme: ${JSON.stringify(question.markScheme)}\n \n Justify your reasoning by including comment(s) and mark the question please. \n Return ONLY JSON in the following format (fill in the values as per the question):\n {\n \"isCorrect\": <boolean>,\n \"points\": <number>,\n \"markschemeState\": [\n { \"id\": <string>, \"correct\": <boolean> }\n ],\n \"comments\": [<string>, ...]\n }\n `,\n z.object({\n isCorrect: z.boolean(),\n points: z.number(),\n markschemeState: z.array(z.object({\n id: z.string(),\n correct: z.boolean(),\n })), // @note: this has to be converted to [id: string]: correct boolean\n comments: z.array(z.string()),\n }),\n {\n reason: 'worksheet_autograde',\n metadata: {\n worksheetResponseId: worksheetResponse.id,\n questionId: worksheetQuestionProgressId,\n studentId: worksheetResponse.studentId,\n },\n },\n ).catch((error) => {\n logger.error('Failed to grade worksheet response', { error });\n throw error;\n });\n\n const updatedStudentQuestionProgress = await prisma.studentQuestionProgress.update({\n where: { id: studentQuestionProgress.id, status: {\n not: {\n in: [JobStatus.CANCELLED],\n },\n } },\n data: {\n status: JobStatus.COMPLETED,\n isCorrect: (apiResponse as { isCorrect: boolean }).isCorrect,\n points: (apiResponse as { points: number }).points,\n markschemeState: (apiResponse as {\n markschemeState: { id: string; correct: boolean }[];\n }).markschemeState.reduce((acc, curr) => {\n acc[\"item-\" + curr.id] = curr.correct;\n return acc;\n }, {} as Record<string, boolean>),\n comments: {\n create: (apiResponse as {\n comments: string[];\n }).comments.map((commentContent) => ({\n content: commentContent,\n authorId: getAIUserId(),\n })),\n },\n },\n });\n pusher.trigger(worksheetChannel(worksheetResponse.id), `set-completed`, {\n id: updatedStudentQuestionProgress.id,\n });\n\n return updatedStudentQuestionProgress;\n } catch (error) {\n logger.error('Failed to grade worksheet response', { error, worksheetResponseId });\n const { count } = await prisma.studentQuestionProgress.updateMany({\n where: {\n id: studentQuestionProgress.id,\n status: { not: JobStatus.CANCELLED },\n },\n data: { status: JobStatus.FAILED },\n });\n if (count === 0) {\n logger.info('Skipping failed status update for cancelled worksheet question', {\n worksheetResponseId,\n worksheetQuestionProgressId,\n });\n return;\n }\n pusher.trigger(worksheetChannel(worksheetResponse.id), `set-failed`, {\n id: studentQuestionProgress.id,\n });\n throw error;\n }\n}\n\n/**\n * Grades and regrades worksheet (can fixed failed responses)\n * @param worksheetResponseId worksheet response id\n * @returns updated worksheet response\n */\n\nconst DO_NOT_INFERENCE_STATUSES = [JobStatus.CANCELLED, JobStatus.PENDING, JobStatus.COMPLETED];\n\nexport const gradeWorksheetPipeline = async (worksheetResponseId: string) => {\n logger.info('Grading worksheet response', { worksheetResponseId });\n const worksheetResponse = await prisma.studentWorksheetResponse.findUnique({\n where: { id: worksheetResponseId },\n include: {\n worksheet: true,\n responses: {\n where: {\n status: {\n not: {\n in: DO_NOT_INFERENCE_STATUSES,\n },\n },\n question: {\n type: {\n not: {\n in: [WorksheetQuestionType.MULTIPLE_CHOICE, WorksheetQuestionType.TRUE_FALSE],\n }\n },\n },\n },\n include: {\n question: true,\n comments: true,\n },\n },\n },\n });\n\n if (!worksheetResponse) {\n logger.error('Worksheet response not found');\n throw new Error('Worksheet response not found');\n }\n\n // Find worksheet questions that have no StudentQuestionProgress for this response\n // and create empty progress records so they get graded (with 0 points).\n const allQuestions = await prisma.worksheetQuestion.findMany({\n where: {\n worksheetId: worksheetResponse.worksheetId,\n type: {\n notIn: [WorksheetQuestionType.MULTIPLE_CHOICE, WorksheetQuestionType.TRUE_FALSE],\n },\n },\n select: { id: true },\n });\n\n const existingQuestionIds = new Set(\n worksheetResponse.responses.map((r) => r.questionId),\n );\n\n for (const q of allQuestions) {\n if (existingQuestionIds.has(q.id)) continue;\n\n const created = await prisma.studentQuestionProgress.create({\n data: {\n studentId: worksheetResponse.studentId,\n questionId: q.id,\n studentWorksheetResponseId: worksheetResponseId,\n response: '',\n isCorrect: false,\n },\n });\n\n worksheetResponse.responses.push({\n ...created,\n question: { id: q.id } as any,\n comments: [],\n } as any);\n }\n\n for (const response of worksheetResponse.responses) {\n logger.info('Grading question', { questionId: response.questionId });\n\n const { count } = await prisma.studentQuestionProgress.updateMany({\n where: {\n id: response.id,\n status: {\n not: {\n in: DO_NOT_INFERENCE_STATUSES,\n },\n },\n },\n data: { status: JobStatus.PENDING },\n });\n\n if (count === 0) {\n continue;\n }\n\n await gradeWorksheetQuestion(worksheetResponseId, response.id);\n\n };\n};\n\nexport const cancelGradePipeline = async (worksheetResponseId: string, worksheetQuestionProgressId: string) => {\n logger.info('Cancelling auto grading', { worksheetResponseId, worksheetQuestionProgressId });\n\n const worksheetResponse = await prisma.studentWorksheetResponse.findUnique({\n where: { id: worksheetResponseId },\n include: {\n worksheet: true,\n },\n });\n if (!worksheetResponse) {\n logger.error('Worksheet response not found');\n throw new Error('Worksheet response not found');\n }\n const updatedStudentQuestionProgress = await prisma.studentQuestionProgress.update({\n where: { id: worksheetQuestionProgressId },\n data: { status: JobStatus.CANCELLED },\n });\n\n await removeAllPreviousAIComments(worksheetQuestionProgressId);\n\n pusher.trigger(worksheetChannel(worksheetResponse.id), `set-cancelled`, {\n id: updatedStudentQuestionProgress.id,\n });\n\n return updatedStudentQuestionProgress;\n};\n\nexport const regradeWorksheetPipeline = async (worksheetResponseId: string, worksheetQuestionProgressId: string) => {\n logger.info('Regrading worksheet response', { worksheetResponseId, worksheetQuestionProgressId });\n try {\n const worksheetResponse = await prisma.studentWorksheetResponse.findUnique({\n where: { id: worksheetResponseId, },\n include: {\n worksheet: true,\n },\n });\n \n if (!worksheetResponse) {\n logger.error('Worksheet response not found');\n throw new Error('Worksheet response not found');\n }\n\n const { count } = await prisma.studentQuestionProgress.updateMany({\n where: {\n id: worksheetQuestionProgressId,\n status: { not: JobStatus.CANCELLED },\n },\n data: { status: JobStatus.PENDING },\n });\n\n if (count === 0) {\n logger.info('Skipping regrade for cancelled worksheet question', {\n worksheetResponseId,\n worksheetQuestionProgressId,\n });\n return null;\n }\n\n const updatedStudentQuestionProgress = await prisma.studentQuestionProgress.findUnique({\n where: { id: worksheetQuestionProgressId },\n });\n\n await gradeWorksheetQuestion(worksheetResponseId, worksheetQuestionProgressId);\n\n return updatedStudentQuestionProgress;\n } catch (error) {\n const { count } = await prisma.studentQuestionProgress.updateMany({\n where: {\n id: worksheetQuestionProgressId,\n status: { not: JobStatus.CANCELLED },\n },\n data: { status: JobStatus.FAILED },\n });\n if (count === 0) {\n logger.info('Skipping failed status update for cancelled worksheet question regrade', {\n worksheetResponseId,\n worksheetQuestionProgressId,\n });\n return null;\n }\n const worksheetResponse = await prisma.studentWorksheetResponse.findUnique({\n where: { id: worksheetResponseId, },\n include: {\n worksheet: true,\n },\n });\n if (!worksheetResponse) {\n logger.error('Worksheet response not found');\n throw new Error('Worksheet response not found');\n }\n pusher.trigger(worksheetChannel(worksheetResponse.id), `set-failed`, {\n id: worksheetQuestionProgressId,\n });\n logger.error('Failed to regrade worksheet response', { error, worksheetResponseId, worksheetQuestionProgressId });\n throw error;\n }\n};\n"],"names":[],"mappings":"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","debug_id":"e408bc40-2745-52a0-b3ee-d97502bc2313"}