@studious-lms/server 1.24.0 → 1.26.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.
- package/data/curriculum/nepal-cdc-g4-g5/subjects/english.json +4 -4
- package/data/curriculum/nepal-cdc-g4-g5/subjects/mathematics.json +1 -1
- package/data/curriculum/nepal-cdc-g4-g5/subjects/science-technology.json +4 -4
- package/data/curriculum/nepal-cdc-g4-g5/subjects/social-studies.json +4 -4
- package/dist/models/attendance.d.ts.map +1 -1
- package/dist/models/attendance.js +17 -14
- package/dist/models/attendance.js.map +1 -1
- package/dist/models/marketplace.d.ts +37 -0
- package/dist/models/marketplace.d.ts.map +1 -1
- package/dist/models/marketplace.js +10 -2
- package/dist/models/marketplace.js.map +1 -1
- package/dist/models/module.d.ts +228 -0
- package/dist/models/module.d.ts.map +1 -0
- package/dist/models/module.js +69 -0
- package/dist/models/module.js.map +1 -0
- package/dist/models/moduleItem.d.ts +5 -1
- package/dist/models/moduleItem.d.ts.map +1 -1
- package/dist/models/moduleItem.js +19 -4
- package/dist/models/moduleItem.js.map +1 -1
- package/dist/pipelines/gradeWorksheet.d.ts.map +1 -1
- package/dist/pipelines/gradeWorksheet.js +4 -2
- package/dist/pipelines/gradeWorksheet.js.map +1 -1
- package/dist/routers/_app.d.ts +768 -6
- package/dist/routers/_app.d.ts.map +1 -1
- package/dist/routers/_app.js +4 -2
- package/dist/routers/_app.js.map +1 -1
- package/dist/routers/fees.d.ts +1 -1
- package/dist/routers/marketplace.d.ts +41 -0
- package/dist/routers/marketplace.d.ts.map +1 -1
- package/dist/routers/marketplace.js +13 -3
- package/dist/routers/marketplace.js.map +1 -1
- package/dist/routers/module.d.ts +345 -0
- package/dist/routers/module.d.ts.map +1 -0
- package/dist/routers/module.js +82 -0
- package/dist/routers/module.js.map +1 -0
- package/dist/routers/moduleItem.d.ts.map +1 -1
- package/dist/routers/moduleItem.js +7 -6
- package/dist/routers/moduleItem.js.map +1 -1
- package/dist/routers/parent.d.ts +2 -1
- package/dist/routers/parent.d.ts.map +1 -1
- package/dist/routers/school.d.ts +1 -1
- package/dist/routers/school.d.ts.map +1 -1
- package/dist/routers/school.js +14 -4
- package/dist/routers/school.js.map +1 -1
- package/dist/services/academicCalendar.d.ts.map +1 -1
- package/dist/services/academicCalendar.js +30 -2
- package/dist/services/academicCalendar.js.map +1 -1
- package/dist/services/assignment.d.ts.map +1 -1
- package/dist/services/assignment.js +8 -3
- package/dist/services/assignment.js.map +1 -1
- package/dist/services/attendance.d.ts +6 -2
- package/dist/services/attendance.d.ts.map +1 -1
- package/dist/services/attendance.js +8 -4
- package/dist/services/attendance.js.map +1 -1
- package/dist/services/class.d.ts +14 -9
- package/dist/services/class.d.ts.map +1 -1
- package/dist/services/class.js +4 -4
- package/dist/services/class.js.map +1 -1
- package/dist/services/fees.d.ts.map +1 -1
- package/dist/services/fees.js +24 -4
- package/dist/services/fees.js.map +1 -1
- package/dist/services/feesAuthz.d.ts +2 -5
- package/dist/services/feesAuthz.d.ts.map +1 -1
- package/dist/services/feesAuthz.js +5 -8
- package/dist/services/feesAuthz.js.map +1 -1
- package/dist/services/marketplace.d.ts +36 -0
- package/dist/services/marketplace.d.ts.map +1 -1
- package/dist/services/marketplace.js +36 -6
- package/dist/services/marketplace.js.map +1 -1
- package/dist/services/module.d.ts +288 -0
- package/dist/services/module.d.ts.map +1 -0
- package/dist/services/module.js +518 -0
- package/dist/services/module.js.map +1 -0
- package/dist/services/moduleItem.d.ts.map +1 -1
- package/dist/services/moduleItem.js +23 -7
- package/dist/services/moduleItem.js.map +1 -1
- package/dist/services/parentPortal.d.ts +3 -5
- package/dist/services/parentPortal.d.ts.map +1 -1
- package/dist/services/parentPortal.js +29 -43
- package/dist/services/parentPortal.js.map +1 -1
- package/dist/services/school.d.ts.map +1 -1
- package/dist/services/school.js +52 -16
- package/dist/services/school.js.map +1 -1
- package/dist/services/studentImport.d.ts.map +1 -1
- package/dist/services/studentImport.js +38 -25
- package/dist/services/studentImport.js.map +1 -1
- package/dist/services/transcript.d.ts +2 -3
- package/dist/services/transcript.d.ts.map +1 -1
- package/dist/services/transcript.js +13 -25
- package/dist/services/transcript.js.map +1 -1
- package/package.json +1 -1
- package/prisma/migrations/20260708013000_add_modules/migration.sql +100 -0
- package/prisma/schema.prisma +93 -0
- package/scripts/import-nepal-cdc-curriculum.ts +29 -6
- package/src/models/attendance.ts +10 -12
- package/src/models/marketplace.ts +19 -0
- package/src/models/module.ts +77 -0
- package/src/models/moduleItem.ts +20 -2
- package/src/pipelines/gradeWorksheet.ts +6 -0
- package/src/routers/_app.ts +2 -0
- package/src/routers/marketplace.ts +16 -0
- package/src/routers/module.ts +118 -0
- package/src/routers/moduleItem.ts +6 -4
- package/src/routers/school.ts +16 -6
- package/src/services/academicCalendar.ts +28 -0
- package/src/services/assignment.ts +12 -1
- package/src/services/attendance.ts +7 -3
- package/src/services/class.ts +2 -2
- package/src/services/fees.ts +25 -2
- package/src/services/feesAuthz.ts +3 -7
- package/src/services/marketplace.ts +47 -3
- package/src/services/module.ts +683 -0
- package/src/services/moduleItem.ts +28 -4
- package/src/services/parentPortal.ts +26 -39
- package/src/services/school.ts +52 -15
- package/src/services/studentImport.ts +37 -25
- package/src/services/transcript.ts +11 -23
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/** @returns Class if user is a teacher or school admin. */
|
|
2
|
+
export declare function findClassWithTeacher(classId: string, teacherId: string): import("@prisma/client/runtime/library.js").DynamicModelExtensionFluentApi<import(".prisma/client").Prisma.TypeMap<import("@prisma/client/runtime/library.js").InternalArgs & {
|
|
3
|
+
result: {
|
|
4
|
+
userProfile: {
|
|
5
|
+
profilePicture: () => {
|
|
6
|
+
needs: {
|
|
7
|
+
profilePicture: true;
|
|
8
|
+
};
|
|
9
|
+
compute(profile: {
|
|
10
|
+
profilePicture: string | null;
|
|
11
|
+
}): string | null;
|
|
12
|
+
};
|
|
13
|
+
profilePictureThumbnail: () => {
|
|
14
|
+
needs: {
|
|
15
|
+
profilePictureThumbnail: true;
|
|
16
|
+
};
|
|
17
|
+
compute(profile: {
|
|
18
|
+
profilePictureThumbnail: string | null;
|
|
19
|
+
}): string | null;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
model: {};
|
|
24
|
+
query: {};
|
|
25
|
+
client: {};
|
|
26
|
+
}, {}>, "Class", "findFirst", null> & import("@prisma/client/runtime/library.js").PrismaPromise<{
|
|
27
|
+
symbol: string | null;
|
|
28
|
+
section: string;
|
|
29
|
+
id: string;
|
|
30
|
+
name: string;
|
|
31
|
+
schoolId: string | null;
|
|
32
|
+
color: string | null;
|
|
33
|
+
subject: string;
|
|
34
|
+
syllabus: string | null;
|
|
35
|
+
departmentId: string | null;
|
|
36
|
+
discussionsEnabled: boolean;
|
|
37
|
+
studentsCanCreateDiscussions: boolean;
|
|
38
|
+
studentsCanReply: boolean;
|
|
39
|
+
sidebarFeatures: import("@prisma/client/runtime/library.js").JsonValue | null;
|
|
40
|
+
treatMissingAsZero: boolean;
|
|
41
|
+
gradeRoundingMode: import(".prisma/client").$Enums.GradeRoundingMode;
|
|
42
|
+
} | null>;
|
|
43
|
+
export declare const moduleInclude: {
|
|
44
|
+
readonly items: {
|
|
45
|
+
readonly orderBy: {
|
|
46
|
+
readonly position: "asc";
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
readonly prerequisites: {
|
|
50
|
+
readonly select: {
|
|
51
|
+
readonly prerequisiteId: true;
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
/** @returns All modules of a class ordered by position. */
|
|
56
|
+
export declare function findModulesByClassId(classId: string, publishedOnly?: boolean): import("@prisma/client/runtime/library.js").PrismaPromise<({
|
|
57
|
+
items: {
|
|
58
|
+
id: string;
|
|
59
|
+
title: string | null;
|
|
60
|
+
position: number;
|
|
61
|
+
moduleId: string;
|
|
62
|
+
indent: number;
|
|
63
|
+
itemType: import(".prisma/client").$Enums.CourseModuleItemType;
|
|
64
|
+
itemId: string | null;
|
|
65
|
+
externalUrl: string | null;
|
|
66
|
+
requirementType: import(".prisma/client").$Enums.ModuleItemRequirementType | null;
|
|
67
|
+
requiredScore: number | null;
|
|
68
|
+
}[];
|
|
69
|
+
prerequisites: {
|
|
70
|
+
prerequisiteId: string;
|
|
71
|
+
}[];
|
|
72
|
+
} & {
|
|
73
|
+
id: string;
|
|
74
|
+
createdAt: Date;
|
|
75
|
+
updatedAt: Date;
|
|
76
|
+
classId: string;
|
|
77
|
+
title: string;
|
|
78
|
+
position: number;
|
|
79
|
+
published: boolean;
|
|
80
|
+
unlockAt: Date | null;
|
|
81
|
+
requireSequential: boolean;
|
|
82
|
+
})[]>;
|
|
83
|
+
/** @returns Module by ID scoped to a class. */
|
|
84
|
+
export declare function findModuleByClassAndId(id: string, classId: string): import("@prisma/client/runtime/library.js").DynamicModelExtensionFluentApi<import(".prisma/client").Prisma.TypeMap<import("@prisma/client/runtime/library.js").InternalArgs & {
|
|
85
|
+
result: {
|
|
86
|
+
userProfile: {
|
|
87
|
+
profilePicture: () => {
|
|
88
|
+
needs: {
|
|
89
|
+
profilePicture: true;
|
|
90
|
+
};
|
|
91
|
+
compute(profile: {
|
|
92
|
+
profilePicture: string | null;
|
|
93
|
+
}): string | null;
|
|
94
|
+
};
|
|
95
|
+
profilePictureThumbnail: () => {
|
|
96
|
+
needs: {
|
|
97
|
+
profilePictureThumbnail: true;
|
|
98
|
+
};
|
|
99
|
+
compute(profile: {
|
|
100
|
+
profilePictureThumbnail: string | null;
|
|
101
|
+
}): string | null;
|
|
102
|
+
};
|
|
103
|
+
};
|
|
104
|
+
};
|
|
105
|
+
model: {};
|
|
106
|
+
query: {};
|
|
107
|
+
client: {};
|
|
108
|
+
}, {}>, "Module", "findFirst", null> & import("@prisma/client/runtime/library.js").PrismaPromise<({
|
|
109
|
+
items: {
|
|
110
|
+
id: string;
|
|
111
|
+
title: string | null;
|
|
112
|
+
position: number;
|
|
113
|
+
moduleId: string;
|
|
114
|
+
indent: number;
|
|
115
|
+
itemType: import(".prisma/client").$Enums.CourseModuleItemType;
|
|
116
|
+
itemId: string | null;
|
|
117
|
+
externalUrl: string | null;
|
|
118
|
+
requirementType: import(".prisma/client").$Enums.ModuleItemRequirementType | null;
|
|
119
|
+
requiredScore: number | null;
|
|
120
|
+
}[];
|
|
121
|
+
prerequisites: {
|
|
122
|
+
prerequisiteId: string;
|
|
123
|
+
}[];
|
|
124
|
+
} & {
|
|
125
|
+
id: string;
|
|
126
|
+
createdAt: Date;
|
|
127
|
+
updatedAt: Date;
|
|
128
|
+
classId: string;
|
|
129
|
+
title: string;
|
|
130
|
+
position: number;
|
|
131
|
+
published: boolean;
|
|
132
|
+
unlockAt: Date | null;
|
|
133
|
+
requireSequential: boolean;
|
|
134
|
+
}) | null>;
|
|
135
|
+
/** @returns Module item with its module, scoped to a class. */
|
|
136
|
+
export declare function findModuleItemInClass(itemId: string, classId: string): import("@prisma/client/runtime/library.js").DynamicModelExtensionFluentApi<import(".prisma/client").Prisma.TypeMap<import("@prisma/client/runtime/library.js").InternalArgs & {
|
|
137
|
+
result: {
|
|
138
|
+
userProfile: {
|
|
139
|
+
profilePicture: () => {
|
|
140
|
+
needs: {
|
|
141
|
+
profilePicture: true;
|
|
142
|
+
};
|
|
143
|
+
compute(profile: {
|
|
144
|
+
profilePicture: string | null;
|
|
145
|
+
}): string | null;
|
|
146
|
+
};
|
|
147
|
+
profilePictureThumbnail: () => {
|
|
148
|
+
needs: {
|
|
149
|
+
profilePictureThumbnail: true;
|
|
150
|
+
};
|
|
151
|
+
compute(profile: {
|
|
152
|
+
profilePictureThumbnail: string | null;
|
|
153
|
+
}): string | null;
|
|
154
|
+
};
|
|
155
|
+
};
|
|
156
|
+
};
|
|
157
|
+
model: {};
|
|
158
|
+
query: {};
|
|
159
|
+
client: {};
|
|
160
|
+
}, {}>, "CourseModuleItem", "findFirst", null> & import("@prisma/client/runtime/library.js").PrismaPromise<({
|
|
161
|
+
module: {
|
|
162
|
+
id: string;
|
|
163
|
+
createdAt: Date;
|
|
164
|
+
updatedAt: Date;
|
|
165
|
+
classId: string;
|
|
166
|
+
title: string;
|
|
167
|
+
position: number;
|
|
168
|
+
published: boolean;
|
|
169
|
+
unlockAt: Date | null;
|
|
170
|
+
requireSequential: boolean;
|
|
171
|
+
};
|
|
172
|
+
} & {
|
|
173
|
+
id: string;
|
|
174
|
+
title: string | null;
|
|
175
|
+
position: number;
|
|
176
|
+
moduleId: string;
|
|
177
|
+
indent: number;
|
|
178
|
+
itemType: import(".prisma/client").$Enums.CourseModuleItemType;
|
|
179
|
+
itemId: string | null;
|
|
180
|
+
externalUrl: string | null;
|
|
181
|
+
requirementType: import(".prisma/client").$Enums.ModuleItemRequirementType | null;
|
|
182
|
+
requiredScore: number | null;
|
|
183
|
+
}) | null>;
|
|
184
|
+
/** @returns Progress rows of a student for all items of a class. */
|
|
185
|
+
export declare function findStudentProgressForClass(classId: string, studentId: string): import("@prisma/client/runtime/library.js").PrismaPromise<{
|
|
186
|
+
status: import(".prisma/client").$Enums.ModuleItemProgressStatus;
|
|
187
|
+
id: string;
|
|
188
|
+
createdAt: Date;
|
|
189
|
+
updatedAt: Date;
|
|
190
|
+
moduleItemId: string;
|
|
191
|
+
studentId: string;
|
|
192
|
+
completedAt: Date | null;
|
|
193
|
+
}[]>;
|
|
194
|
+
/** Upsert a progress row for a student and item. */
|
|
195
|
+
export declare function upsertItemProgress(moduleItemId: string, studentId: string, status: "INCOMPLETE" | "COMPLETE"): import("@prisma/client/runtime/library.js").DynamicModelExtensionFluentApi<import(".prisma/client").Prisma.TypeMap<import("@prisma/client/runtime/library.js").InternalArgs & {
|
|
196
|
+
result: {
|
|
197
|
+
userProfile: {
|
|
198
|
+
profilePicture: () => {
|
|
199
|
+
needs: {
|
|
200
|
+
profilePicture: true;
|
|
201
|
+
};
|
|
202
|
+
compute(profile: {
|
|
203
|
+
profilePicture: string | null;
|
|
204
|
+
}): string | null;
|
|
205
|
+
};
|
|
206
|
+
profilePictureThumbnail: () => {
|
|
207
|
+
needs: {
|
|
208
|
+
profilePictureThumbnail: true;
|
|
209
|
+
};
|
|
210
|
+
compute(profile: {
|
|
211
|
+
profilePictureThumbnail: string | null;
|
|
212
|
+
}): string | null;
|
|
213
|
+
};
|
|
214
|
+
};
|
|
215
|
+
};
|
|
216
|
+
model: {};
|
|
217
|
+
query: {};
|
|
218
|
+
client: {};
|
|
219
|
+
}, {}>, "ModuleItemProgress", "upsert", never> & import("@prisma/client/runtime/library.js").PrismaPromise<{
|
|
220
|
+
status: import(".prisma/client").$Enums.ModuleItemProgressStatus;
|
|
221
|
+
id: string;
|
|
222
|
+
createdAt: Date;
|
|
223
|
+
updatedAt: Date;
|
|
224
|
+
moduleItemId: string;
|
|
225
|
+
studentId: string;
|
|
226
|
+
completedAt: Date | null;
|
|
227
|
+
}>;
|
|
228
|
+
//# sourceMappingURL=module.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"module.d.ts","sourceRoot":"/","sources":["models/module.ts"],"names":[],"mappings":"AAMA,2DAA2D;AAC3D,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UAOtE;AAED,eAAO,MAAM,aAAa;;;;;;;;;;;CAOhB,CAAC;AAEX,2DAA2D;AAC3D,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,aAAa,UAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;MAM1E;AAED,+CAA+C;AAC/C,wBAAgB,sBAAsB,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WAKjE;AAED,+DAA+D;AAC/D,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WAKpE;AAED,oEAAoE;AACpE,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;;;;;;;;KAI7E;AAED,oDAAoD;AACpD,wBAAgB,kBAAkB,CAChC,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,YAAY,GAAG,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAelC"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Module model – Canvas-style class modules, items and student progress.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
!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]="3e54b96a-f15c-5e1c-aac9-de042e3ac71c")}catch(e){}}();
|
|
6
|
+
import { prisma } from "../lib/prisma.js";
|
|
7
|
+
import { classTeacherOrSchoolAdminWhere } from "./access.js";
|
|
8
|
+
/** @returns Class if user is a teacher or school admin. */
|
|
9
|
+
export function findClassWithTeacher(classId, teacherId) {
|
|
10
|
+
return prisma.class.findFirst({
|
|
11
|
+
where: {
|
|
12
|
+
id: classId,
|
|
13
|
+
...classTeacherOrSchoolAdminWhere(teacherId),
|
|
14
|
+
},
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
export const moduleInclude = {
|
|
18
|
+
items: {
|
|
19
|
+
orderBy: { position: "asc" },
|
|
20
|
+
},
|
|
21
|
+
prerequisites: {
|
|
22
|
+
select: { prerequisiteId: true },
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
/** @returns All modules of a class ordered by position. */
|
|
26
|
+
export function findModulesByClassId(classId, publishedOnly = false) {
|
|
27
|
+
return prisma.module.findMany({
|
|
28
|
+
where: { classId, ...(publishedOnly && { published: true }) },
|
|
29
|
+
orderBy: { position: "asc" },
|
|
30
|
+
include: moduleInclude,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
/** @returns Module by ID scoped to a class. */
|
|
34
|
+
export function findModuleByClassAndId(id, classId) {
|
|
35
|
+
return prisma.module.findFirst({
|
|
36
|
+
where: { id, classId },
|
|
37
|
+
include: moduleInclude,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
/** @returns Module item with its module, scoped to a class. */
|
|
41
|
+
export function findModuleItemInClass(itemId, classId) {
|
|
42
|
+
return prisma.courseModuleItem.findFirst({
|
|
43
|
+
where: { id: itemId, module: { classId } },
|
|
44
|
+
include: { module: true },
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
/** @returns Progress rows of a student for all items of a class. */
|
|
48
|
+
export function findStudentProgressForClass(classId, studentId) {
|
|
49
|
+
return prisma.moduleItemProgress.findMany({
|
|
50
|
+
where: { studentId, moduleItem: { module: { classId } } },
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
/** Upsert a progress row for a student and item. */
|
|
54
|
+
export function upsertItemProgress(moduleItemId, studentId, status) {
|
|
55
|
+
return prisma.moduleItemProgress.upsert({
|
|
56
|
+
where: { moduleItemId_studentId: { moduleItemId, studentId } },
|
|
57
|
+
create: {
|
|
58
|
+
moduleItemId,
|
|
59
|
+
studentId,
|
|
60
|
+
status,
|
|
61
|
+
...(status === "COMPLETE" && { completedAt: new Date() }),
|
|
62
|
+
},
|
|
63
|
+
update: status === "COMPLETE"
|
|
64
|
+
? { status, completedAt: new Date() }
|
|
65
|
+
: {},
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=module.js.map
|
|
69
|
+
//# debugId=3e54b96a-f15c-5e1c-aac9-de042e3ac71c
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"module.js","sources":["models/module.ts"],"sourceRoot":"/","sourcesContent":["/**\n * Module model – Canvas-style class modules, items and student progress.\n */\nimport { prisma } from \"../lib/prisma.js\";\nimport { classTeacherOrSchoolAdminWhere } from \"./access.js\";\n\n/** @returns Class if user is a teacher or school admin. */\nexport function findClassWithTeacher(classId: string, teacherId: string) {\n return prisma.class.findFirst({\n where: {\n id: classId,\n ...classTeacherOrSchoolAdminWhere(teacherId),\n },\n });\n}\n\nexport const moduleInclude = {\n items: {\n orderBy: { position: \"asc\" as const },\n },\n prerequisites: {\n select: { prerequisiteId: true },\n },\n} as const;\n\n/** @returns All modules of a class ordered by position. */\nexport function findModulesByClassId(classId: string, publishedOnly = false) {\n return prisma.module.findMany({\n where: { classId, ...(publishedOnly && { published: true }) },\n orderBy: { position: \"asc\" },\n include: moduleInclude,\n });\n}\n\n/** @returns Module by ID scoped to a class. */\nexport function findModuleByClassAndId(id: string, classId: string) {\n return prisma.module.findFirst({\n where: { id, classId },\n include: moduleInclude,\n });\n}\n\n/** @returns Module item with its module, scoped to a class. */\nexport function findModuleItemInClass(itemId: string, classId: string) {\n return prisma.courseModuleItem.findFirst({\n where: { id: itemId, module: { classId } },\n include: { module: true },\n });\n}\n\n/** @returns Progress rows of a student for all items of a class. */\nexport function findStudentProgressForClass(classId: string, studentId: string) {\n return prisma.moduleItemProgress.findMany({\n where: { studentId, moduleItem: { module: { classId } } },\n });\n}\n\n/** Upsert a progress row for a student and item. */\nexport function upsertItemProgress(\n moduleItemId: string,\n studentId: string,\n status: \"INCOMPLETE\" | \"COMPLETE\"\n) {\n return prisma.moduleItemProgress.upsert({\n where: { moduleItemId_studentId: { moduleItemId, studentId } },\n create: {\n moduleItemId,\n studentId,\n status,\n ...(status === \"COMPLETE\" && { completedAt: new Date() }),\n },\n update:\n status === \"COMPLETE\"\n ? { status, completedAt: new Date() }\n : {},\n });\n}\n"],"names":[],"mappings":"AAAA;;GAEG;;;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,8BAA8B,EAAE,MAAM,aAAa,CAAC;AAE7D,2DAA2D;AAC3D,MAAM,UAAU,oBAAoB,CAAC,OAAe,EAAE,SAAiB;IACrE,OAAO,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC;QAC5B,KAAK,EAAE;YACL,EAAE,EAAE,OAAO;YACX,GAAG,8BAA8B,CAAC,SAAS,CAAC;SAC7C;KACF,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,KAAK,EAAE;QACL,OAAO,EAAE,EAAE,QAAQ,EAAE,KAAc,EAAE;KACtC;IACD,aAAa,EAAE;QACb,MAAM,EAAE,EAAE,cAAc,EAAE,IAAI,EAAE;KACjC;CACO,CAAC;AAEX,2DAA2D;AAC3D,MAAM,UAAU,oBAAoB,CAAC,OAAe,EAAE,aAAa,GAAG,KAAK;IACzE,OAAO,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC;QAC5B,KAAK,EAAE,EAAE,OAAO,EAAE,GAAG,CAAC,aAAa,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,EAAE;QAC7D,OAAO,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE;QAC5B,OAAO,EAAE,aAAa;KACvB,CAAC,CAAC;AACL,CAAC;AAED,+CAA+C;AAC/C,MAAM,UAAU,sBAAsB,CAAC,EAAU,EAAE,OAAe;IAChE,OAAO,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC;QAC7B,KAAK,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE;QACtB,OAAO,EAAE,aAAa;KACvB,CAAC,CAAC;AACL,CAAC;AAED,+DAA+D;AAC/D,MAAM,UAAU,qBAAqB,CAAC,MAAc,EAAE,OAAe;IACnE,OAAO,MAAM,CAAC,gBAAgB,CAAC,SAAS,CAAC;QACvC,KAAK,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,EAAE;QAC1C,OAAO,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE;KAC1B,CAAC,CAAC;AACL,CAAC;AAED,oEAAoE;AACpE,MAAM,UAAU,2BAA2B,CAAC,OAAe,EAAE,SAAiB;IAC5E,OAAO,MAAM,CAAC,kBAAkB,CAAC,QAAQ,CAAC;QACxC,KAAK,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE;KAC1D,CAAC,CAAC;AACL,CAAC;AAED,oDAAoD;AACpD,MAAM,UAAU,kBAAkB,CAChC,YAAoB,EACpB,SAAiB,EACjB,MAAiC;IAEjC,OAAO,MAAM,CAAC,kBAAkB,CAAC,MAAM,CAAC;QACtC,KAAK,EAAE,EAAE,sBAAsB,EAAE,EAAE,YAAY,EAAE,SAAS,EAAE,EAAE;QAC9D,MAAM,EAAE;YACN,YAAY;YACZ,SAAS;YACT,MAAM;YACN,GAAG,CAAC,MAAM,KAAK,UAAU,IAAI,EAAE,WAAW,EAAE,IAAI,IAAI,EAAE,EAAE,CAAC;SAC1D;QACD,MAAM,EACJ,MAAM,KAAK,UAAU;YACnB,CAAC,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,IAAI,IAAI,EAAE,EAAE;YACrC,CAAC,CAAC,EAAE;KACT,CAAC,CAAC;AACL,CAAC","debug_id":"3e54b96a-f15c-5e1c-aac9-de042e3ac71c"}
|
|
@@ -300,5 +300,9 @@ export declare function deleteModuleItem(id: string): import("@prisma/client/run
|
|
|
300
300
|
order: number | null;
|
|
301
301
|
published: boolean;
|
|
302
302
|
}>;
|
|
303
|
-
export declare function
|
|
303
|
+
export declare function findAccessibleFilesByIds(fileIds: string[], userId: string): import("@prisma/client/runtime/library.js").PrismaPromise<{
|
|
304
|
+
type: string;
|
|
305
|
+
id: string;
|
|
306
|
+
}[]>;
|
|
307
|
+
export declare function attachFilesToModuleItem(moduleItemId: string, fileIds: string[], userId: string): import("@prisma/client/runtime/library.js").PrismaPromise<import("@prisma/client/runtime/library.js").GetBatchResult>;
|
|
304
308
|
//# sourceMappingURL=moduleItem.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"moduleItem.d.ts","sourceRoot":"/","sources":["models/moduleItem.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAsBrD,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;MAMvD;AAED,wBAAgB,0BAA0B,CAAC,SAAS,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;MAM3D;AAED,wBAAgB,2BAA2B,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WAQrE;AAED,wBAAgB,4BAA4B,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WAQtE;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE;IACrC,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,cAAc,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAKA;AAED,wBAAgB,gBAAgB,CAC9B,EAAE,EAAE,MAAM,EACV,IAAI,EAAE;IACJ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,IAAI,CAAC,EAAE,cAAc,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAOF;AAED,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAE1C;AAED,wBAAgB,uBAAuB,CACrC,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"moduleItem.d.ts","sourceRoot":"/","sources":["models/moduleItem.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAsBrD,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;MAMvD;AAED,wBAAgB,0BAA0B,CAAC,SAAS,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;MAM3D;AAED,wBAAgB,2BAA2B,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WAQrE;AAED,wBAAgB,4BAA4B,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WAQtE;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE;IACrC,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,cAAc,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAKA;AAED,wBAAgB,gBAAgB,CAC9B,EAAE,EAAE,MAAM,EACV,IAAI,EAAE;IACJ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,IAAI,CAAC,EAAE,cAAc,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAOF;AAED,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAE1C;AAYD,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,MAAM;;;KAKzE;AAED,wBAAgB,uBAAuB,CACrC,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,MAAM,EAAE,EACjB,MAAM,EAAE,MAAM,yHAMf"}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* ModuleItem model – Canvas-style pages / file collections / videos in a section.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
!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]="
|
|
5
|
+
!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]="2f0405dc-024b-5d26-b459-816b46ee79ff")}catch(e){}}();
|
|
6
6
|
import { prisma } from "../lib/prisma.js";
|
|
7
7
|
import { classMemberOrSchoolAdminWhere, classTeacherOrSchoolAdminWhere, } from "./access.js";
|
|
8
8
|
const moduleItemInclude = {
|
|
@@ -68,11 +68,26 @@ export function updateModuleItem(id, data) {
|
|
|
68
68
|
export function deleteModuleItem(id) {
|
|
69
69
|
return prisma.moduleItem.delete({ where: { id } });
|
|
70
70
|
}
|
|
71
|
-
|
|
71
|
+
function fileAccessibleToUserWhere(userId) {
|
|
72
|
+
return {
|
|
73
|
+
OR: [
|
|
74
|
+
{ userId },
|
|
75
|
+
{ folder: { personalOwnerId: userId } },
|
|
76
|
+
{ folder: { class: { is: classTeacherOrSchoolAdminWhere(userId) } } },
|
|
77
|
+
],
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
export function findAccessibleFilesByIds(fileIds, userId) {
|
|
81
|
+
return prisma.file.findMany({
|
|
82
|
+
where: { id: { in: fileIds }, ...fileAccessibleToUserWhere(userId) },
|
|
83
|
+
select: { id: true, type: true },
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
export function attachFilesToModuleItem(moduleItemId, fileIds, userId) {
|
|
72
87
|
return prisma.file.updateMany({
|
|
73
|
-
where: { id: { in: fileIds } },
|
|
88
|
+
where: { id: { in: fileIds }, ...fileAccessibleToUserWhere(userId) },
|
|
74
89
|
data: { moduleItemId },
|
|
75
90
|
});
|
|
76
91
|
}
|
|
77
92
|
//# sourceMappingURL=moduleItem.js.map
|
|
78
|
-
//# debugId=
|
|
93
|
+
//# debugId=2f0405dc-024b-5d26-b459-816b46ee79ff
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"moduleItem.js","sources":["models/moduleItem.ts"],"sourceRoot":"/","sourcesContent":["/**\n * ModuleItem model – Canvas-style pages / file collections / videos in a section.\n */\nimport { prisma } from \"../lib/prisma.js\";\nimport type { ModuleItemType } from \"@prisma/client\";\nimport {\n classMemberOrSchoolAdminWhere,\n classTeacherOrSchoolAdminWhere,\n} from \"./access.js\";\n\nconst moduleItemInclude = {\n files: {\n select: {\n id: true,\n name: true,\n type: true,\n size: true,\n path: true,\n uploadStatus: true,\n },\n },\n section: {\n select: { id: true, name: true, classId: true, color: true },\n },\n} as const;\n\nexport function findModuleItemsByClassId(classId: string) {\n return prisma.moduleItem.findMany({\n where: { section: { classId } },\n include: moduleItemInclude,\n orderBy: [{ section: { order: \"asc\" } }, { order: \"asc\" }],\n });\n}\n\nexport function findModuleItemsBySectionId(sectionId: string) {\n return prisma.moduleItem.findMany({\n where: { sectionId },\n include: moduleItemInclude,\n orderBy: { order: \"asc\" },\n });\n}\n\nexport function findModuleItemByIdForMember(id: string, userId: string) {\n return prisma.moduleItem.findFirst({\n where: {\n id,\n section: { class: classMemberOrSchoolAdminWhere(userId) },\n },\n include: moduleItemInclude,\n });\n}\n\nexport function findModuleItemByIdForTeacher(id: string, userId: string) {\n return prisma.moduleItem.findFirst({\n where: {\n id,\n section: { class: classTeacherOrSchoolAdminWhere(userId) },\n },\n include: moduleItemInclude,\n });\n}\n\nexport function createModuleItem(data: {\n sectionId: string;\n title: string;\n type: ModuleItemType;\n body?: string | null;\n order?: number | null;\n published?: boolean;\n}) {\n return prisma.moduleItem.create({\n data,\n include: moduleItemInclude,\n });\n}\n\nexport function updateModuleItem(\n id: string,\n data: {\n title?: string;\n body?: string | null;\n order?: number | null;\n published?: boolean;\n type?: ModuleItemType;\n sectionId?: string;\n }\n) {\n return prisma.moduleItem.update({\n where: { id },\n data,\n include: moduleItemInclude,\n });\n}\n\nexport function deleteModuleItem(id: string) {\n return prisma.moduleItem.delete({ where: { id } });\n}\n\nexport function attachFilesToModuleItem(\n moduleItemId: string,\n fileIds: string[]\n) {\n return prisma.file.updateMany({\n where: { id: { in: fileIds } },\n data: { moduleItemId },\n });\n}\n"],"names":[],"mappings":"AAAA;;GAEG;;;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE1C,OAAO,EACL,6BAA6B,EAC7B,8BAA8B,GAC/B,MAAM,aAAa,CAAC;AAErB,MAAM,iBAAiB,GAAG;IACxB,KAAK,EAAE;QACL,MAAM,EAAE;YACN,EAAE,EAAE,IAAI;YACR,IAAI,EAAE,IAAI;YACV,IAAI,EAAE,IAAI;YACV,IAAI,EAAE,IAAI;YACV,IAAI,EAAE,IAAI;YACV,YAAY,EAAE,IAAI;SACnB;KACF;IACD,OAAO,EAAE;QACP,MAAM,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE;KAC7D;CACO,CAAC;AAEX,MAAM,UAAU,wBAAwB,CAAC,OAAe;IACtD,OAAO,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC;QAChC,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,EAAE;QAC/B,OAAO,EAAE,iBAAiB;QAC1B,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;KAC3D,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,0BAA0B,CAAC,SAAiB;IAC1D,OAAO,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC;QAChC,KAAK,EAAE,EAAE,SAAS,EAAE;QACpB,OAAO,EAAE,iBAAiB;QAC1B,OAAO,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE;KAC1B,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,2BAA2B,CAAC,EAAU,EAAE,MAAc;IACpE,OAAO,MAAM,CAAC,UAAU,CAAC,SAAS,CAAC;QACjC,KAAK,EAAE;YACL,EAAE;YACF,OAAO,EAAE,EAAE,KAAK,EAAE,6BAA6B,CAAC,MAAM,CAAC,EAAE;SAC1D;QACD,OAAO,EAAE,iBAAiB;KAC3B,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,4BAA4B,CAAC,EAAU,EAAE,MAAc;IACrE,OAAO,MAAM,CAAC,UAAU,CAAC,SAAS,CAAC;QACjC,KAAK,EAAE;YACL,EAAE;YACF,OAAO,EAAE,EAAE,KAAK,EAAE,8BAA8B,CAAC,MAAM,CAAC,EAAE;SAC3D;QACD,OAAO,EAAE,iBAAiB;KAC3B,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,IAOhC;IACC,OAAO,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC;QAC9B,IAAI;QACJ,OAAO,EAAE,iBAAiB;KAC3B,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,gBAAgB,CAC9B,EAAU,EACV,IAOC;IAED,OAAO,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC;QAC9B,KAAK,EAAE,EAAE,EAAE,EAAE;QACb,IAAI;QACJ,OAAO,EAAE,iBAAiB;KAC3B,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,EAAU;IACzC,OAAO,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;AACrD,CAAC;AAED,MAAM,UAAU,uBAAuB,CACrC,YAAoB,EACpB,OAAiB;
|
|
1
|
+
{"version":3,"file":"moduleItem.js","sources":["models/moduleItem.ts"],"sourceRoot":"/","sourcesContent":["/**\n * ModuleItem model – Canvas-style pages / file collections / videos in a section.\n */\nimport { prisma } from \"../lib/prisma.js\";\nimport type { ModuleItemType } from \"@prisma/client\";\nimport {\n classMemberOrSchoolAdminWhere,\n classTeacherOrSchoolAdminWhere,\n} from \"./access.js\";\n\nconst moduleItemInclude = {\n files: {\n select: {\n id: true,\n name: true,\n type: true,\n size: true,\n path: true,\n uploadStatus: true,\n },\n },\n section: {\n select: { id: true, name: true, classId: true, color: true },\n },\n} as const;\n\nexport function findModuleItemsByClassId(classId: string) {\n return prisma.moduleItem.findMany({\n where: { section: { classId } },\n include: moduleItemInclude,\n orderBy: [{ section: { order: \"asc\" } }, { order: \"asc\" }],\n });\n}\n\nexport function findModuleItemsBySectionId(sectionId: string) {\n return prisma.moduleItem.findMany({\n where: { sectionId },\n include: moduleItemInclude,\n orderBy: { order: \"asc\" },\n });\n}\n\nexport function findModuleItemByIdForMember(id: string, userId: string) {\n return prisma.moduleItem.findFirst({\n where: {\n id,\n section: { class: classMemberOrSchoolAdminWhere(userId) },\n },\n include: moduleItemInclude,\n });\n}\n\nexport function findModuleItemByIdForTeacher(id: string, userId: string) {\n return prisma.moduleItem.findFirst({\n where: {\n id,\n section: { class: classTeacherOrSchoolAdminWhere(userId) },\n },\n include: moduleItemInclude,\n });\n}\n\nexport function createModuleItem(data: {\n sectionId: string;\n title: string;\n type: ModuleItemType;\n body?: string | null;\n order?: number | null;\n published?: boolean;\n}) {\n return prisma.moduleItem.create({\n data,\n include: moduleItemInclude,\n });\n}\n\nexport function updateModuleItem(\n id: string,\n data: {\n title?: string;\n body?: string | null;\n order?: number | null;\n published?: boolean;\n type?: ModuleItemType;\n sectionId?: string;\n }\n) {\n return prisma.moduleItem.update({\n where: { id },\n data,\n include: moduleItemInclude,\n });\n}\n\nexport function deleteModuleItem(id: string) {\n return prisma.moduleItem.delete({ where: { id } });\n}\n\nfunction fileAccessibleToUserWhere(userId: string) {\n return {\n OR: [\n { userId },\n { folder: { personalOwnerId: userId } },\n { folder: { class: { is: classTeacherOrSchoolAdminWhere(userId) } } },\n ],\n };\n}\n\nexport function findAccessibleFilesByIds(fileIds: string[], userId: string) {\n return prisma.file.findMany({\n where: { id: { in: fileIds }, ...fileAccessibleToUserWhere(userId) },\n select: { id: true, type: true },\n });\n}\n\nexport function attachFilesToModuleItem(\n moduleItemId: string,\n fileIds: string[],\n userId: string\n) {\n return prisma.file.updateMany({\n where: { id: { in: fileIds }, ...fileAccessibleToUserWhere(userId) },\n data: { moduleItemId },\n });\n}\n"],"names":[],"mappings":"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","debug_id":"2f0405dc-024b-5d26-b459-816b46ee79ff"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gradeWorksheet.d.ts","sourceRoot":"/","sources":["pipelines/gradeWorksheet.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"gradeWorksheet.d.ts","sourceRoot":"/","sources":["pipelines/gradeWorksheet.ts"],"names":[],"mappings":"AA8QA,eAAO,MAAM,sBAAsB,GAC/B,qBAAqB,MAAM,EAC3B,eAAe,MAAM,GAAG,IAAI,kBAyG/B,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAAU,qBAAqB,MAAM,EAAE,6BAA6B,MAAM;;;;;;;;;;;;;;EAyBzG,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAC3B,qBAAqB,MAAM,EAC3B,eAAe,MAAM,GAAG,IAAI,kBAwD/B,CAAC;AAEF,eAAO,MAAM,wBAAwB,GACjC,qBAAqB,MAAM,EAC3B,6BAA6B,MAAM,EACnC,eAAe,MAAM,GAAG,IAAI;;;;;;;;;;;;;;SAsE/B,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]="
|
|
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]="c9757a56-c224-55bb-9755-7290f2990294")}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";
|
|
@@ -11,6 +11,7 @@ import z from "zod";
|
|
|
11
11
|
import { inference } from "../utils/inference.js";
|
|
12
12
|
import { getAIUserId } from "../utils/aiUser.js";
|
|
13
13
|
import { pusher, worksheetChannel } from "../lib/pusher.js";
|
|
14
|
+
import { syncWorksheetModuleProgress } from "../services/module.js";
|
|
14
15
|
const removeAllPreviousAIComments = async (worksheetQuestionProgressId) => {
|
|
15
16
|
await prisma.comment.deleteMany({
|
|
16
17
|
where: {
|
|
@@ -309,6 +310,7 @@ export const gradeWorksheetPipeline = async (worksheetResponseId, actingUserId)
|
|
|
309
310
|
}
|
|
310
311
|
}
|
|
311
312
|
;
|
|
313
|
+
await syncWorksheetModuleProgress(worksheetResponse.studentId, worksheetResponse.worksheetId);
|
|
312
314
|
};
|
|
313
315
|
export const cancelGradePipeline = async (worksheetResponseId, worksheetQuestionProgressId) => {
|
|
314
316
|
logger.info('Cancelling auto grading', { worksheetResponseId, worksheetQuestionProgressId });
|
|
@@ -451,4 +453,4 @@ export const regradeWorksheetPipeline = async (worksheetResponseId, worksheetQue
|
|
|
451
453
|
}
|
|
452
454
|
};
|
|
453
455
|
//# sourceMappingURL=gradeWorksheet.js.map
|
|
454
|
-
//# debugId=
|
|
456
|
+
//# debugId=c9757a56-c224-55bb-9755-7290f2990294
|
|
@@ -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\ntype MarkSchemeItem = { id: string; points: number; description?: string; text?: string };\n\nconst gradingResponseSchema = 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 })),\n comments: z.array(z.string()),\n annotations: z.array(z.object({\n quote: z.string().describe('An exact substring from the student response that is relevant to this criterion.'),\n markSchemeId: z.string().describe('The id of the mark scheme criterion this quote relates to.'),\n criterion: z.string().describe('Short label describing what this mark scheme criterion evaluates.'),\n met: z.boolean().describe('Whether the quoted text satisfies this criterion.'),\n })).describe('Highlight specific parts of the student response and map each to a mark scheme criterion.'),\n});\n\nfunction buildGradingPrompt(\n question: { question: string; answer: string; type: WorksheetQuestionType; points: number; markScheme: unknown },\n responseText: string,\n): string {\n const markSchemeItems = Array.isArray(question.markScheme) ? question.markScheme as MarkSchemeItem[] : [];\n const markSchemeBlock = markSchemeItems.length > 0\n ? markSchemeItems.map((item, i) =>\n ` ${i + 1}. [id=\"${item.id}\", ${item.points} pts] ${item.description || item.text || `Criterion ${i + 1}`}`\n ).join('\\n')\n : ' No mark scheme provided — grade based on correctness and completeness.';\n\n const maxPoints = question.points || markSchemeItems.reduce((sum, item) => sum + (item.points || 0), 0);\n\n return `Grade the following student response.\n\nQuestion type: ${question.type}\nMaximum points: ${maxPoints}\n\nQUESTION:\n${question.question}\n\nCORRECT ANSWER:\n${question.answer || 'No answer key provided.'}\n\nMARK SCHEME:\n${markSchemeBlock}\n\nSTUDENT RESPONSE:\n${responseText}\n\nINSTRUCTIONS:\n- Evaluate the student response against each mark scheme criterion.\n- Total points must not exceed ${maxPoints}.\n- In markschemeState, use the EXACT \"id\" string from the MARK SCHEME items above (e.g. \"${markSchemeItems[0]?.id || 'ms-xxx'}\"). Do NOT invent new IDs.\n- In annotations, use the same EXACT mark scheme id for \"markSchemeId\".\n- For each criterion, find the exact substring in the student's response that is relevant (the \"quote\" field). Copy the text verbatim — do not paraphrase.\n- If no part of the response addresses a criterion, still include an annotation with an empty quote and met=false.\n- Provide constructive feedback comments.\n- Mark isCorrect=true only if the student earned full or near-full marks.`;\n}\n\nconst gradeWorksheetQuestion = async (\n worksheetResponseId: string,\n worksheetQuestionProgressId: string,\n actingUserId?: string | null,\n) => {\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 try {\n const prompt = buildGradingPrompt(question, responseText);\n\n const apiResponse = await inference(\n prompt,\n gradingResponseSchema,\n {\n reason: 'worksheet_autograde',\n metadata: {\n worksheetResponseId: worksheetResponse.id,\n questionId: worksheetQuestionProgressId,\n studentId: worksheetResponse.studentId,\n },\n attribution: {\n userId: actingUserId ?? worksheetResponse.studentId,\n classId: worksheetResponse.worksheet.classId,\n },\n },\n ).catch((error) => {\n logger.error('Failed to grade worksheet response', { error });\n throw error;\n });\n\n const maxPoints = question.points || 0;\n const clampedPoints = Math.max(0, Math.min(Math.round((apiResponse as { points: number }).points), maxPoints));\n\n const markSchemeItems = Array.isArray(question.markScheme) ? question.markScheme as MarkSchemeItem[] : [];\n const validMarkSchemeIds = new Set(markSchemeItems.map(item => item.id));\n\n const aiMarkschemeState = (apiResponse as {\n markschemeState: { id: string; correct: boolean }[];\n }).markschemeState;\n\n const allIdsValid = aiMarkschemeState.length > 0 &&\n aiMarkschemeState.every(entry => validMarkSchemeIds.has(entry.id));\n\n let resolvedState: Record<string, boolean | unknown[]> = {};\n\n if (allIdsValid) {\n resolvedState = aiMarkschemeState.reduce((acc, curr) => {\n acc[\"item-\" + curr.id] = curr.correct;\n return acc;\n }, {} as Record<string, boolean>);\n } else if (markSchemeItems.length > 0) {\n logger.warn('AI returned mismatched markscheme IDs, falling back to index mapping', {\n expected: markSchemeItems.map(i => i.id),\n received: aiMarkschemeState.map(i => i.id),\n });\n for (let i = 0; i < markSchemeItems.length; i++) {\n const aiEntry = aiMarkschemeState[i];\n resolvedState[\"item-\" + markSchemeItems[i].id] = aiEntry?.correct ?? false;\n }\n }\n\n const annotations = (apiResponse as {\n annotations: { quote: string; markSchemeId: string; criterion: string; met: boolean }[];\n }).annotations\n .filter(a => a.quote.length === 0 || responseText.includes(a.quote));\n\n // Store annotations separately so the boolean-only markschemeState\n // isn't broken by non-boolean values (the frontend parser rejects\n // the entire object if any value isn't a boolean).\n resolvedState['_annotations'] = annotations;\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: clampedPoints,\n markschemeState: resolvedState,\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 (\n worksheetResponseId: string,\n actingUserId?: string | null,\n) => {\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 try {\n await gradeWorksheetQuestion(worksheetResponseId, response.id, actingUserId);\n } catch (questionError) {\n logger.error('Failed to grade individual question, continuing with remaining questions', {\n questionId: response.questionId,\n error: questionError,\n });\n }\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 regradeAllPipeline = async (\n worksheetResponseId: string,\n actingUserId?: string | null,\n) => {\n logger.info('Regrading all questions for worksheet response', { worksheetResponseId });\n const worksheetResponse = await prisma.studentWorksheetResponse.findUnique({\n where: { id: worksheetResponseId },\n include: {\n worksheet: true,\n responses: {\n where: {\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 // Reset all non-cancelled responses to PENDING\n for (const response of worksheetResponse.responses) {\n await prisma.studentQuestionProgress.updateMany({\n where: {\n id: response.id,\n status: { not: JobStatus.CANCELLED },\n },\n data: { status: JobStatus.PENDING },\n });\n\n pusher.trigger(worksheetChannel(worksheetResponse.id), `set-pending`, {\n id: response.id,\n });\n }\n\n // Re-grade each question sequentially\n for (const response of worksheetResponse.responses) {\n try {\n await gradeWorksheetQuestion(worksheetResponseId, response.id, actingUserId);\n } catch (questionError) {\n logger.error('Failed to regrade question, continuing with remaining', {\n questionId: response.questionId,\n error: questionError,\n });\n }\n }\n};\n\nexport const regradeWorksheetPipeline = async (\n worksheetResponseId: string,\n worksheetQuestionProgressId: string,\n actingUserId?: string | null,\n) => {\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, actingUserId);\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":"60823007-f8d4-5536-b222-c825a7db8909"}
|
|
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\";\nimport { syncWorksheetModuleProgress } from \"../services/module.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\ntype MarkSchemeItem = { id: string; points: number; description?: string; text?: string };\n\nconst gradingResponseSchema = 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 })),\n comments: z.array(z.string()),\n annotations: z.array(z.object({\n quote: z.string().describe('An exact substring from the student response that is relevant to this criterion.'),\n markSchemeId: z.string().describe('The id of the mark scheme criterion this quote relates to.'),\n criterion: z.string().describe('Short label describing what this mark scheme criterion evaluates.'),\n met: z.boolean().describe('Whether the quoted text satisfies this criterion.'),\n })).describe('Highlight specific parts of the student response and map each to a mark scheme criterion.'),\n});\n\nfunction buildGradingPrompt(\n question: { question: string; answer: string; type: WorksheetQuestionType; points: number; markScheme: unknown },\n responseText: string,\n): string {\n const markSchemeItems = Array.isArray(question.markScheme) ? question.markScheme as MarkSchemeItem[] : [];\n const markSchemeBlock = markSchemeItems.length > 0\n ? markSchemeItems.map((item, i) =>\n ` ${i + 1}. [id=\"${item.id}\", ${item.points} pts] ${item.description || item.text || `Criterion ${i + 1}`}`\n ).join('\\n')\n : ' No mark scheme provided — grade based on correctness and completeness.';\n\n const maxPoints = question.points || markSchemeItems.reduce((sum, item) => sum + (item.points || 0), 0);\n\n return `Grade the following student response.\n\nQuestion type: ${question.type}\nMaximum points: ${maxPoints}\n\nQUESTION:\n${question.question}\n\nCORRECT ANSWER:\n${question.answer || 'No answer key provided.'}\n\nMARK SCHEME:\n${markSchemeBlock}\n\nSTUDENT RESPONSE:\n${responseText}\n\nINSTRUCTIONS:\n- Evaluate the student response against each mark scheme criterion.\n- Total points must not exceed ${maxPoints}.\n- In markschemeState, use the EXACT \"id\" string from the MARK SCHEME items above (e.g. \"${markSchemeItems[0]?.id || 'ms-xxx'}\"). Do NOT invent new IDs.\n- In annotations, use the same EXACT mark scheme id for \"markSchemeId\".\n- For each criterion, find the exact substring in the student's response that is relevant (the \"quote\" field). Copy the text verbatim — do not paraphrase.\n- If no part of the response addresses a criterion, still include an annotation with an empty quote and met=false.\n- Provide constructive feedback comments.\n- Mark isCorrect=true only if the student earned full or near-full marks.`;\n}\n\nconst gradeWorksheetQuestion = async (\n worksheetResponseId: string,\n worksheetQuestionProgressId: string,\n actingUserId?: string | null,\n) => {\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 try {\n const prompt = buildGradingPrompt(question, responseText);\n\n const apiResponse = await inference(\n prompt,\n gradingResponseSchema,\n {\n reason: 'worksheet_autograde',\n metadata: {\n worksheetResponseId: worksheetResponse.id,\n questionId: worksheetQuestionProgressId,\n studentId: worksheetResponse.studentId,\n },\n attribution: {\n userId: actingUserId ?? worksheetResponse.studentId,\n classId: worksheetResponse.worksheet.classId,\n },\n },\n ).catch((error) => {\n logger.error('Failed to grade worksheet response', { error });\n throw error;\n });\n\n const maxPoints = question.points || 0;\n const clampedPoints = Math.max(0, Math.min(Math.round((apiResponse as { points: number }).points), maxPoints));\n\n const markSchemeItems = Array.isArray(question.markScheme) ? question.markScheme as MarkSchemeItem[] : [];\n const validMarkSchemeIds = new Set(markSchemeItems.map(item => item.id));\n\n const aiMarkschemeState = (apiResponse as {\n markschemeState: { id: string; correct: boolean }[];\n }).markschemeState;\n\n const allIdsValid = aiMarkschemeState.length > 0 &&\n aiMarkschemeState.every(entry => validMarkSchemeIds.has(entry.id));\n\n let resolvedState: Record<string, boolean | unknown[]> = {};\n\n if (allIdsValid) {\n resolvedState = aiMarkschemeState.reduce((acc, curr) => {\n acc[\"item-\" + curr.id] = curr.correct;\n return acc;\n }, {} as Record<string, boolean>);\n } else if (markSchemeItems.length > 0) {\n logger.warn('AI returned mismatched markscheme IDs, falling back to index mapping', {\n expected: markSchemeItems.map(i => i.id),\n received: aiMarkschemeState.map(i => i.id),\n });\n for (let i = 0; i < markSchemeItems.length; i++) {\n const aiEntry = aiMarkschemeState[i];\n resolvedState[\"item-\" + markSchemeItems[i].id] = aiEntry?.correct ?? false;\n }\n }\n\n const annotations = (apiResponse as {\n annotations: { quote: string; markSchemeId: string; criterion: string; met: boolean }[];\n }).annotations\n .filter(a => a.quote.length === 0 || responseText.includes(a.quote));\n\n // Store annotations separately so the boolean-only markschemeState\n // isn't broken by non-boolean values (the frontend parser rejects\n // the entire object if any value isn't a boolean).\n resolvedState['_annotations'] = annotations;\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: clampedPoints,\n markschemeState: resolvedState,\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 (\n worksheetResponseId: string,\n actingUserId?: string | null,\n) => {\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 try {\n await gradeWorksheetQuestion(worksheetResponseId, response.id, actingUserId);\n } catch (questionError) {\n logger.error('Failed to grade individual question, continuing with remaining questions', {\n questionId: response.questionId,\n error: questionError,\n });\n }\n\n };\n\n await syncWorksheetModuleProgress(\n worksheetResponse.studentId,\n worksheetResponse.worksheetId,\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 regradeAllPipeline = async (\n worksheetResponseId: string,\n actingUserId?: string | null,\n) => {\n logger.info('Regrading all questions for worksheet response', { worksheetResponseId });\n const worksheetResponse = await prisma.studentWorksheetResponse.findUnique({\n where: { id: worksheetResponseId },\n include: {\n worksheet: true,\n responses: {\n where: {\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 // Reset all non-cancelled responses to PENDING\n for (const response of worksheetResponse.responses) {\n await prisma.studentQuestionProgress.updateMany({\n where: {\n id: response.id,\n status: { not: JobStatus.CANCELLED },\n },\n data: { status: JobStatus.PENDING },\n });\n\n pusher.trigger(worksheetChannel(worksheetResponse.id), `set-pending`, {\n id: response.id,\n });\n }\n\n // Re-grade each question sequentially\n for (const response of worksheetResponse.responses) {\n try {\n await gradeWorksheetQuestion(worksheetResponseId, response.id, actingUserId);\n } catch (questionError) {\n logger.error('Failed to regrade question, continuing with remaining', {\n questionId: response.questionId,\n error: questionError,\n });\n }\n }\n};\n\nexport const regradeWorksheetPipeline = async (\n worksheetResponseId: string,\n worksheetQuestionProgressId: string,\n actingUserId?: string | null,\n) => {\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, actingUserId);\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":"c9757a56-c224-55bb-9755-7290f2990294"}
|