@studious-lms/server 1.21.0 → 1.22.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/dist/models/agenda.d.ts +6 -0
- package/dist/models/agenda.d.ts.map +1 -1
- package/dist/models/announcement.d.ts +2 -0
- package/dist/models/announcement.d.ts.map +1 -1
- package/dist/models/assignment.d.ts +27 -0
- package/dist/models/assignment.d.ts.map +1 -1
- package/dist/models/auth.d.ts +4 -0
- package/dist/models/auth.d.ts.map +1 -1
- package/dist/models/class.d.ts +14 -0
- package/dist/models/class.d.ts.map +1 -1
- package/dist/models/event.d.ts +11 -0
- package/dist/models/event.d.ts.map +1 -1
- package/dist/models/file.d.ts +19 -0
- package/dist/models/file.d.ts.map +1 -1
- package/dist/models/folder.d.ts +4 -0
- package/dist/models/folder.d.ts.map +1 -1
- package/dist/models/gradebook.d.ts +180 -0
- package/dist/models/gradebook.d.ts.map +1 -0
- package/dist/models/gradebook.js +88 -0
- package/dist/models/gradebook.js.map +1 -0
- package/dist/models/labChat.d.ts +4 -0
- package/dist/models/labChat.d.ts.map +1 -1
- package/dist/models/newtonChat.d.ts +2 -0
- package/dist/models/newtonChat.d.ts.map +1 -1
- package/dist/models/school.d.ts +2 -0
- package/dist/models/school.d.ts.map +1 -1
- package/dist/models/section.d.ts +3 -0
- package/dist/models/section.d.ts.map +1 -1
- package/dist/models/worksheet.d.ts +6 -0
- package/dist/models/worksheet.d.ts.map +1 -1
- package/dist/routers/_app.d.ts +1241 -67
- 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/admin.d.ts +164 -0
- package/dist/routers/admin.d.ts.map +1 -1
- package/dist/routers/admin.js +23 -4
- package/dist/routers/admin.js.map +1 -1
- package/dist/routers/agenda.d.ts +6 -0
- package/dist/routers/agenda.d.ts.map +1 -1
- package/dist/routers/assignment.d.ts +30 -3
- package/dist/routers/assignment.d.ts.map +1 -1
- package/dist/routers/class.d.ts +17 -0
- package/dist/routers/class.d.ts.map +1 -1
- package/dist/routers/event.d.ts +4 -0
- package/dist/routers/event.d.ts.map +1 -1
- package/dist/routers/file.d.ts +66 -0
- package/dist/routers/file.d.ts.map +1 -1
- package/dist/routers/file.js +111 -2
- package/dist/routers/file.js.map +1 -1
- package/dist/routers/gradebook.d.ts +299 -0
- package/dist/routers/gradebook.d.ts.map +1 -0
- package/dist/routers/gradebook.js +107 -0
- package/dist/routers/gradebook.js.map +1 -0
- package/dist/routers/school.d.ts +4 -0
- package/dist/routers/school.d.ts.map +1 -1
- package/dist/routers/section.d.ts +1 -1
- package/dist/routers/worksheet.d.ts +3 -1
- package/dist/routers/worksheet.d.ts.map +1 -1
- package/dist/services/adminInstitution.d.ts +51 -0
- package/dist/services/adminInstitution.d.ts.map +1 -1
- package/dist/services/adminInstitution.js +52 -2
- package/dist/services/adminInstitution.js.map +1 -1
- package/dist/services/adminMetrics.d.ts +17 -0
- package/dist/services/adminMetrics.d.ts.map +1 -0
- package/dist/services/adminMetrics.js +37 -0
- package/dist/services/adminMetrics.js.map +1 -0
- package/dist/services/adminRunJob.d.ts +9 -0
- package/dist/services/adminRunJob.d.ts.map +1 -1
- package/dist/services/adminRunJob.js +18 -2
- package/dist/services/adminRunJob.js.map +1 -1
- package/dist/services/adminUser.d.ts +67 -0
- package/dist/services/adminUser.d.ts.map +1 -0
- package/dist/services/adminUser.js +87 -0
- package/dist/services/adminUser.js.map +1 -0
- package/dist/services/agenda.d.ts +6 -0
- package/dist/services/agenda.d.ts.map +1 -1
- package/dist/services/assignment.d.ts +27 -0
- package/dist/services/assignment.d.ts.map +1 -1
- package/dist/services/class.d.ts +17 -0
- package/dist/services/class.d.ts.map +1 -1
- package/dist/services/event.d.ts +4 -0
- package/dist/services/event.d.ts.map +1 -1
- package/dist/services/gradeCalculation.d.ts +70 -0
- package/dist/services/gradeCalculation.d.ts.map +1 -0
- package/dist/services/gradeCalculation.js +148 -0
- package/dist/services/gradeCalculation.js.map +1 -0
- package/dist/services/gradebook.d.ts +197 -0
- package/dist/services/gradebook.d.ts.map +1 -0
- package/dist/services/gradebook.js +263 -0
- package/dist/services/gradebook.js.map +1 -0
- package/dist/services/school.d.ts +4 -0
- package/dist/services/school.d.ts.map +1 -1
- package/dist/services/worksheet.d.ts +2 -0
- package/dist/services/worksheet.d.ts.map +1 -1
- package/package.json +1 -1
- package/prisma/migrations/20260708011629_add_gradebook_categories_columns_policies/migration.sql +78 -0
- package/prisma/migrations/20260723142314_make_file_annotation_submission_optional/migration.sql +2 -0
- package/prisma/schema.prisma +62 -2
- package/src/models/gradebook.ts +91 -0
- package/src/routers/_app.ts +2 -0
- package/src/routers/admin.ts +28 -0
- package/src/routers/file.ts +128 -0
- package/src/routers/gradebook.ts +173 -0
- package/src/services/adminInstitution.ts +52 -0
- package/src/services/adminMetrics.ts +46 -0
- package/src/services/adminRunJob.ts +24 -0
- package/src/services/adminUser.ts +93 -0
- package/src/services/gradeCalculation.ts +253 -0
- package/src/services/gradebook.ts +383 -0
- package/tests/unit/services/gradeCalculation.test.ts +206 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gradebook.js","sources":["services/gradebook.ts"],"sourceRoot":"/","sourcesContent":["/**\n * Gradebook service – grade categories, custom columns/entries, class\n * grade-calculation policies, the computed gradebook, and CSV export.\n *\n * All grade math lives in `gradeCalculation.ts` (pure, unit-tested); this\n * service only assembles inputs and persists configuration.\n */\nimport { GradeRoundingMode as PrismaGradeRoundingMode } from \"@prisma/client\";\nimport { TRPCError } from \"@trpc/server\";\nimport { prisma } from \"../lib/prisma.js\";\nimport { isTeacherInClass } from \"../models/class.js\";\nimport {\n findGradeCategoriesByClassId,\n findGradeCategory,\n findCustomGradeColumnsByClassId,\n findCustomGradeColumn,\n findClassForGradebook,\n findClassGradePolicies,\n} from \"../models/gradebook.js\";\nimport {\n computeFinalGrade,\n resolveLetterGrade,\n type FinalGradeResult,\n type GradeCategoryInput,\n type ScoreInput,\n} from \"./gradeCalculation.js\";\n\n// ===== Categories =====\n\nexport async function listGradeCategories(classId: string) {\n return findGradeCategoriesByClassId(classId);\n}\n\nexport async function createGradeCategory(\n classId: string,\n data: { name: string; weight: number; dropLowest?: number; position?: number }\n) {\n return prisma.gradeCategory.create({\n data: {\n classId,\n name: data.name,\n weight: data.weight,\n dropLowest: data.dropLowest ?? 0,\n position: data.position ?? (await findGradeCategoriesByClassId(classId)).length,\n },\n });\n}\n\nexport async function updateGradeCategory(\n categoryId: string,\n classId: string,\n data: {\n name?: string;\n weight?: number;\n dropLowest?: number;\n position?: number;\n }\n) {\n const category = await findGradeCategory(categoryId, classId);\n if (!category) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Category not found\" });\n }\n return prisma.gradeCategory.update({\n where: { id: categoryId },\n data,\n });\n}\n\nexport async function deleteGradeCategory(categoryId: string, classId: string) {\n const category = await findGradeCategory(categoryId, classId);\n if (!category) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Category not found\" });\n }\n return prisma.gradeCategory.delete({ where: { id: categoryId } });\n}\n\nexport async function assignAssignmentCategory(\n classId: string,\n assignmentId: string,\n gradeCategoryId: string | null\n) {\n if (gradeCategoryId) {\n const category = await findGradeCategory(gradeCategoryId, classId);\n if (!category) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Category not found\" });\n }\n }\n return prisma.assignment.update({\n where: { id: assignmentId, classId },\n data: { gradeCategoryId },\n });\n}\n\n// ===== Custom columns =====\n\nexport async function listCustomGradeColumns(classId: string) {\n return findCustomGradeColumnsByClassId(classId);\n}\n\nexport async function createCustomGradeColumn(\n classId: string,\n data: {\n name: string;\n maxPoints: number;\n gradeCategoryId?: string | null;\n position?: number;\n }\n) {\n if (data.gradeCategoryId) {\n const category = await findGradeCategory(data.gradeCategoryId, classId);\n if (!category) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Category not found\" });\n }\n }\n return prisma.customGradeColumn.create({\n data: {\n classId,\n name: data.name,\n maxPoints: data.maxPoints,\n gradeCategoryId: data.gradeCategoryId ?? null,\n position:\n data.position ?? (await findCustomGradeColumnsByClassId(classId)).length,\n },\n });\n}\n\nexport async function updateCustomGradeColumn(\n columnId: string,\n classId: string,\n data: {\n name?: string;\n maxPoints?: number;\n gradeCategoryId?: string | null;\n position?: number;\n }\n) {\n const column = await findCustomGradeColumn(columnId, classId);\n if (!column) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Column not found\" });\n }\n if (data.gradeCategoryId) {\n const category = await findGradeCategory(data.gradeCategoryId, classId);\n if (!category) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Category not found\" });\n }\n }\n return prisma.customGradeColumn.update({\n where: { id: columnId },\n data,\n });\n}\n\nexport async function deleteCustomGradeColumn(\n columnId: string,\n classId: string\n) {\n const column = await findCustomGradeColumn(columnId, classId);\n if (!column) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Column not found\" });\n }\n return prisma.customGradeColumn.delete({ where: { id: columnId } });\n}\n\nexport async function setCustomGradeEntry(\n classId: string,\n columnId: string,\n studentId: string,\n points: number | null,\n note?: string | null\n) {\n const column = await findCustomGradeColumn(columnId, classId);\n if (!column) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Column not found\" });\n }\n if (points === null) {\n await prisma.customGradeEntry.deleteMany({\n where: { columnId, studentId },\n });\n return null;\n }\n return prisma.customGradeEntry.upsert({\n where: { columnId_studentId: { columnId, studentId } },\n create: { columnId, studentId, points, note: note ?? null },\n update: { points, ...(note !== undefined ? { note } : {}) },\n });\n}\n\n// ===== Policies =====\n\nexport async function getGradePolicies(classId: string) {\n const policies = await findClassGradePolicies(classId);\n if (!policies) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Class not found\" });\n }\n return policies;\n}\n\nexport async function updateGradePolicies(\n classId: string,\n data: {\n treatMissingAsZero?: boolean;\n gradeRoundingMode?: PrismaGradeRoundingMode;\n }\n) {\n return prisma.class.update({\n where: { id: classId },\n data,\n select: {\n id: true,\n treatMissingAsZero: true,\n gradeRoundingMode: true,\n },\n });\n}\n\n// ===== Computed gradebook =====\n\nexport interface GradebookStudentRow {\n studentId: string;\n studentName: string;\n assignmentScores: Record<string, number | null>;\n customScores: Record<string, { points: number | null; note: string | null }>;\n result: FinalGradeResult;\n letterGrade: string | null;\n}\n\nexport async function getComputedGradebook(classId: string, userId: string) {\n const [classData, categories, columns, isTeacher] = await Promise.all([\n findClassForGradebook(classId),\n findGradeCategoriesByClassId(classId),\n findCustomGradeColumnsByClassId(classId),\n isTeacherInClass(classId, userId),\n ]);\n if (!classData) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Class not found\" });\n }\n\n // Students only ever see their own row.\n const students = isTeacher\n ? classData.students\n : classData.students.filter((student) => student.id === userId);\n\n const policies = {\n treatMissingAsZero: classData.treatMissingAsZero,\n roundingMode: classData.gradeRoundingMode,\n };\n const categoryInputs: GradeCategoryInput[] = categories.map((category) => ({\n id: category.id,\n name: category.name,\n weight: category.weight,\n dropLowest: category.dropLowest,\n }));\n const boundary = classData.gradingBoundaries[0] ?? null;\n\n const rows: GradebookStudentRow[] = students.map((student) => {\n const assignmentScores: Record<string, number | null> = {};\n const scores: ScoreInput[] = [];\n\n for (const assignment of classData.assignments) {\n const submission = assignment.submissions.find(\n (item) => item.studentId === student.id\n );\n const pointsEarned = submission?.gradeReceived ?? null;\n assignmentScores[assignment.id] = pointsEarned;\n scores.push({\n categoryId: assignment.gradeCategoryId,\n pointsEarned,\n maxPoints: assignment.maxGrade ?? 0,\n weight: assignment.weight,\n });\n }\n\n const customScores: GradebookStudentRow[\"customScores\"] = {};\n for (const column of columns) {\n const entry = column.entries.find(\n (item) => item.studentId === student.id\n );\n customScores[column.id] = {\n points: entry?.points ?? null,\n note: entry?.note ?? null,\n };\n scores.push({\n categoryId: column.gradeCategoryId,\n pointsEarned: entry?.points ?? null,\n maxPoints: column.maxPoints,\n });\n }\n\n const result = computeFinalGrade(policies, categoryInputs, scores);\n const letterGrade =\n boundary && result.percentage !== null\n ? resolveLetterGrade(boundary.structured, result.percentage)\n : null;\n\n return {\n studentId: student.id,\n studentName: student.profile?.displayName || student.username,\n assignmentScores,\n customScores,\n result,\n letterGrade,\n };\n });\n\n return {\n policies,\n categories,\n assignments: classData.assignments.map((assignment) => ({\n id: assignment.id,\n title: assignment.title,\n maxGrade: assignment.maxGrade,\n weight: assignment.weight,\n gradeCategoryId: assignment.gradeCategoryId,\n dueDate: assignment.dueDate,\n })),\n customColumns: columns.map((column) => ({\n id: column.id,\n name: column.name,\n maxPoints: column.maxPoints,\n gradeCategoryId: column.gradeCategoryId,\n position: column.position,\n })),\n gradingBoundary: boundary,\n students: rows,\n };\n}\n\n// ===== CSV export =====\n\nfunction escapeCsvCell(value: string): string {\n if (/[\",\\n\\r]/.test(value)) {\n return `\"${value.replace(/\"/g, '\"\"')}\"`;\n }\n return value;\n}\n\nexport async function exportGradebookCsv(classId: string, userId: string) {\n const gradebook = await getComputedGradebook(classId, userId);\n\n const header = [\n \"Student\",\n ...gradebook.assignments.map(\n (assignment) => `${assignment.title} (/${assignment.maxGrade ?? 0})`\n ),\n ...gradebook.customColumns.map(\n (column) => `${column.name} (/${column.maxPoints})`\n ),\n ...gradebook.categories.map((category) => `${category.name} Avg (%)`),\n \"Final (%)\",\n \"Letter\",\n ];\n\n const lines = [header.map(escapeCsvCell).join(\",\")];\n\n for (const row of gradebook.students) {\n const categoryCells = gradebook.categories.map((category) => {\n const average = row.result.categories.find(\n (item) => item.categoryId === category.id\n )?.average;\n return average === null || average === undefined\n ? \"\"\n : average.toFixed(2);\n });\n\n const cells = [\n row.studentName,\n ...gradebook.assignments.map((assignment) => {\n const points = row.assignmentScores[assignment.id];\n return points === null || points === undefined ? \"\" : String(points);\n }),\n ...gradebook.customColumns.map((column) => {\n const points = row.customScores[column.id]?.points;\n return points === null || points === undefined ? \"\" : String(points);\n }),\n ...categoryCells,\n row.result.percentage === null ? \"\" : row.result.percentage.toFixed(2),\n row.letterGrade ?? \"\",\n ];\n lines.push(cells.map(escapeCsvCell).join(\",\"));\n }\n\n return { csv: lines.join(\"\\n\") };\n}\n"],"names":[],"mappings":"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","debug_id":"2860d786-c6b1-5d9a-b1bd-880c5bef0f51"}
|
|
@@ -144,6 +144,8 @@ export declare function listClasses(userId: string, schoolId: string): Promise<{
|
|
|
144
144
|
discussionsEnabled: boolean;
|
|
145
145
|
studentsCanCreateDiscussions: boolean;
|
|
146
146
|
studentsCanReply: boolean;
|
|
147
|
+
treatMissingAsZero: boolean;
|
|
148
|
+
gradeRoundingMode: import(".prisma/client").$Enums.GradeRoundingMode;
|
|
147
149
|
})[];
|
|
148
150
|
}>;
|
|
149
151
|
export declare function createSchoolClass(userId: string, input: {
|
|
@@ -191,6 +193,8 @@ export declare function createSchoolClass(userId: string, input: {
|
|
|
191
193
|
discussionsEnabled: boolean;
|
|
192
194
|
studentsCanCreateDiscussions: boolean;
|
|
193
195
|
studentsCanReply: boolean;
|
|
196
|
+
treatMissingAsZero: boolean;
|
|
197
|
+
gradeRoundingMode: import(".prisma/client").$Enums.GradeRoundingMode;
|
|
194
198
|
};
|
|
195
199
|
}>;
|
|
196
200
|
export declare function listMembers(userId: string, schoolId: string): Promise<{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"school.d.ts","sourceRoot":"/","sources":["services/school.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,oBAAoB,EAAe,MAAM,gBAAgB,CAAC;AA4DnE,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyBvE;AAED,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IACL,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;;;;;;;;;;;;;;;;;;;;GAcF;AAED,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IACL,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;;;;;;;;GAUF;AAED,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BtE;AAED,wBAAsB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM
|
|
1
|
+
{"version":3,"file":"school.d.ts","sourceRoot":"/","sources":["services/school.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,oBAAoB,EAAe,MAAM,gBAAgB,CAAC;AA4DnE,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyBvE;AAED,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IACL,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;;;;;;;;;;;;;;;;;;;;GAcF;AAED,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IACL,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;;;;;;;;GAUF;AAED,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BtE;AAED,wBAAsB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAGjE;AAED,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IACL,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCF;AAED,wBAAsB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;;;;;;;;;;;;;;;;;;GAGjE;AAED,wBAAsB,SAAS,CAC7B,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,oBAAoB,CAAA;CAAE;;;;;;;;;;GAS9E;AAED,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,oBAAoB,CAAA;CAAE;;;;;;;;;;GAwB9E;AAED,wBAAsB,YAAY,CAChC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE;;GAoBlD;AAED,wBAAsB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;;;;;;;;;;;;;;;;;;GAGjE;AAED;;;;GAIG;AACH,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,4DAchE;AAED,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,OAAO;;GAKvB;AAED;;;GAGG;AACH,wBAAsB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,qBAOnE;AAED,4EAA4E;AAC5E,wBAAsB,aAAa,CACjC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,KAAK,SAAK,EACV,IAAI,CAAC,EAAE,OAAO,GAAG,SAAS,GAAG,SAAS;;;;;;;;;;;KA2CvC;AAED;;;;GAIG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IACL,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,EAAE,CAAC,EAAE,IAAI,CAAC;CACX;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyIF;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IACL,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,EAAE,CAAC,EAAE,IAAI,CAAC;CACX;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UAoM0B,SAAS,GAAG,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAY/C;AAED,wBAAsB,YAAY,CAChC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IACL,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,oBAAoB,CAAC;IAC3B;sEACkE;IAClE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uEAAuE;IACvE,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;;;;;;;;;;;;;;;;;;;;;;;;;GAkGF;AAED,wBAAsB,YAAY,CAChC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE;;;;;;;;;;;;;;;GAI9C;AAED,wBAAsB,SAAS,CAAC,KAAK,EAAE,MAAM;;;;;;;;;;;GAiB5C;AAED,wBAAsB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;;;;;;;;;;GAsB/D"}
|
|
@@ -14,6 +14,8 @@ export declare function getWorksheet(worksheetId: string, userId: string): Promi
|
|
|
14
14
|
discussionsEnabled: boolean;
|
|
15
15
|
studentsCanCreateDiscussions: boolean;
|
|
16
16
|
studentsCanReply: boolean;
|
|
17
|
+
treatMissingAsZero: boolean;
|
|
18
|
+
gradeRoundingMode: import(".prisma/client").$Enums.GradeRoundingMode;
|
|
17
19
|
} | null;
|
|
18
20
|
tags: {
|
|
19
21
|
id: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worksheet.d.ts","sourceRoot":"/","sources":["services/worksheet.ts"],"names":[],"mappings":"AAKA,OAAO,EAAa,MAAM,EAAE,KAAK,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AA8D/E,qDAAqD;AACrD,wBAAsB,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM
|
|
1
|
+
{"version":3,"file":"worksheet.d.ts","sourceRoot":"/","sources":["services/worksheet.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/prisma/migrations/20260708011629_add_gradebook_categories_columns_policies/migration.sql
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
-- CreateEnum
|
|
2
|
+
CREATE TYPE "GradeRoundingMode" AS ENUM ('NONE', 'HALF_UP', 'FLOOR', 'CEILING');
|
|
3
|
+
|
|
4
|
+
-- AlterTable
|
|
5
|
+
ALTER TABLE "Assignment" ADD COLUMN "gradeCategoryId" TEXT;
|
|
6
|
+
|
|
7
|
+
-- AlterTable
|
|
8
|
+
ALTER TABLE "Class" ADD COLUMN "gradeRoundingMode" "GradeRoundingMode" NOT NULL DEFAULT 'NONE',
|
|
9
|
+
ADD COLUMN "treatMissingAsZero" BOOLEAN NOT NULL DEFAULT false;
|
|
10
|
+
|
|
11
|
+
-- CreateTable
|
|
12
|
+
CREATE TABLE "GradeCategory" (
|
|
13
|
+
"id" TEXT NOT NULL,
|
|
14
|
+
"classId" TEXT NOT NULL,
|
|
15
|
+
"name" TEXT NOT NULL,
|
|
16
|
+
"weight" DOUBLE PRECISION NOT NULL,
|
|
17
|
+
"dropLowest" INTEGER NOT NULL DEFAULT 0,
|
|
18
|
+
"position" INTEGER NOT NULL DEFAULT 0,
|
|
19
|
+
|
|
20
|
+
CONSTRAINT "GradeCategory_pkey" PRIMARY KEY ("id")
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
-- CreateTable
|
|
24
|
+
CREATE TABLE "CustomGradeColumn" (
|
|
25
|
+
"id" TEXT NOT NULL,
|
|
26
|
+
"classId" TEXT NOT NULL,
|
|
27
|
+
"gradeCategoryId" TEXT,
|
|
28
|
+
"name" TEXT NOT NULL,
|
|
29
|
+
"maxPoints" DOUBLE PRECISION NOT NULL,
|
|
30
|
+
"position" INTEGER NOT NULL DEFAULT 0,
|
|
31
|
+
|
|
32
|
+
CONSTRAINT "CustomGradeColumn_pkey" PRIMARY KEY ("id")
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
-- CreateTable
|
|
36
|
+
CREATE TABLE "CustomGradeEntry" (
|
|
37
|
+
"id" TEXT NOT NULL,
|
|
38
|
+
"columnId" TEXT NOT NULL,
|
|
39
|
+
"studentId" TEXT NOT NULL,
|
|
40
|
+
"points" DOUBLE PRECISION NOT NULL,
|
|
41
|
+
"note" TEXT,
|
|
42
|
+
|
|
43
|
+
CONSTRAINT "CustomGradeEntry_pkey" PRIMARY KEY ("id")
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
-- CreateIndex
|
|
47
|
+
CREATE INDEX "GradeCategory_classId_idx" ON "GradeCategory"("classId");
|
|
48
|
+
|
|
49
|
+
-- CreateIndex
|
|
50
|
+
CREATE INDEX "CustomGradeColumn_classId_idx" ON "CustomGradeColumn"("classId");
|
|
51
|
+
|
|
52
|
+
-- CreateIndex
|
|
53
|
+
CREATE INDEX "CustomGradeColumn_gradeCategoryId_idx" ON "CustomGradeColumn"("gradeCategoryId");
|
|
54
|
+
|
|
55
|
+
-- CreateIndex
|
|
56
|
+
CREATE INDEX "CustomGradeEntry_studentId_idx" ON "CustomGradeEntry"("studentId");
|
|
57
|
+
|
|
58
|
+
-- CreateIndex
|
|
59
|
+
CREATE UNIQUE INDEX "CustomGradeEntry_columnId_studentId_key" ON "CustomGradeEntry"("columnId", "studentId");
|
|
60
|
+
|
|
61
|
+
-- AddForeignKey
|
|
62
|
+
ALTER TABLE "GradeCategory" ADD CONSTRAINT "GradeCategory_classId_fkey" FOREIGN KEY ("classId") REFERENCES "Class"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
63
|
+
|
|
64
|
+
-- AddForeignKey
|
|
65
|
+
ALTER TABLE "CustomGradeColumn" ADD CONSTRAINT "CustomGradeColumn_classId_fkey" FOREIGN KEY ("classId") REFERENCES "Class"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
66
|
+
|
|
67
|
+
-- AddForeignKey
|
|
68
|
+
ALTER TABLE "CustomGradeColumn" ADD CONSTRAINT "CustomGradeColumn_gradeCategoryId_fkey" FOREIGN KEY ("gradeCategoryId") REFERENCES "GradeCategory"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
|
69
|
+
|
|
70
|
+
-- AddForeignKey
|
|
71
|
+
ALTER TABLE "CustomGradeEntry" ADD CONSTRAINT "CustomGradeEntry_columnId_fkey" FOREIGN KEY ("columnId") REFERENCES "CustomGradeColumn"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
72
|
+
|
|
73
|
+
-- AddForeignKey
|
|
74
|
+
ALTER TABLE "CustomGradeEntry" ADD CONSTRAINT "CustomGradeEntry_studentId_fkey" FOREIGN KEY ("studentId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
75
|
+
|
|
76
|
+
-- AddForeignKey
|
|
77
|
+
ALTER TABLE "Assignment" ADD CONSTRAINT "Assignment_gradeCategoryId_fkey" FOREIGN KEY ("gradeCategoryId") REFERENCES "GradeCategory"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
|
78
|
+
|
package/prisma/schema.prisma
CHANGED
|
@@ -241,6 +241,7 @@ model User {
|
|
|
241
241
|
discussions Discussion[]
|
|
242
242
|
discussionReplies DiscussionReply[]
|
|
243
243
|
discussionReads DiscussionRead[]
|
|
244
|
+
customGradeEntries CustomGradeEntry[] @relation("UserCustomGradeEntries")
|
|
244
245
|
fileAnnotations FileAnnotation[] @relation("AuthoredFileAnnotations")
|
|
245
246
|
}
|
|
246
247
|
|
|
@@ -335,10 +336,67 @@ model Class {
|
|
|
335
336
|
studentsCanCreateDiscussions Boolean @default(true)
|
|
336
337
|
studentsCanReply Boolean @default(true)
|
|
337
338
|
|
|
339
|
+
gradeCategories GradeCategory[]
|
|
340
|
+
customGradeColumns CustomGradeColumn[]
|
|
341
|
+
|
|
342
|
+
treatMissingAsZero Boolean @default(false)
|
|
343
|
+
gradeRoundingMode GradeRoundingMode @default(NONE)
|
|
344
|
+
|
|
338
345
|
@@index([schoolId])
|
|
339
346
|
@@index([departmentId])
|
|
340
347
|
}
|
|
341
348
|
|
|
349
|
+
enum GradeRoundingMode {
|
|
350
|
+
NONE
|
|
351
|
+
HALF_UP
|
|
352
|
+
FLOOR
|
|
353
|
+
CEILING
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
model GradeCategory {
|
|
357
|
+
id String @id @default(uuid())
|
|
358
|
+
classId String
|
|
359
|
+
class Class @relation(fields: [classId], references: [id], onDelete: Cascade)
|
|
360
|
+
name String
|
|
361
|
+
weight Float
|
|
362
|
+
dropLowest Int @default(0)
|
|
363
|
+
position Int @default(0)
|
|
364
|
+
|
|
365
|
+
assignments Assignment[]
|
|
366
|
+
customColumns CustomGradeColumn[]
|
|
367
|
+
|
|
368
|
+
@@index([classId])
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
model CustomGradeColumn {
|
|
372
|
+
id String @id @default(uuid())
|
|
373
|
+
classId String
|
|
374
|
+
class Class @relation(fields: [classId], references: [id], onDelete: Cascade)
|
|
375
|
+
gradeCategoryId String?
|
|
376
|
+
gradeCategory GradeCategory? @relation(fields: [gradeCategoryId], references: [id], onDelete: SetNull)
|
|
377
|
+
name String
|
|
378
|
+
maxPoints Float
|
|
379
|
+
position Int @default(0)
|
|
380
|
+
|
|
381
|
+
entries CustomGradeEntry[]
|
|
382
|
+
|
|
383
|
+
@@index([classId])
|
|
384
|
+
@@index([gradeCategoryId])
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
model CustomGradeEntry {
|
|
388
|
+
id String @id @default(uuid())
|
|
389
|
+
columnId String
|
|
390
|
+
column CustomGradeColumn @relation(fields: [columnId], references: [id], onDelete: Cascade)
|
|
391
|
+
studentId String
|
|
392
|
+
student User @relation("UserCustomGradeEntries", fields: [studentId], references: [id], onDelete: Cascade)
|
|
393
|
+
points Float
|
|
394
|
+
note String?
|
|
395
|
+
|
|
396
|
+
@@unique([columnId, studentId])
|
|
397
|
+
@@index([studentId])
|
|
398
|
+
}
|
|
399
|
+
|
|
342
400
|
model MarkScheme {
|
|
343
401
|
id String @id @default(uuid())
|
|
344
402
|
classId String?
|
|
@@ -470,8 +528,8 @@ model FileAnnotation {
|
|
|
470
528
|
id String @id @default(uuid())
|
|
471
529
|
file File @relation("FileAnnotations", fields: [fileId], references: [id], onDelete: Cascade)
|
|
472
530
|
fileId String
|
|
473
|
-
submission Submission
|
|
474
|
-
submissionId String
|
|
531
|
+
submission Submission? @relation("SubmissionFileAnnotations", fields: [submissionId], references: [id], onDelete: Cascade)
|
|
532
|
+
submissionId String?
|
|
475
533
|
author User @relation("AuthoredFileAnnotations", fields: [authorId], references: [id], onDelete: Cascade)
|
|
476
534
|
authorId String
|
|
477
535
|
page Int
|
|
@@ -523,6 +581,8 @@ model Assignment {
|
|
|
523
581
|
order Int?
|
|
524
582
|
gradingBoundary GradingBoundary? @relation(fields: [gradingBoundaryId], references: [id], onDelete: Cascade)
|
|
525
583
|
gradingBoundaryId String?
|
|
584
|
+
gradeCategory GradeCategory? @relation(fields: [gradeCategoryId], references: [id], onDelete: SetNull)
|
|
585
|
+
gradeCategoryId String?
|
|
526
586
|
worksheets Worksheet[] @relation("AssignmentWorksheets")
|
|
527
587
|
targetedBySubmissions Submission[] @relation("SubmissionTargetedAssignment")
|
|
528
588
|
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gradebook model – grade categories, custom gradebook columns/entries,
|
|
3
|
+
* and the queries backing the computed gradebook.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { prisma } from "../lib/prisma.js";
|
|
7
|
+
|
|
8
|
+
/** @returns Grade categories for a class, ordered by position. */
|
|
9
|
+
export function findGradeCategoriesByClassId(classId: string) {
|
|
10
|
+
return prisma.gradeCategory.findMany({
|
|
11
|
+
where: { classId },
|
|
12
|
+
orderBy: { position: "asc" },
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** @returns A grade category scoped to a class. */
|
|
17
|
+
export function findGradeCategory(categoryId: string, classId: string) {
|
|
18
|
+
return prisma.gradeCategory.findFirst({
|
|
19
|
+
where: { id: categoryId, classId },
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** @returns Custom gradebook columns for a class with their entries. */
|
|
24
|
+
export function findCustomGradeColumnsByClassId(classId: string) {
|
|
25
|
+
return prisma.customGradeColumn.findMany({
|
|
26
|
+
where: { classId },
|
|
27
|
+
orderBy: { position: "asc" },
|
|
28
|
+
include: {
|
|
29
|
+
entries: {
|
|
30
|
+
select: { id: true, studentId: true, points: true, note: true },
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** @returns A custom gradebook column scoped to a class. */
|
|
37
|
+
export function findCustomGradeColumn(columnId: string, classId: string) {
|
|
38
|
+
return prisma.customGradeColumn.findFirst({
|
|
39
|
+
where: { id: columnId, classId },
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** @returns Class data needed to compute the gradebook. */
|
|
44
|
+
export function findClassForGradebook(classId: string) {
|
|
45
|
+
return prisma.class.findUnique({
|
|
46
|
+
where: { id: classId },
|
|
47
|
+
select: {
|
|
48
|
+
id: true,
|
|
49
|
+
treatMissingAsZero: true,
|
|
50
|
+
gradeRoundingMode: true,
|
|
51
|
+
students: {
|
|
52
|
+
select: {
|
|
53
|
+
id: true,
|
|
54
|
+
username: true,
|
|
55
|
+
profile: { select: { displayName: true } },
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
gradingBoundaries: {
|
|
59
|
+
take: 1,
|
|
60
|
+
select: { id: true, name: true, structured: true },
|
|
61
|
+
},
|
|
62
|
+
assignments: {
|
|
63
|
+
where: { graded: true, template: false, inProgress: false },
|
|
64
|
+
orderBy: [{ order: "asc" }, { dueDate: "asc" }],
|
|
65
|
+
select: {
|
|
66
|
+
id: true,
|
|
67
|
+
title: true,
|
|
68
|
+
maxGrade: true,
|
|
69
|
+
weight: true,
|
|
70
|
+
gradeCategoryId: true,
|
|
71
|
+
dueDate: true,
|
|
72
|
+
submissions: {
|
|
73
|
+
select: { studentId: true, gradeReceived: true },
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** @returns Grade policy fields for a class. */
|
|
82
|
+
export function findClassGradePolicies(classId: string) {
|
|
83
|
+
return prisma.class.findUnique({
|
|
84
|
+
where: { id: classId },
|
|
85
|
+
select: {
|
|
86
|
+
id: true,
|
|
87
|
+
treatMissingAsZero: true,
|
|
88
|
+
gradeRoundingMode: true,
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
}
|
package/src/routers/_app.ts
CHANGED
|
@@ -29,6 +29,7 @@ import { permissionsRouter } from "./permissions.js";
|
|
|
29
29
|
import { discussionRouter } from "./discussion.js";
|
|
30
30
|
import { adminRouter } from "./admin.js";
|
|
31
31
|
import { embeddingRouter } from "./embedding.js";
|
|
32
|
+
import { gradebookRouter } from "./gradebook.js";
|
|
32
33
|
|
|
33
34
|
export const appRouter = createTRPCRouter({
|
|
34
35
|
class: classRouter,
|
|
@@ -59,6 +60,7 @@ export const appRouter = createTRPCRouter({
|
|
|
59
60
|
discussion: discussionRouter,
|
|
60
61
|
admin: adminRouter,
|
|
61
62
|
embedding: embeddingRouter,
|
|
63
|
+
gradebook: gradebookRouter,
|
|
62
64
|
});
|
|
63
65
|
|
|
64
66
|
// Export type router type definition
|
package/src/routers/admin.ts
CHANGED
|
@@ -4,15 +4,21 @@ import { createTRPCRouter, adminProcedure } from '../trpc.js';
|
|
|
4
4
|
import {
|
|
5
5
|
getRunJobForAdmin,
|
|
6
6
|
listRunJobsForAdmin,
|
|
7
|
+
retryRunJobForAdmin,
|
|
7
8
|
} from '../services/adminRunJob.js';
|
|
8
9
|
import {
|
|
10
|
+
getSchoolForAdmin,
|
|
9
11
|
listDepartmentsForAdmin,
|
|
10
12
|
listEarlyAccessRequestsForAdmin,
|
|
11
13
|
listSchoolDevelopmentProgramsForAdmin,
|
|
12
14
|
listSchoolsForAdmin,
|
|
13
15
|
} from '../services/adminInstitution.js';
|
|
16
|
+
import { getOverviewMetricsForAdmin } from '../services/adminMetrics.js';
|
|
17
|
+
import { getUserForAdmin, searchUsersForAdmin } from '../services/adminUser.js';
|
|
14
18
|
|
|
15
19
|
export const adminRouter = createTRPCRouter({
|
|
20
|
+
getOverviewMetrics: adminProcedure.query(() => getOverviewMetricsForAdmin()),
|
|
21
|
+
|
|
16
22
|
listRunJobs: adminProcedure
|
|
17
23
|
.input(
|
|
18
24
|
z.object({
|
|
@@ -28,6 +34,14 @@ export const adminRouter = createTRPCRouter({
|
|
|
28
34
|
.input(z.object({ id: z.string().uuid() }))
|
|
29
35
|
.query(({ input }) => getRunJobForAdmin(input.id)),
|
|
30
36
|
|
|
37
|
+
retryRunJob: adminProcedure
|
|
38
|
+
.input(z.object({ id: z.string().uuid() }))
|
|
39
|
+
.mutation(({ input }) => retryRunJobForAdmin(input.id)),
|
|
40
|
+
|
|
41
|
+
getSchool: adminProcedure
|
|
42
|
+
.input(z.object({ id: z.string().uuid() }))
|
|
43
|
+
.query(({ input }) => getSchoolForAdmin(input.id)),
|
|
44
|
+
|
|
31
45
|
listSchools: adminProcedure
|
|
32
46
|
.input(
|
|
33
47
|
z.object({
|
|
@@ -67,4 +81,18 @@ export const adminRouter = createTRPCRouter({
|
|
|
67
81
|
}),
|
|
68
82
|
)
|
|
69
83
|
.query(({ input }) => listEarlyAccessRequestsForAdmin(input)),
|
|
84
|
+
|
|
85
|
+
searchUsers: adminProcedure
|
|
86
|
+
.input(
|
|
87
|
+
z.object({
|
|
88
|
+
page: z.number().int().min(0).default(0),
|
|
89
|
+
pageSize: z.number().int().min(1).max(100).default(25),
|
|
90
|
+
query: z.string().optional(),
|
|
91
|
+
}),
|
|
92
|
+
)
|
|
93
|
+
.query(({ input }) => searchUsersForAdmin(input)),
|
|
94
|
+
|
|
95
|
+
getUser: adminProcedure
|
|
96
|
+
.input(z.object({ id: z.string().uuid() }))
|
|
97
|
+
.query(({ input }) => getUserForAdmin(input.id)),
|
|
70
98
|
});
|
package/src/routers/file.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { TRPCError } from "@trpc/server";
|
|
2
3
|
import { createTRPCRouter, protectedProcedure } from "../trpc.js";
|
|
4
|
+
import { prisma } from "../lib/prisma.js";
|
|
3
5
|
import {
|
|
4
6
|
deleteFileGeneric,
|
|
5
7
|
getFileSignedUrl,
|
|
@@ -7,6 +9,31 @@ import {
|
|
|
7
9
|
renameFileScoped,
|
|
8
10
|
} from "../services/file.js";
|
|
9
11
|
|
|
12
|
+
const upsertAnnotationSchema = z.object({
|
|
13
|
+
fileId: z.string(),
|
|
14
|
+
id: z.string().optional(),
|
|
15
|
+
page: z.number().int().min(1),
|
|
16
|
+
type: z.enum(["HIGHLIGHT", "INK", "COMMENT", "TEXT"]),
|
|
17
|
+
geometry: z.record(z.string(), z.unknown()),
|
|
18
|
+
text: z.string().optional(),
|
|
19
|
+
color: z.string().optional(),
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
/** @throws NOT_FOUND unless the file exists and is owned by the user. */
|
|
23
|
+
async function requireOwnedFile(userId: string, fileId: string) {
|
|
24
|
+
const file = await prisma.file.findFirst({
|
|
25
|
+
where: { id: fileId, userId },
|
|
26
|
+
select: { id: true },
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
if (!file) {
|
|
30
|
+
throw new TRPCError({
|
|
31
|
+
code: "NOT_FOUND",
|
|
32
|
+
message: "File not found or you don't own it",
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
10
37
|
/**
|
|
11
38
|
* File router. Backed by the same unified scope-aware permission model as the
|
|
12
39
|
* folder router. The class files page and the cloud-storage page (personal /
|
|
@@ -55,4 +82,105 @@ export const fileRouter = createTRPCRouter({
|
|
|
55
82
|
.mutation(({ ctx, input }) =>
|
|
56
83
|
deleteFileGeneric(ctx.user!.id, input.fileId)
|
|
57
84
|
),
|
|
85
|
+
|
|
86
|
+
// PDF annotations on files the user owns (personal cloud storage). The
|
|
87
|
+
// submission review flow uses the annotation endpoints on the assignment
|
|
88
|
+
// router instead; these are owner-only and carry no submission context.
|
|
89
|
+
listAnnotations: protectedProcedure
|
|
90
|
+
.input(z.object({ fileId: z.string() }))
|
|
91
|
+
.query(async ({ ctx, input }) => {
|
|
92
|
+
await requireOwnedFile(ctx.user!.id, input.fileId);
|
|
93
|
+
|
|
94
|
+
const annotations = await prisma.fileAnnotation.findMany({
|
|
95
|
+
where: { fileId: input.fileId, submissionId: null },
|
|
96
|
+
orderBy: { createdAt: "asc" },
|
|
97
|
+
include: {
|
|
98
|
+
author: {
|
|
99
|
+
select: {
|
|
100
|
+
id: true,
|
|
101
|
+
username: true,
|
|
102
|
+
profile: { select: { displayName: true } },
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
return { annotations };
|
|
109
|
+
}),
|
|
110
|
+
|
|
111
|
+
upsertAnnotation: protectedProcedure
|
|
112
|
+
.input(upsertAnnotationSchema)
|
|
113
|
+
.mutation(async ({ ctx, input }) => {
|
|
114
|
+
const { fileId, id, page, type, geometry, text, color } = input;
|
|
115
|
+
|
|
116
|
+
await requireOwnedFile(ctx.user!.id, fileId);
|
|
117
|
+
|
|
118
|
+
if (id) {
|
|
119
|
+
const existing = await prisma.fileAnnotation.findFirst({
|
|
120
|
+
where: { id, fileId, submissionId: null, authorId: ctx.user!.id },
|
|
121
|
+
select: { id: true },
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
if (!existing) {
|
|
125
|
+
throw new TRPCError({
|
|
126
|
+
code: "NOT_FOUND",
|
|
127
|
+
message: "Annotation not found",
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const annotation = await prisma.fileAnnotation.update({
|
|
132
|
+
where: { id },
|
|
133
|
+
data: {
|
|
134
|
+
page,
|
|
135
|
+
type,
|
|
136
|
+
geometry: geometry as object,
|
|
137
|
+
text: text ?? null,
|
|
138
|
+
color: color ?? null,
|
|
139
|
+
},
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
return { annotation };
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const annotation = await prisma.fileAnnotation.create({
|
|
146
|
+
data: {
|
|
147
|
+
fileId,
|
|
148
|
+
authorId: ctx.user!.id,
|
|
149
|
+
page,
|
|
150
|
+
type,
|
|
151
|
+
geometry: geometry as object,
|
|
152
|
+
text: text ?? null,
|
|
153
|
+
color: color ?? null,
|
|
154
|
+
},
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
return { annotation };
|
|
158
|
+
}),
|
|
159
|
+
|
|
160
|
+
deleteAnnotation: protectedProcedure
|
|
161
|
+
.input(z.object({ fileId: z.string(), id: z.string() }))
|
|
162
|
+
.mutation(async ({ ctx, input }) => {
|
|
163
|
+
await requireOwnedFile(ctx.user!.id, input.fileId);
|
|
164
|
+
|
|
165
|
+
const annotation = await prisma.fileAnnotation.findFirst({
|
|
166
|
+
where: {
|
|
167
|
+
id: input.id,
|
|
168
|
+
fileId: input.fileId,
|
|
169
|
+
submissionId: null,
|
|
170
|
+
authorId: ctx.user!.id,
|
|
171
|
+
},
|
|
172
|
+
select: { id: true },
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
if (!annotation) {
|
|
176
|
+
throw new TRPCError({
|
|
177
|
+
code: "NOT_FOUND",
|
|
178
|
+
message: "Annotation not found",
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
await prisma.fileAnnotation.delete({ where: { id: input.id } });
|
|
183
|
+
|
|
184
|
+
return { success: true };
|
|
185
|
+
}),
|
|
58
186
|
});
|