@studious-lms/server 1.26.0 → 1.28.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/.env.example +3 -1
- package/.github/workflows/npm-publish.yml +1 -0
- package/AGENTS.md +84 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +31 -4
- package/dist/index.js.map +1 -1
- package/dist/instrument.js +9 -4
- package/dist/instrument.js.map +1 -1
- package/dist/lib/fileUpload.d.ts +15 -0
- package/dist/lib/fileUpload.d.ts.map +1 -1
- package/dist/lib/fileUpload.js +43 -2
- package/dist/lib/fileUpload.js.map +1 -1
- package/dist/models/cloudStorage.d.ts +11 -0
- package/dist/models/cloudStorage.d.ts.map +1 -1
- package/dist/models/cloudStorage.js +18 -2
- package/dist/models/cloudStorage.js.map +1 -1
- package/dist/models/conversation.d.ts +1 -0
- package/dist/models/conversation.d.ts.map +1 -1
- package/dist/models/labChat.d.ts +40 -0
- package/dist/models/labChat.d.ts.map +1 -1
- package/dist/models/labChat.js +32 -2
- package/dist/models/labChat.js.map +1 -1
- package/dist/models/message.d.ts +3 -0
- package/dist/models/message.d.ts.map +1 -1
- package/dist/pipelines/aiLabChat.d.ts +2 -0
- package/dist/pipelines/aiLabChat.d.ts.map +1 -1
- package/dist/pipelines/aiLabChat.js +60 -16
- package/dist/pipelines/aiLabChat.js.map +1 -1
- package/dist/pipelines/aiLabChatContract.d.ts +170 -2
- package/dist/pipelines/aiLabChatContract.d.ts.map +1 -1
- package/dist/pipelines/aiLabChatContract.js +30 -2
- package/dist/pipelines/aiLabChatContract.js.map +1 -1
- package/dist/pipelines/aiNewtonChat.d.ts.map +1 -1
- package/dist/pipelines/aiNewtonChat.js +30 -5
- package/dist/pipelines/aiNewtonChat.js.map +1 -1
- package/dist/pipelines/generateVisualization.d.ts +33 -0
- package/dist/pipelines/generateVisualization.d.ts.map +1 -0
- package/dist/pipelines/generateVisualization.js +79 -0
- package/dist/pipelines/generateVisualization.js.map +1 -0
- package/dist/pipelines/labChatPrompt.d.ts +1 -1
- package/dist/pipelines/labChatPrompt.d.ts.map +1 -1
- package/dist/pipelines/labChatPrompt.js +13 -14
- package/dist/pipelines/labChatPrompt.js.map +1 -1
- package/dist/routers/_app.d.ts +1118 -70
- 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 +297 -1
- package/dist/routers/admin.d.ts.map +1 -1
- package/dist/routers/admin.js +43 -2
- package/dist/routers/admin.js.map +1 -1
- package/dist/routers/class.d.ts.map +1 -1
- package/dist/routers/class.js +16 -3
- package/dist/routers/class.js.map +1 -1
- package/dist/routers/conversation.d.ts +1 -0
- package/dist/routers/conversation.d.ts.map +1 -1
- package/dist/routers/labChat.d.ts +10 -0
- package/dist/routers/labChat.d.ts.map +1 -1
- package/dist/routers/labChat.js +6 -3
- package/dist/routers/labChat.js.map +1 -1
- package/dist/routers/message.d.ts +37 -4
- package/dist/routers/message.d.ts.map +1 -1
- package/dist/routers/message.js +24 -7
- package/dist/routers/message.js.map +1 -1
- package/dist/routers/module.d.ts +1 -1
- package/dist/routers/parent.d.ts +18 -0
- package/dist/routers/parent.d.ts.map +1 -1
- package/dist/routers/parent.js +10 -3
- package/dist/routers/parent.js.map +1 -1
- package/dist/routers/studentProgress.d.ts +44 -0
- package/dist/routers/studentProgress.d.ts.map +1 -1
- package/dist/routers/studentProgress.js +12 -3
- package/dist/routers/studentProgress.js.map +1 -1
- package/dist/routers/visualization.d.ts +124 -0
- package/dist/routers/visualization.d.ts.map +1 -0
- package/dist/routers/visualization.js +41 -0
- package/dist/routers/visualization.js.map +1 -0
- package/dist/routes/aiStream.js +32 -2
- package/dist/routes/aiStream.js.map +1 -1
- package/dist/seedDatabase.d.ts.map +1 -1
- package/dist/seedDatabase.js +107 -3
- package/dist/seedDatabase.js.map +1 -1
- package/dist/services/adminClass.d.ts +153 -0
- package/dist/services/adminClass.d.ts.map +1 -0
- package/dist/services/adminClass.js +196 -0
- package/dist/services/adminClass.js.map +1 -0
- package/dist/services/adminInstitution.d.ts +2 -0
- package/dist/services/adminInstitution.d.ts.map +1 -1
- package/dist/services/adminInstitution.js +11 -4
- package/dist/services/adminInstitution.js.map +1 -1
- package/dist/services/adminMetrics.d.ts +8 -0
- package/dist/services/adminMetrics.d.ts.map +1 -1
- package/dist/services/adminMetrics.js +30 -3
- package/dist/services/adminMetrics.js.map +1 -1
- package/dist/services/adminSpam.d.ts +76 -0
- package/dist/services/adminSpam.d.ts.map +1 -0
- package/dist/services/adminSpam.js +163 -0
- package/dist/services/adminSpam.js.map +1 -0
- package/dist/services/adminSql.d.ts +36 -0
- package/dist/services/adminSql.d.ts.map +1 -0
- package/dist/services/adminSql.js +172 -0
- package/dist/services/adminSql.js.map +1 -0
- package/dist/services/adminTelemetry.d.ts +56 -0
- package/dist/services/adminTelemetry.d.ts.map +1 -0
- package/dist/services/adminTelemetry.js +147 -0
- package/dist/services/adminTelemetry.js.map +1 -0
- package/dist/services/adminUser.d.ts +45 -1
- package/dist/services/adminUser.d.ts.map +1 -1
- package/dist/services/adminUser.js +53 -13
- package/dist/services/adminUser.js.map +1 -1
- package/dist/services/aiChatStream.d.ts +15 -0
- package/dist/services/aiChatStream.d.ts.map +1 -1
- package/dist/services/aiChatStream.js +144 -74
- package/dist/services/aiChatStream.js.map +1 -1
- package/dist/services/aiGeneration.d.ts +4 -0
- package/dist/services/aiGeneration.d.ts.map +1 -0
- package/dist/services/aiGeneration.js +88 -0
- package/dist/services/aiGeneration.js.map +1 -0
- package/dist/services/aiProposalTools.d.ts +4 -1
- package/dist/services/aiProposalTools.d.ts.map +1 -1
- package/dist/services/aiProposalTools.js +128 -3
- package/dist/services/aiProposalTools.js.map +1 -1
- package/dist/services/aiProposals.d.ts +70 -0
- package/dist/services/aiProposals.d.ts.map +1 -0
- package/dist/services/aiProposals.js +363 -0
- package/dist/services/aiProposals.js.map +1 -0
- package/dist/services/auth.d.ts.map +1 -1
- package/dist/services/auth.js +10 -4
- package/dist/services/auth.js.map +1 -1
- package/dist/services/conversation.d.ts +1 -0
- package/dist/services/conversation.d.ts.map +1 -1
- package/dist/services/fees.d.ts.map +1 -1
- package/dist/services/fees.js +7 -3
- package/dist/services/fees.js.map +1 -1
- package/dist/services/labChat.d.ts +10 -0
- package/dist/services/labChat.d.ts.map +1 -1
- package/dist/services/labChat.js +52 -3
- package/dist/services/labChat.js.map +1 -1
- package/dist/services/message.d.ts +24 -8
- package/dist/services/message.d.ts.map +1 -1
- package/dist/services/message.js +170 -117
- package/dist/services/message.js.map +1 -1
- package/dist/services/module.d.ts.map +1 -1
- package/dist/services/module.js +23 -4
- package/dist/services/module.js.map +1 -1
- package/dist/services/parentPortal.d.ts +14 -0
- package/dist/services/parentPortal.d.ts.map +1 -1
- package/dist/services/parentPortal.js +72 -2
- package/dist/services/parentPortal.js.map +1 -1
- package/dist/services/studentProgress.d.ts +50 -0
- package/dist/services/studentProgress.d.ts.map +1 -1
- package/dist/services/studentProgress.js +107 -2
- package/dist/services/studentProgress.js.map +1 -1
- package/dist/services/visualization.d.ts +72 -0
- package/dist/services/visualization.d.ts.map +1 -0
- package/dist/services/visualization.js +122 -0
- package/dist/services/visualization.js.map +1 -0
- package/dist/utils/inference.d.ts +10 -0
- package/dist/utils/inference.d.ts.map +1 -1
- package/dist/utils/inference.js +77 -2
- package/dist/utils/inference.js.map +1 -1
- package/dist/utils/sentryTrpc.d.ts +3 -0
- package/dist/utils/sentryTrpc.d.ts.map +1 -0
- package/dist/utils/sentryTrpc.js +26 -0
- package/dist/utils/sentryTrpc.js.map +1 -0
- package/dist/workers/processors/createRunJobWorker.js +17 -16
- package/dist/workers/processors/createRunJobWorker.js.map +1 -1
- package/package.json +1 -1
- package/prisma/migrations/20260811130000_add_ai_message_feedback/migration.sql +7 -0
- package/prisma/migrations/20261025120000_add_visualizations/migration.sql +27 -0
- package/prisma/schema.prisma +32 -0
- package/src/index.ts +30 -4
- package/src/instrument.ts +8 -2
- package/src/lib/fileUpload.ts +49 -0
- package/src/models/cloudStorage.ts +19 -0
- package/src/models/labChat.ts +34 -0
- package/src/pipelines/aiLabChat.ts +79 -18
- package/src/pipelines/aiLabChatContract.ts +35 -0
- package/src/pipelines/aiNewtonChat.ts +34 -2
- package/src/pipelines/generateVisualization.ts +107 -0
- package/src/pipelines/labChatPrompt.ts +11 -12
- package/src/routers/_app.ts +2 -0
- package/src/routers/admin.ts +72 -0
- package/src/routers/class.ts +14 -1
- package/src/routers/labChat.ts +5 -0
- package/src/routers/message.ts +34 -5
- package/src/routers/parent.ts +11 -0
- package/src/routers/studentProgress.ts +15 -0
- package/src/routers/visualization.ts +60 -0
- package/src/routes/aiStream.ts +32 -0
- package/src/seedDatabase.ts +107 -2
- package/src/services/adminClass.ts +222 -0
- package/src/services/adminInstitution.ts +9 -2
- package/src/services/adminMetrics.ts +35 -0
- package/src/services/adminSpam.ts +224 -0
- package/src/services/adminSql.ts +214 -0
- package/src/services/adminTelemetry.ts +194 -0
- package/src/services/adminUser.ts +66 -11
- package/src/services/aiChatStream.ts +83 -7
- package/src/services/aiGeneration.ts +92 -0
- package/src/services/aiProposalTools.ts +166 -1
- package/src/services/aiProposals.ts +428 -0
- package/src/services/auth.ts +8 -2
- package/src/services/fees.ts +5 -1
- package/src/services/labChat.ts +65 -0
- package/src/services/message.ts +205 -142
- package/src/services/module.ts +29 -2
- package/src/services/parentPortal.ts +85 -0
- package/src/services/studentProgress.ts +135 -0
- package/src/services/visualization.ts +147 -0
- package/src/utils/inference.ts +85 -0
- package/src/utils/sentryTrpc.ts +25 -0
- package/src/workers/processors/createRunJobWorker.ts +17 -17
- package/tests/unit/lib/fileUploadSession.test.ts +67 -0
- package/tests/unit/services/adminClass.test.ts +102 -0
- package/tests/unit/services/adminSpam.test.ts +108 -0
- package/tests/unit/services/adminSql.test.ts +56 -0
- package/tests/unit/services/adminTelemetry.test.ts +47 -0
- package/tests/unit/services/adminUser.test.ts +77 -0
- package/tests/unit/services/visualization.test.ts +130 -0
- package/tests/unit/utils/adminPanelAccess.test.ts +26 -0
- package/tests/unit/utils/sentryTrpc.test.ts +15 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"module.js","sources":["services/module.ts"],"sourceRoot":"/","sourcesContent":["/**\n * Module service – Canvas-style modules (\"pathways\") for classes.\n *\n * Modules are ordered groups of content items (assignments, worksheets,\n * files, announcements, discussions, external URLs, sub-headers) with\n * completion requirements, prerequisites, unlock dates and optional\n * sequential progression.\n */\nimport { TRPCError } from \"@trpc/server\";\nimport {\n CourseModuleItemType as ModuleItemType,\n ModuleItemRequirementType,\n ModuleItemProgressStatus,\n Prisma,\n} from \"@prisma/client\";\nimport { prisma } from \"../lib/prisma.js\";\nimport { logger } from \"../utils/logger.js\";\nimport {\n findClassWithTeacher,\n findModulesByClassId,\n findModuleByClassAndId,\n findModuleItemInClass,\n findStudentProgressForClass,\n upsertItemProgress,\n moduleInclude,\n} from \"../models/module.js\";\n\nasync function assertTeacher(userId: string, classId: string) {\n const classData = await findClassWithTeacher(classId, userId);\n if (!classData) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Class not found or you are not a teacher\",\n });\n }\n return classData;\n}\n\n/* -------------------------------------------------------------------------- */\n/* Module CRUD */\n/* -------------------------------------------------------------------------- */\n\nexport async function listModulesRecord(userId: string, classId: string) {\n await assertTeacher(userId, classId);\n return findModulesByClassId(classId);\n}\n\nexport async function createModuleRecord(\n userId: string,\n input: { classId: string; title: string; unlockAt?: Date | null; requireSequential?: boolean }\n) {\n await assertTeacher(userId, input.classId);\n const last = await prisma.module.findFirst({\n where: { classId: input.classId },\n orderBy: { position: \"desc\" },\n select: { position: true },\n });\n return prisma.module.create({\n data: {\n classId: input.classId,\n title: input.title,\n position: (last?.position ?? 0) + 1,\n unlockAt: input.unlockAt ?? null,\n requireSequential: input.requireSequential ?? false,\n },\n include: moduleInclude,\n });\n}\n\nexport async function updateModuleRecord(\n userId: string,\n input: {\n id: string;\n classId: string;\n title?: string;\n unlockAt?: Date | null;\n requireSequential?: boolean;\n published?: boolean;\n prerequisiteIds?: string[];\n }\n) {\n await assertTeacher(userId, input.classId);\n const module = await findModuleByClassAndId(input.id, input.classId);\n if (!module) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Module not found\" });\n }\n\n if (input.prerequisiteIds) {\n const prereqs = await prisma.module.findMany({\n where: { id: { in: input.prerequisiteIds }, classId: input.classId },\n select: { id: true, position: true },\n });\n if (prereqs.length !== input.prerequisiteIds.length) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"Prerequisite module not found in this class\",\n });\n }\n // Like Canvas, only earlier modules can be prerequisites (prevents cycles).\n if (prereqs.some((p) => p.id === input.id || p.position >= module.position)) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"Prerequisites must be modules positioned before this one\",\n });\n }\n }\n\n return prisma.$transaction(async (tx) => {\n if (input.prerequisiteIds) {\n await tx.modulePrerequisite.deleteMany({ where: { moduleId: input.id } });\n if (input.prerequisiteIds.length) {\n await tx.modulePrerequisite.createMany({\n data: input.prerequisiteIds.map((prerequisiteId) => ({\n moduleId: input.id,\n prerequisiteId,\n })),\n });\n }\n }\n return tx.module.update({\n where: { id: input.id },\n data: {\n ...(input.title !== undefined && { title: input.title }),\n ...(input.unlockAt !== undefined && { unlockAt: input.unlockAt }),\n ...(input.requireSequential !== undefined && {\n requireSequential: input.requireSequential,\n }),\n ...(input.published !== undefined && { published: input.published }),\n },\n include: moduleInclude,\n });\n });\n}\n\nexport async function deleteModuleRecord(\n userId: string,\n input: { id: string; classId: string }\n) {\n await assertTeacher(userId, input.classId);\n const module = await findModuleByClassAndId(input.id, input.classId);\n if (!module) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Module not found\" });\n }\n await prisma.module.delete({ where: { id: input.id } });\n return { id: input.id };\n}\n\nexport async function reorderModulesRecord(\n userId: string,\n input: { classId: string; orderedIds: string[] }\n) {\n await assertTeacher(userId, input.classId);\n const modules = await prisma.module.findMany({\n where: { classId: input.classId },\n select: { id: true },\n });\n const classIds = new Set(modules.map((m) => m.id));\n if (\n input.orderedIds.length !== classIds.size ||\n input.orderedIds.some((id) => !classIds.has(id))\n ) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"orderedIds must contain every module of the class exactly once\",\n });\n }\n await prisma.$transaction(\n input.orderedIds.map((id, index) =>\n prisma.module.update({ where: { id }, data: { position: index + 1 } })\n )\n );\n return findModulesByClassId(input.classId);\n}\n\n/* -------------------------------------------------------------------------- */\n/* Module items */\n/* -------------------------------------------------------------------------- */\n\nconst CONTENT_TYPES: ModuleItemType[] = [\n ModuleItemType.ASSIGNMENT,\n ModuleItemType.WORKSHEET,\n ModuleItemType.FILE,\n ModuleItemType.ANNOUNCEMENT,\n ModuleItemType.DISCUSSION,\n];\n\nconst SUBMITTABLE_TYPES: ModuleItemType[] = [\n ModuleItemType.ASSIGNMENT,\n ModuleItemType.WORKSHEET,\n];\n\nasync function validateItemInput(\n classId: string,\n input: {\n itemType: ModuleItemType;\n itemId?: string | null;\n externalUrl?: string | null;\n title?: string | null;\n requirementType?: ModuleItemRequirementType | null;\n requiredScore?: number | null;\n }\n) {\n const { itemType, itemId, externalUrl, title, requirementType } = input;\n\n if (itemType === ModuleItemType.SUB_HEADER) {\n if (!title) {\n throw new TRPCError({ code: \"BAD_REQUEST\", message: \"Sub-header requires a title\" });\n }\n if (requirementType) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"Sub-headers cannot have completion requirements\",\n });\n }\n return;\n }\n\n if (itemType === ModuleItemType.EXTERNAL_URL) {\n if (!externalUrl || !title) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"External URL items require a URL and a title\",\n });\n }\n } else if (CONTENT_TYPES.includes(itemType)) {\n if (!itemId) {\n throw new TRPCError({ code: \"BAD_REQUEST\", message: \"itemId is required\" });\n }\n const exists =\n itemType === ModuleItemType.ASSIGNMENT\n ? await prisma.assignment.findFirst({ where: { id: itemId, classId } })\n : itemType === ModuleItemType.WORKSHEET\n ? await prisma.worksheet.findFirst({ where: { id: itemId, classId } })\n : itemType === ModuleItemType.ANNOUNCEMENT\n ? await prisma.announcement.findFirst({ where: { id: itemId, classId } })\n : itemType === ModuleItemType.DISCUSSION\n ? await prisma.discussion.findFirst({ where: { id: itemId, classId } })\n : await prisma.file.findUnique({ where: { id: itemId } });\n if (!exists) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Referenced content not found in this class\",\n });\n }\n }\n\n if (\n (requirementType === ModuleItemRequirementType.SUBMIT ||\n requirementType === ModuleItemRequirementType.SCORE_AT_LEAST) &&\n !SUBMITTABLE_TYPES.includes(itemType)\n ) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"Submit/score requirements only apply to assignments and worksheets\",\n });\n }\n if (\n requirementType === ModuleItemRequirementType.SCORE_AT_LEAST &&\n (input.requiredScore == null || input.requiredScore < 0)\n ) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"requiredScore is required for score requirements\",\n });\n }\n}\n\nexport async function createModuleItemRecord(\n userId: string,\n input: {\n classId: string;\n moduleId: string;\n itemType: ModuleItemType;\n itemId?: string;\n externalUrl?: string;\n title?: string;\n indent?: number;\n requirementType?: ModuleItemRequirementType;\n requiredScore?: number;\n }\n) {\n await assertTeacher(userId, input.classId);\n const module = await findModuleByClassAndId(input.moduleId, input.classId);\n if (!module) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Module not found\" });\n }\n await validateItemInput(input.classId, input);\n\n const last = await prisma.courseModuleItem.findFirst({\n where: { moduleId: input.moduleId },\n orderBy: { position: \"desc\" },\n select: { position: true },\n });\n\n return prisma.courseModuleItem.create({\n data: {\n moduleId: input.moduleId,\n position: (last?.position ?? 0) + 1,\n indent: input.indent ?? 0,\n itemType: input.itemType,\n itemId: input.itemId ?? null,\n externalUrl: input.externalUrl ?? null,\n title: input.title ?? null,\n requirementType: input.requirementType ?? null,\n requiredScore:\n input.requirementType === ModuleItemRequirementType.SCORE_AT_LEAST\n ? input.requiredScore\n : null,\n },\n });\n}\n\nexport async function updateModuleItemRecord(\n userId: string,\n input: {\n classId: string;\n id: string;\n title?: string | null;\n externalUrl?: string;\n indent?: number;\n requirementType?: ModuleItemRequirementType | null;\n requiredScore?: number | null;\n }\n) {\n await assertTeacher(userId, input.classId);\n const item = await findModuleItemInClass(input.id, input.classId);\n if (!item) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Module item not found\" });\n }\n\n const requirementType =\n input.requirementType !== undefined ? input.requirementType : item.requirementType;\n const requiredScore =\n input.requiredScore !== undefined ? input.requiredScore : item.requiredScore;\n\n await validateItemInput(input.classId, {\n itemType: item.itemType,\n itemId: item.itemId,\n externalUrl: input.externalUrl !== undefined ? input.externalUrl : item.externalUrl,\n title: input.title !== undefined ? input.title : item.title,\n requirementType,\n requiredScore,\n });\n\n return prisma.courseModuleItem.update({\n where: { id: input.id },\n data: {\n ...(input.title !== undefined && { title: input.title }),\n ...(input.externalUrl !== undefined && { externalUrl: input.externalUrl }),\n ...(input.indent !== undefined && { indent: input.indent }),\n ...(input.requirementType !== undefined && {\n requirementType: input.requirementType,\n }),\n requiredScore:\n requirementType === ModuleItemRequirementType.SCORE_AT_LEAST\n ? requiredScore\n : null,\n },\n });\n}\n\nexport async function deleteModuleItemRecord(\n userId: string,\n input: { classId: string; id: string }\n) {\n await assertTeacher(userId, input.classId);\n const item = await findModuleItemInClass(input.id, input.classId);\n if (!item) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Module item not found\" });\n }\n await prisma.courseModuleItem.delete({ where: { id: input.id } });\n return { id: input.id };\n}\n\n/**\n * Reorder items, supporting drag-and-drop across modules. `items` is the full\n * new ordering: for every affected module, its items in display order.\n */\nexport async function reorderModuleItemsRecord(\n userId: string,\n input: { classId: string; items: { id: string; moduleId: string }[] }\n) {\n await assertTeacher(userId, input.classId);\n\n const itemIds = input.items.map((i) => i.id);\n const moduleIds = [...new Set(input.items.map((i) => i.moduleId))];\n\n const [dbItems, dbModules] = await Promise.all([\n prisma.courseModuleItem.findMany({\n where: { id: { in: itemIds }, module: { classId: input.classId } },\n select: { id: true },\n }),\n prisma.module.findMany({\n where: { id: { in: moduleIds }, classId: input.classId },\n select: { id: true },\n }),\n ]);\n if (dbItems.length !== itemIds.length || dbModules.length !== moduleIds.length) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"Item or module not found in this class\",\n });\n }\n\n const positionByModule = new Map<string, number>();\n await prisma.$transaction(\n input.items.map((item) => {\n const next = (positionByModule.get(item.moduleId) ?? 0) + 1;\n positionByModule.set(item.moduleId, next);\n return prisma.courseModuleItem.update({\n where: { id: item.id },\n data: { moduleId: item.moduleId, position: next },\n });\n })\n );\n return findModulesByClassId(input.classId);\n}\n\n/* -------------------------------------------------------------------------- */\n/* Student state & progress */\n/* -------------------------------------------------------------------------- */\n\ntype ModuleWithItems = Prisma.ModuleGetPayload<{\n include: typeof moduleInclude;\n}>;\n\nfunction isModuleComplete(\n module: ModuleWithItems,\n completedItemIds: Set<string>\n) {\n const required = module.items.filter((i) => i.requirementType);\n return required.every((i) => completedItemIds.has(i.id));\n}\n\n/**\n * Compute the full module state for a student: lock status, per-item\n * completion/accessibility and per-module progress percentages.\n */\nexport async function getStudentModulesState(classId: string, studentId: string) {\n const [modules, progress] = await Promise.all([\n findModulesByClassId(classId, true),\n findStudentProgressForClass(classId, studentId),\n ]);\n\n const completedItemIds = new Set(\n progress\n .filter((p) => p.status === ModuleItemProgressStatus.COMPLETE)\n .map((p) => p.moduleItemId)\n );\n\n // Students open worksheets through the assignment they are attached to, so\n // resolve each worksheet item to its containing assignment for navigation.\n const worksheetIds = [\n ...new Set(\n modules\n .flatMap((m) => m.items)\n .filter((i) => i.itemType === ModuleItemType.WORKSHEET && i.itemId)\n .map((i) => i.itemId as string)\n ),\n ];\n const worksheets = worksheetIds.length\n ? await prisma.worksheet.findMany({\n where: { id: { in: worksheetIds } },\n select: { id: true, assignments: { select: { id: true }, take: 1 } },\n })\n : [];\n const assignmentIdByWorksheetId = new Map(\n worksheets.map((w) => [w.id, w.assignments[0]?.id ?? null])\n );\n\n const completeModuleIds = new Set<string>();\n for (const module of modules) {\n if (isModuleComplete(module, completedItemIds)) {\n completeModuleIds.add(module.id);\n }\n }\n\n const now = new Date();\n let continueItemId: string | null = null;\n let continueModuleId: string | null = null;\n\n const result = modules.map((module) => {\n const publishedPrereqIds = module.prerequisites\n .map((p) => p.prerequisiteId)\n .filter((id) => modules.some((m) => m.id === id));\n const lockedByPrerequisite = publishedPrereqIds.some(\n (id) => !completeModuleIds.has(id)\n );\n const lockedByDate = !!module.unlockAt && module.unlockAt > now;\n const locked = lockedByPrerequisite || lockedByDate;\n\n const required = module.items.filter((i) => i.requirementType);\n const completedRequired = required.filter((i) => completedItemIds.has(i.id));\n const percentComplete = required.length\n ? Math.round((completedRequired.length / required.length) * 100)\n : completeModuleIds.has(module.id)\n ? 100\n : 0;\n\n // With sequential progression, items after the first incomplete required\n // item are not yet accessible.\n let sequentialBlocked = false;\n const items = module.items.map((item) => {\n const completed = completedItemIds.has(item.id);\n const accessible =\n !locked && (!module.requireSequential || !sequentialBlocked);\n if (\n module.requireSequential &&\n item.requirementType &&\n !completed &&\n !sequentialBlocked\n ) {\n sequentialBlocked = true;\n }\n if (\n !continueItemId &&\n !locked &&\n accessible &&\n item.requirementType &&\n !completed\n ) {\n continueItemId = item.id;\n continueModuleId = module.id;\n }\n return {\n ...item,\n completed,\n accessible,\n worksheetAssignmentId:\n item.itemType === ModuleItemType.WORKSHEET && item.itemId\n ? (assignmentIdByWorksheetId.get(item.itemId) ?? null)\n : null,\n };\n });\n\n return {\n ...module,\n items,\n locked,\n lockedByPrerequisite,\n lockedByDate,\n completed: completeModuleIds.has(module.id),\n requiredCount: required.length,\n completedRequiredCount: completedRequired.length,\n percentComplete,\n };\n });\n\n return {\n modules: result,\n continueItemId,\n continueModuleId,\n };\n}\n\n/**\n * Mark a module item as viewed by a student. Completes VIEW requirements;\n * for other requirement types this only records that a progress row exists.\n */\nexport async function markItemViewedRecord(\n studentId: string,\n input: { classId: string; itemId: string }\n) {\n const item = await findModuleItemInClass(input.itemId, input.classId);\n if (!item || !item.module.published) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Module item not found\" });\n }\n\n const state = await getStudentModulesState(input.classId, studentId);\n const moduleState = state.modules.find((m) => m.id === item.moduleId);\n const itemState = moduleState?.items.find((i) => i.id === item.id);\n if (!moduleState || !itemState || moduleState.locked || !itemState.accessible) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"This module item is locked\",\n });\n }\n\n if (item.requirementType === ModuleItemRequirementType.VIEW) {\n return upsertItemProgress(item.id, studentId, \"COMPLETE\");\n }\n return upsertItemProgress(item.id, studentId, \"INCOMPLETE\");\n}\n\n/* -------------------------------------------------------------------------- */\n/* Automatic completion hooks */\n/* -------------------------------------------------------------------------- */\n\n/**\n * Update module progress for a student's assignment after a submission event\n * (submitted or returned/graded). Safe to call from any flow; never throws.\n */\nexport async function syncAssignmentModuleProgress(\n studentId: string,\n assignmentId: string\n) {\n try {\n const items = await prisma.courseModuleItem.findMany({\n where: {\n itemType: ModuleItemType.ASSIGNMENT,\n itemId: assignmentId,\n requirementType: {\n in: [\n ModuleItemRequirementType.SUBMIT,\n ModuleItemRequirementType.SCORE_AT_LEAST,\n ],\n },\n },\n });\n if (!items.length) return;\n\n const submission = await prisma.submission.findFirst({\n where: { assignmentId, studentId },\n select: { submitted: true, returned: true, gradeReceived: true },\n });\n if (!submission) return;\n\n for (const item of items) {\n const complete =\n item.requirementType === ModuleItemRequirementType.SUBMIT\n ? !!submission.submitted\n : !!submission.returned &&\n submission.gradeReceived != null &&\n submission.gradeReceived >= (item.requiredScore ?? 0);\n if (complete) {\n await upsertItemProgress(item.id, studentId, \"COMPLETE\");\n }\n }\n } catch (error) {\n logger.error(\"Failed to sync assignment module progress\", {\n error,\n studentId,\n assignmentId,\n });\n }\n}\n\n/**\n * Update module progress for a student's worksheet after grading completes.\n * SUBMIT requirements complete immediately; SCORE_AT_LEAST compares the sum\n * of earned points across the worksheet response. Never throws.\n */\nexport async function syncWorksheetModuleProgress(\n studentId: string,\n worksheetId: string\n) {\n try {\n const items = await prisma.courseModuleItem.findMany({\n where: {\n itemType: ModuleItemType.WORKSHEET,\n itemId: worksheetId,\n requirementType: {\n in: [\n ModuleItemRequirementType.SUBMIT,\n ModuleItemRequirementType.SCORE_AT_LEAST,\n ],\n },\n },\n });\n if (!items.length) return;\n\n const earned = await prisma.studentQuestionProgress.aggregate({\n where: { studentId, question: { worksheetId } },\n _sum: { points: true },\n });\n const totalPoints = earned._sum.points ?? 0;\n\n for (const item of items) {\n const complete =\n item.requirementType === ModuleItemRequirementType.SUBMIT ||\n totalPoints >= (item.requiredScore ?? 0);\n if (complete) {\n await upsertItemProgress(item.id, studentId, \"COMPLETE\");\n }\n }\n } catch (error) {\n logger.error(\"Failed to sync worksheet module progress\", {\n error,\n studentId,\n worksheetId,\n });\n }\n}\n"],"names":[],"mappings":"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","debug_id":"86e40dd6-85f3-59e2-afd9-e0da189f9288"}
|
|
1
|
+
{"version":3,"file":"module.js","sources":["services/module.ts"],"sourceRoot":"/","sourcesContent":["/**\n * Module service – Canvas-style modules (\"pathways\") for classes.\n *\n * Modules are ordered groups of content items (assignments, worksheets,\n * files, announcements, discussions, external URLs, sub-headers) with\n * completion requirements, prerequisites, unlock dates and optional\n * sequential progression.\n */\nimport { TRPCError } from \"@trpc/server\";\nimport {\n CourseModuleItemType as ModuleItemType,\n ModuleItemRequirementType,\n ModuleItemProgressStatus,\n Prisma,\n} from \"@prisma/client\";\nimport { prisma } from \"../lib/prisma.js\";\nimport { logger } from \"../utils/logger.js\";\nimport {\n findClassWithTeacher,\n findModulesByClassId,\n findModuleByClassAndId,\n findModuleItemInClass,\n findStudentProgressForClass,\n upsertItemProgress,\n moduleInclude,\n} from \"../models/module.js\";\n\nasync function assertTeacher(userId: string, classId: string) {\n const classData = await findClassWithTeacher(classId, userId);\n if (!classData) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Class not found or you are not a teacher\",\n });\n }\n return classData;\n}\n\n/* -------------------------------------------------------------------------- */\n/* Module CRUD */\n/* -------------------------------------------------------------------------- */\n\nexport async function listModulesRecord(userId: string, classId: string) {\n await assertTeacher(userId, classId);\n return findModulesByClassId(classId);\n}\n\nexport async function createModuleRecord(\n userId: string,\n input: { classId: string; title: string; unlockAt?: Date | null; requireSequential?: boolean }\n) {\n await assertTeacher(userId, input.classId);\n const last = await prisma.module.findFirst({\n where: { classId: input.classId },\n orderBy: { position: \"desc\" },\n select: { position: true },\n });\n return prisma.module.create({\n data: {\n classId: input.classId,\n title: input.title,\n position: (last?.position ?? 0) + 1,\n unlockAt: input.unlockAt ?? null,\n requireSequential: input.requireSequential ?? false,\n },\n include: moduleInclude,\n });\n}\n\nexport async function updateModuleRecord(\n userId: string,\n input: {\n id: string;\n classId: string;\n title?: string;\n unlockAt?: Date | null;\n requireSequential?: boolean;\n published?: boolean;\n prerequisiteIds?: string[];\n }\n) {\n await assertTeacher(userId, input.classId);\n const module = await findModuleByClassAndId(input.id, input.classId);\n if (!module) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Module not found\" });\n }\n\n if (input.prerequisiteIds) {\n const prereqs = await prisma.module.findMany({\n where: { id: { in: input.prerequisiteIds }, classId: input.classId },\n select: { id: true, position: true },\n });\n if (prereqs.length !== input.prerequisiteIds.length) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"Prerequisite module not found in this class\",\n });\n }\n // Like Canvas, only earlier modules can be prerequisites (prevents cycles).\n if (prereqs.some((p) => p.id === input.id || p.position >= module.position)) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"Prerequisites must be modules positioned before this one\",\n });\n }\n }\n\n return prisma.$transaction(async (tx) => {\n if (input.prerequisiteIds) {\n await tx.modulePrerequisite.deleteMany({ where: { moduleId: input.id } });\n if (input.prerequisiteIds.length) {\n await tx.modulePrerequisite.createMany({\n data: input.prerequisiteIds.map((prerequisiteId) => ({\n moduleId: input.id,\n prerequisiteId,\n })),\n });\n }\n }\n return tx.module.update({\n where: { id: input.id },\n data: {\n ...(input.title !== undefined && { title: input.title }),\n ...(input.unlockAt !== undefined && { unlockAt: input.unlockAt }),\n ...(input.requireSequential !== undefined && {\n requireSequential: input.requireSequential,\n }),\n ...(input.published !== undefined && { published: input.published }),\n },\n include: moduleInclude,\n });\n });\n}\n\nexport async function deleteModuleRecord(\n userId: string,\n input: { id: string; classId: string }\n) {\n await assertTeacher(userId, input.classId);\n const module = await findModuleByClassAndId(input.id, input.classId);\n if (!module) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Module not found\" });\n }\n await prisma.module.delete({ where: { id: input.id } });\n return { id: input.id };\n}\n\nexport async function reorderModulesRecord(\n userId: string,\n input: { classId: string; orderedIds: string[] }\n) {\n await assertTeacher(userId, input.classId);\n const modules = await prisma.module.findMany({\n where: { classId: input.classId },\n select: { id: true },\n });\n const classIds = new Set(modules.map((m) => m.id));\n if (\n input.orderedIds.length !== classIds.size ||\n input.orderedIds.some((id) => !classIds.has(id))\n ) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"orderedIds must contain every module of the class exactly once\",\n });\n }\n await prisma.$transaction(\n input.orderedIds.map((id, index) =>\n prisma.module.update({ where: { id }, data: { position: index + 1 } })\n )\n );\n return findModulesByClassId(input.classId);\n}\n\n/* -------------------------------------------------------------------------- */\n/* Module items */\n/* -------------------------------------------------------------------------- */\n\nconst CONTENT_TYPES: ModuleItemType[] = [\n ModuleItemType.ASSIGNMENT,\n ModuleItemType.WORKSHEET,\n ModuleItemType.FILE,\n ModuleItemType.ANNOUNCEMENT,\n ModuleItemType.DISCUSSION,\n ModuleItemType.VISUALIZATION,\n];\n\nconst SUBMITTABLE_TYPES: ModuleItemType[] = [\n ModuleItemType.ASSIGNMENT,\n ModuleItemType.WORKSHEET,\n];\n\nasync function validateItemInput(\n classId: string,\n input: {\n itemType: ModuleItemType;\n itemId?: string | null;\n externalUrl?: string | null;\n title?: string | null;\n requirementType?: ModuleItemRequirementType | null;\n requiredScore?: number | null;\n }\n) {\n const { itemType, itemId, externalUrl, title, requirementType } = input;\n\n if (itemType === ModuleItemType.SUB_HEADER) {\n if (!title) {\n throw new TRPCError({ code: \"BAD_REQUEST\", message: \"Sub-header requires a title\" });\n }\n if (requirementType) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"Sub-headers cannot have completion requirements\",\n });\n }\n return;\n }\n\n if (itemType === ModuleItemType.EXTERNAL_URL) {\n if (!externalUrl || !title) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"External URL items require a URL and a title\",\n });\n }\n } else if (CONTENT_TYPES.includes(itemType)) {\n if (!itemId) {\n throw new TRPCError({ code: \"BAD_REQUEST\", message: \"itemId is required\" });\n }\n const exists =\n itemType === ModuleItemType.ASSIGNMENT\n ? await prisma.assignment.findFirst({ where: { id: itemId, classId } })\n : itemType === ModuleItemType.WORKSHEET\n ? await prisma.worksheet.findFirst({ where: { id: itemId, classId } })\n : itemType === ModuleItemType.ANNOUNCEMENT\n ? await prisma.announcement.findFirst({ where: { id: itemId, classId } })\n : itemType === ModuleItemType.DISCUSSION\n ? await prisma.discussion.findFirst({ where: { id: itemId, classId } })\n : itemType === ModuleItemType.VISUALIZATION\n ? await prisma.visualization.findFirst({ where: { id: itemId, classId } })\n : await prisma.file.findUnique({ where: { id: itemId } });\n if (!exists) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Referenced content not found in this class\",\n });\n }\n }\n\n if (\n (requirementType === ModuleItemRequirementType.SUBMIT ||\n requirementType === ModuleItemRequirementType.SCORE_AT_LEAST) &&\n !SUBMITTABLE_TYPES.includes(itemType)\n ) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"Submit/score requirements only apply to assignments and worksheets\",\n });\n }\n if (\n requirementType === ModuleItemRequirementType.SCORE_AT_LEAST &&\n (input.requiredScore == null || input.requiredScore < 0)\n ) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"requiredScore is required for score requirements\",\n });\n }\n}\n\nexport async function createModuleItemRecord(\n userId: string,\n input: {\n classId: string;\n moduleId: string;\n itemType: ModuleItemType;\n itemId?: string;\n externalUrl?: string;\n title?: string;\n indent?: number;\n requirementType?: ModuleItemRequirementType;\n requiredScore?: number;\n }\n) {\n await assertTeacher(userId, input.classId);\n const module = await findModuleByClassAndId(input.moduleId, input.classId);\n if (!module) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Module not found\" });\n }\n await validateItemInput(input.classId, input);\n\n const last = await prisma.courseModuleItem.findFirst({\n where: { moduleId: input.moduleId },\n orderBy: { position: \"desc\" },\n select: { position: true },\n });\n\n return prisma.courseModuleItem.create({\n data: {\n moduleId: input.moduleId,\n position: (last?.position ?? 0) + 1,\n indent: input.indent ?? 0,\n itemType: input.itemType,\n itemId: input.itemId ?? null,\n externalUrl: input.externalUrl ?? null,\n title: input.title ?? null,\n requirementType: input.requirementType ?? null,\n requiredScore:\n input.requirementType === ModuleItemRequirementType.SCORE_AT_LEAST\n ? input.requiredScore\n : null,\n },\n });\n}\n\nexport async function updateModuleItemRecord(\n userId: string,\n input: {\n classId: string;\n id: string;\n title?: string | null;\n externalUrl?: string;\n indent?: number;\n requirementType?: ModuleItemRequirementType | null;\n requiredScore?: number | null;\n }\n) {\n await assertTeacher(userId, input.classId);\n const item = await findModuleItemInClass(input.id, input.classId);\n if (!item) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Module item not found\" });\n }\n\n const requirementType =\n input.requirementType !== undefined ? input.requirementType : item.requirementType;\n const requiredScore =\n input.requiredScore !== undefined ? input.requiredScore : item.requiredScore;\n\n await validateItemInput(input.classId, {\n itemType: item.itemType,\n itemId: item.itemId,\n externalUrl: input.externalUrl !== undefined ? input.externalUrl : item.externalUrl,\n title: input.title !== undefined ? input.title : item.title,\n requirementType,\n requiredScore,\n });\n\n return prisma.courseModuleItem.update({\n where: { id: input.id },\n data: {\n ...(input.title !== undefined && { title: input.title }),\n ...(input.externalUrl !== undefined && { externalUrl: input.externalUrl }),\n ...(input.indent !== undefined && { indent: input.indent }),\n ...(input.requirementType !== undefined && {\n requirementType: input.requirementType,\n }),\n requiredScore:\n requirementType === ModuleItemRequirementType.SCORE_AT_LEAST\n ? requiredScore\n : null,\n },\n });\n}\n\nexport async function deleteModuleItemRecord(\n userId: string,\n input: { classId: string; id: string }\n) {\n await assertTeacher(userId, input.classId);\n const item = await findModuleItemInClass(input.id, input.classId);\n if (!item) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Module item not found\" });\n }\n await prisma.courseModuleItem.delete({ where: { id: input.id } });\n return { id: input.id };\n}\n\n/**\n * Reorder items, supporting drag-and-drop across modules. `items` is the full\n * new ordering: for every affected module, its items in display order.\n */\nexport async function reorderModuleItemsRecord(\n userId: string,\n input: { classId: string; items: { id: string; moduleId: string }[] }\n) {\n await assertTeacher(userId, input.classId);\n\n const itemIds = input.items.map((i) => i.id);\n const moduleIds = [...new Set(input.items.map((i) => i.moduleId))];\n\n const [dbItems, dbModules] = await Promise.all([\n prisma.courseModuleItem.findMany({\n where: { id: { in: itemIds }, module: { classId: input.classId } },\n select: { id: true },\n }),\n prisma.module.findMany({\n where: { id: { in: moduleIds }, classId: input.classId },\n select: { id: true },\n }),\n ]);\n if (dbItems.length !== itemIds.length || dbModules.length !== moduleIds.length) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"Item or module not found in this class\",\n });\n }\n\n const positionByModule = new Map<string, number>();\n await prisma.$transaction(\n input.items.map((item) => {\n const next = (positionByModule.get(item.moduleId) ?? 0) + 1;\n positionByModule.set(item.moduleId, next);\n return prisma.courseModuleItem.update({\n where: { id: item.id },\n data: { moduleId: item.moduleId, position: next },\n });\n })\n );\n return findModulesByClassId(input.classId);\n}\n\n/* -------------------------------------------------------------------------- */\n/* Student state & progress */\n/* -------------------------------------------------------------------------- */\n\ntype ModuleWithItems = Prisma.ModuleGetPayload<{\n include: typeof moduleInclude;\n}>;\n\nfunction isModuleComplete(\n module: ModuleWithItems,\n completedItemIds: Set<string>\n) {\n const required = module.items.filter((i) => i.requirementType);\n return required.every((i) => completedItemIds.has(i.id));\n}\n\n/**\n * Compute the full module state for a student: lock status, per-item\n * completion/accessibility and per-module progress percentages.\n */\nexport async function getStudentModulesState(classId: string, studentId: string) {\n const [allModules, progress] = await Promise.all([\n findModulesByClassId(classId, true),\n findStudentProgressForClass(classId, studentId),\n ]);\n\n // Hide visualisations the teacher hasn't published yet.\n const visualizationIds = allModules\n .flatMap((m) => m.items)\n .filter((i) => i.itemType === ModuleItemType.VISUALIZATION && i.itemId)\n .map((i) => i.itemId as string);\n const publishedVisualizationIds = new Set(\n visualizationIds.length\n ? (\n await prisma.visualization.findMany({\n where: { id: { in: visualizationIds }, classId, published: true },\n select: { id: true },\n })\n ).map((v) => v.id)\n : []\n );\n const modules = allModules.map((m) => ({\n ...m,\n items: m.items.filter(\n (i) =>\n i.itemType !== ModuleItemType.VISUALIZATION ||\n (!!i.itemId && publishedVisualizationIds.has(i.itemId))\n ),\n }));\n\n const completedItemIds = new Set(\n progress\n .filter((p) => p.status === ModuleItemProgressStatus.COMPLETE)\n .map((p) => p.moduleItemId)\n );\n\n // Students open worksheets through the assignment they are attached to, so\n // resolve each worksheet item to its containing assignment for navigation.\n const worksheetIds = [\n ...new Set(\n modules\n .flatMap((m) => m.items)\n .filter((i) => i.itemType === ModuleItemType.WORKSHEET && i.itemId)\n .map((i) => i.itemId as string)\n ),\n ];\n const worksheets = worksheetIds.length\n ? await prisma.worksheet.findMany({\n where: { id: { in: worksheetIds } },\n select: { id: true, assignments: { select: { id: true }, take: 1 } },\n })\n : [];\n const assignmentIdByWorksheetId = new Map(\n worksheets.map((w) => [w.id, w.assignments[0]?.id ?? null])\n );\n\n const completeModuleIds = new Set<string>();\n for (const module of modules) {\n if (isModuleComplete(module, completedItemIds)) {\n completeModuleIds.add(module.id);\n }\n }\n\n const now = new Date();\n let continueItemId: string | null = null;\n let continueModuleId: string | null = null;\n\n const result = modules.map((module) => {\n const publishedPrereqIds = module.prerequisites\n .map((p) => p.prerequisiteId)\n .filter((id) => modules.some((m) => m.id === id));\n const lockedByPrerequisite = publishedPrereqIds.some(\n (id) => !completeModuleIds.has(id)\n );\n const lockedByDate = !!module.unlockAt && module.unlockAt > now;\n const locked = lockedByPrerequisite || lockedByDate;\n\n const required = module.items.filter((i) => i.requirementType);\n const completedRequired = required.filter((i) => completedItemIds.has(i.id));\n const percentComplete = required.length\n ? Math.round((completedRequired.length / required.length) * 100)\n : completeModuleIds.has(module.id)\n ? 100\n : 0;\n\n // With sequential progression, items after the first incomplete required\n // item are not yet accessible.\n let sequentialBlocked = false;\n const items = module.items.map((item) => {\n const completed = completedItemIds.has(item.id);\n const accessible =\n !locked && (!module.requireSequential || !sequentialBlocked);\n if (\n module.requireSequential &&\n item.requirementType &&\n !completed &&\n !sequentialBlocked\n ) {\n sequentialBlocked = true;\n }\n if (\n !continueItemId &&\n !locked &&\n accessible &&\n item.requirementType &&\n !completed\n ) {\n continueItemId = item.id;\n continueModuleId = module.id;\n }\n return {\n ...item,\n completed,\n accessible,\n worksheetAssignmentId:\n item.itemType === ModuleItemType.WORKSHEET && item.itemId\n ? (assignmentIdByWorksheetId.get(item.itemId) ?? null)\n : null,\n };\n });\n\n return {\n ...module,\n items,\n locked,\n lockedByPrerequisite,\n lockedByDate,\n completed: completeModuleIds.has(module.id),\n requiredCount: required.length,\n completedRequiredCount: completedRequired.length,\n percentComplete,\n };\n });\n\n return {\n modules: result,\n continueItemId,\n continueModuleId,\n };\n}\n\n/**\n * Mark a module item as viewed by a student. Completes VIEW requirements;\n * for other requirement types this only records that a progress row exists.\n */\nexport async function markItemViewedRecord(\n studentId: string,\n input: { classId: string; itemId: string }\n) {\n const item = await findModuleItemInClass(input.itemId, input.classId);\n if (!item || !item.module.published) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Module item not found\" });\n }\n\n const state = await getStudentModulesState(input.classId, studentId);\n const moduleState = state.modules.find((m) => m.id === item.moduleId);\n const itemState = moduleState?.items.find((i) => i.id === item.id);\n if (!moduleState || !itemState || moduleState.locked || !itemState.accessible) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"This module item is locked\",\n });\n }\n\n if (item.requirementType === ModuleItemRequirementType.VIEW) {\n return upsertItemProgress(item.id, studentId, \"COMPLETE\");\n }\n return upsertItemProgress(item.id, studentId, \"INCOMPLETE\");\n}\n\n/* -------------------------------------------------------------------------- */\n/* Automatic completion hooks */\n/* -------------------------------------------------------------------------- */\n\n/**\n * Update module progress for a student's assignment after a submission event\n * (submitted or returned/graded). Safe to call from any flow; never throws.\n */\nexport async function syncAssignmentModuleProgress(\n studentId: string,\n assignmentId: string\n) {\n try {\n const items = await prisma.courseModuleItem.findMany({\n where: {\n itemType: ModuleItemType.ASSIGNMENT,\n itemId: assignmentId,\n requirementType: {\n in: [\n ModuleItemRequirementType.SUBMIT,\n ModuleItemRequirementType.SCORE_AT_LEAST,\n ],\n },\n },\n });\n if (!items.length) return;\n\n const submission = await prisma.submission.findFirst({\n where: { assignmentId, studentId },\n select: { submitted: true, returned: true, gradeReceived: true },\n });\n if (!submission) return;\n\n for (const item of items) {\n const complete =\n item.requirementType === ModuleItemRequirementType.SUBMIT\n ? !!submission.submitted\n : !!submission.returned &&\n submission.gradeReceived != null &&\n submission.gradeReceived >= (item.requiredScore ?? 0);\n if (complete) {\n await upsertItemProgress(item.id, studentId, \"COMPLETE\");\n }\n }\n } catch (error) {\n logger.error(\"Failed to sync assignment module progress\", {\n error,\n studentId,\n assignmentId,\n });\n }\n}\n\n/**\n * Update module progress for a student's worksheet after grading completes.\n * SUBMIT requirements complete immediately; SCORE_AT_LEAST compares the sum\n * of earned points across the worksheet response. Never throws.\n */\nexport async function syncWorksheetModuleProgress(\n studentId: string,\n worksheetId: string\n) {\n try {\n const items = await prisma.courseModuleItem.findMany({\n where: {\n itemType: ModuleItemType.WORKSHEET,\n itemId: worksheetId,\n requirementType: {\n in: [\n ModuleItemRequirementType.SUBMIT,\n ModuleItemRequirementType.SCORE_AT_LEAST,\n ],\n },\n },\n });\n if (!items.length) return;\n\n const earned = await prisma.studentQuestionProgress.aggregate({\n where: { studentId, question: { worksheetId } },\n _sum: { points: true },\n });\n const totalPoints = earned._sum.points ?? 0;\n\n for (const item of items) {\n const complete =\n item.requirementType === ModuleItemRequirementType.SUBMIT ||\n totalPoints >= (item.requiredScore ?? 0);\n if (complete) {\n await upsertItemProgress(item.id, studentId, \"COMPLETE\");\n }\n }\n } catch (error) {\n logger.error(\"Failed to sync worksheet module progress\", {\n error,\n studentId,\n worksheetId,\n });\n }\n}\n"],"names":[],"mappings":"AAAA;;;;;;;GAOG;;;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EACL,oBAAoB,IAAI,cAAc,EACtC,yBAAyB,EACzB,wBAAwB,GAEzB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,sBAAsB,EACtB,qBAAqB,EACrB,2BAA2B,EAC3B,kBAAkB,EAClB,aAAa,GACd,MAAM,qBAAqB,CAAC;AAE7B,KAAK,UAAU,aAAa,CAAC,MAAc,EAAE,OAAe;IAC1D,MAAM,SAAS,GAAG,MAAM,oBAAoB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAC9D,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,MAAM,IAAI,SAAS,CAAC;YAClB,IAAI,EAAE,WAAW;YACjB,OAAO,EAAE,0CAA0C;SACpD,CAAC,CAAC;IACL,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,gFAAgF;AAChF,gFAAgF;AAChF,gFAAgF;AAEhF,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,MAAc,EAAE,OAAe;IACrE,MAAM,aAAa,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,OAAO,oBAAoB,CAAC,OAAO,CAAC,CAAC;AACvC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,MAAc,EACd,KAA8F;IAE9F,MAAM,aAAa,CAAC,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;IAC3C,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC;QACzC,KAAK,EAAE,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE;QACjC,OAAO,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE;QAC7B,MAAM,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;KAC3B,CAAC,CAAC;IACH,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC;QAC1B,IAAI,EAAE;YACJ,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,QAAQ,EAAE,CAAC,IAAI,EAAE,QAAQ,IAAI,CAAC,CAAC,GAAG,CAAC;YACnC,QAAQ,EAAE,KAAK,CAAC,QAAQ,IAAI,IAAI;YAChC,iBAAiB,EAAE,KAAK,CAAC,iBAAiB,IAAI,KAAK;SACpD;QACD,OAAO,EAAE,aAAa;KACvB,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,MAAc,EACd,KAQC;IAED,MAAM,aAAa,CAAC,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;IAC3C,MAAM,MAAM,GAAG,MAAM,sBAAsB,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;IACrE,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,SAAS,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,kBAAkB,EAAE,CAAC,CAAC;IAC1E,CAAC;IAED,IAAI,KAAK,CAAC,eAAe,EAAE,CAAC;QAC1B,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC;YAC3C,KAAK,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,eAAe,EAAE,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE;YACpE,MAAM,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE;SACrC,CAAC,CAAC;QACH,IAAI,OAAO,CAAC,MAAM,KAAK,KAAK,CAAC,eAAe,CAAC,MAAM,EAAE,CAAC;YACpD,MAAM,IAAI,SAAS,CAAC;gBAClB,IAAI,EAAE,aAAa;gBACnB,OAAO,EAAE,6CAA6C;aACvD,CAAC,CAAC;QACL,CAAC;QACD,4EAA4E;QAC5E,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,KAAK,CAAC,EAAE,IAAI,CAAC,CAAC,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC5E,MAAM,IAAI,SAAS,CAAC;gBAClB,IAAI,EAAE,aAAa;gBACnB,OAAO,EAAE,0DAA0D;aACpE,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC,YAAY,CAAC,KAAK,EAAE,EAAE,EAAE,EAAE;QACtC,IAAI,KAAK,CAAC,eAAe,EAAE,CAAC;YAC1B,MAAM,EAAE,CAAC,kBAAkB,CAAC,UAAU,CAAC,EAAE,KAAK,EAAE,EAAE,QAAQ,EAAE,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;YAC1E,IAAI,KAAK,CAAC,eAAe,CAAC,MAAM,EAAE,CAAC;gBACjC,MAAM,EAAE,CAAC,kBAAkB,CAAC,UAAU,CAAC;oBACrC,IAAI,EAAE,KAAK,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC;wBACnD,QAAQ,EAAE,KAAK,CAAC,EAAE;wBAClB,cAAc;qBACf,CAAC,CAAC;iBACJ,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QACD,OAAO,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC;YACtB,KAAK,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,EAAE,EAAE;YACvB,IAAI,EAAE;gBACJ,GAAG,CAAC,KAAK,CAAC,KAAK,KAAK,SAAS,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC;gBACxD,GAAG,CAAC,KAAK,CAAC,QAAQ,KAAK,SAAS,IAAI,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC;gBACjE,GAAG,CAAC,KAAK,CAAC,iBAAiB,KAAK,SAAS,IAAI;oBAC3C,iBAAiB,EAAE,KAAK,CAAC,iBAAiB;iBAC3C,CAAC;gBACF,GAAG,CAAC,KAAK,CAAC,SAAS,KAAK,SAAS,IAAI,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,EAAE,CAAC;aACrE;YACD,OAAO,EAAE,aAAa;SACvB,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,MAAc,EACd,KAAsC;IAEtC,MAAM,aAAa,CAAC,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;IAC3C,MAAM,MAAM,GAAG,MAAM,sBAAsB,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;IACrE,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,SAAS,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,kBAAkB,EAAE,CAAC,CAAC;IAC1E,CAAC;IACD,MAAM,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;IACxD,OAAO,EAAE,EAAE,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC;AAC1B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,MAAc,EACd,KAAgD;IAEhD,MAAM,aAAa,CAAC,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;IAC3C,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC;QAC3C,KAAK,EAAE,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE;QACjC,MAAM,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE;KACrB,CAAC,CAAC;IACH,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACnD,IACE,KAAK,CAAC,UAAU,CAAC,MAAM,KAAK,QAAQ,CAAC,IAAI;QACzC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,EAChD,CAAC;QACD,MAAM,IAAI,SAAS,CAAC;YAClB,IAAI,EAAE,aAAa;YACnB,OAAO,EAAE,gEAAgE;SAC1E,CAAC,CAAC;IACL,CAAC;IACD,MAAM,MAAM,CAAC,YAAY,CACvB,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,KAAK,EAAE,EAAE,CACjC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,KAAK,GAAG,CAAC,EAAE,EAAE,CAAC,CACvE,CACF,CAAC;IACF,OAAO,oBAAoB,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;AAC7C,CAAC;AAED,gFAAgF;AAChF,gFAAgF;AAChF,gFAAgF;AAEhF,MAAM,aAAa,GAAqB;IACtC,cAAc,CAAC,UAAU;IACzB,cAAc,CAAC,SAAS;IACxB,cAAc,CAAC,IAAI;IACnB,cAAc,CAAC,YAAY;IAC3B,cAAc,CAAC,UAAU;IACzB,cAAc,CAAC,aAAa;CAC7B,CAAC;AAEF,MAAM,iBAAiB,GAAqB;IAC1C,cAAc,CAAC,UAAU;IACzB,cAAc,CAAC,SAAS;CACzB,CAAC;AAEF,KAAK,UAAU,iBAAiB,CAC9B,OAAe,EACf,KAOC;IAED,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,eAAe,EAAE,GAAG,KAAK,CAAC;IAExE,IAAI,QAAQ,KAAK,cAAc,CAAC,UAAU,EAAE,CAAC;QAC3C,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,IAAI,SAAS,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,6BAA6B,EAAE,CAAC,CAAC;QACvF,CAAC;QACD,IAAI,eAAe,EAAE,CAAC;YACpB,MAAM,IAAI,SAAS,CAAC;gBAClB,IAAI,EAAE,aAAa;gBACnB,OAAO,EAAE,iDAAiD;aAC3D,CAAC,CAAC;QACL,CAAC;QACD,OAAO;IACT,CAAC;IAED,IAAI,QAAQ,KAAK,cAAc,CAAC,YAAY,EAAE,CAAC;QAC7C,IAAI,CAAC,WAAW,IAAI,CAAC,KAAK,EAAE,CAAC;YAC3B,MAAM,IAAI,SAAS,CAAC;gBAClB,IAAI,EAAE,aAAa;gBACnB,OAAO,EAAE,8CAA8C;aACxD,CAAC,CAAC;QACL,CAAC;IACH,CAAC;SAAM,IAAI,aAAa,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC5C,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,IAAI,SAAS,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,oBAAoB,EAAE,CAAC,CAAC;QAC9E,CAAC;QACD,MAAM,MAAM,GACV,QAAQ,KAAK,cAAc,CAAC,UAAU;YACpC,CAAC,CAAC,MAAM,MAAM,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,CAAC;YACvE,CAAC,CAAC,QAAQ,KAAK,cAAc,CAAC,SAAS;gBACrC,CAAC,CAAC,MAAM,MAAM,CAAC,SAAS,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,CAAC;gBACtE,CAAC,CAAC,QAAQ,KAAK,cAAc,CAAC,YAAY;oBACxC,CAAC,CAAC,MAAM,MAAM,CAAC,YAAY,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,CAAC;oBACzE,CAAC,CAAC,QAAQ,KAAK,cAAc,CAAC,UAAU;wBACtC,CAAC,CAAC,MAAM,MAAM,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,CAAC;wBACvE,CAAC,CAAC,QAAQ,KAAK,cAAc,CAAC,aAAa;4BACzC,CAAC,CAAC,MAAM,MAAM,CAAC,aAAa,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,CAAC;4BAC1E,CAAC,CAAC,MAAM,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC;QACtE,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,IAAI,SAAS,CAAC;gBAClB,IAAI,EAAE,WAAW;gBACjB,OAAO,EAAE,4CAA4C;aACtD,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,IACE,CAAC,eAAe,KAAK,yBAAyB,CAAC,MAAM;QACnD,eAAe,KAAK,yBAAyB,CAAC,cAAc,CAAC;QAC/D,CAAC,iBAAiB,CAAC,QAAQ,CAAC,QAAQ,CAAC,EACrC,CAAC;QACD,MAAM,IAAI,SAAS,CAAC;YAClB,IAAI,EAAE,aAAa;YACnB,OAAO,EAAE,oEAAoE;SAC9E,CAAC,CAAC;IACL,CAAC;IACD,IACE,eAAe,KAAK,yBAAyB,CAAC,cAAc;QAC5D,CAAC,KAAK,CAAC,aAAa,IAAI,IAAI,IAAI,KAAK,CAAC,aAAa,GAAG,CAAC,CAAC,EACxD,CAAC;QACD,MAAM,IAAI,SAAS,CAAC;YAClB,IAAI,EAAE,aAAa;YACnB,OAAO,EAAE,kDAAkD;SAC5D,CAAC,CAAC;IACL,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,MAAc,EACd,KAUC;IAED,MAAM,aAAa,CAAC,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;IAC3C,MAAM,MAAM,GAAG,MAAM,sBAAsB,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;IAC3E,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,SAAS,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,kBAAkB,EAAE,CAAC,CAAC;IAC1E,CAAC;IACD,MAAM,iBAAiB,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IAE9C,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,gBAAgB,CAAC,SAAS,CAAC;QACnD,KAAK,EAAE,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE;QACnC,OAAO,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE;QAC7B,MAAM,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;KAC3B,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC,gBAAgB,CAAC,MAAM,CAAC;QACpC,IAAI,EAAE;YACJ,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,QAAQ,EAAE,CAAC,IAAI,EAAE,QAAQ,IAAI,CAAC,CAAC,GAAG,CAAC;YACnC,MAAM,EAAE,KAAK,CAAC,MAAM,IAAI,CAAC;YACzB,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,MAAM,EAAE,KAAK,CAAC,MAAM,IAAI,IAAI;YAC5B,WAAW,EAAE,KAAK,CAAC,WAAW,IAAI,IAAI;YACtC,KAAK,EAAE,KAAK,CAAC,KAAK,IAAI,IAAI;YAC1B,eAAe,EAAE,KAAK,CAAC,eAAe,IAAI,IAAI;YAC9C,aAAa,EACX,KAAK,CAAC,eAAe,KAAK,yBAAyB,CAAC,cAAc;gBAChE,CAAC,CAAC,KAAK,CAAC,aAAa;gBACrB,CAAC,CAAC,IAAI;SACX;KACF,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,MAAc,EACd,KAQC;IAED,MAAM,aAAa,CAAC,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;IAC3C,MAAM,IAAI,GAAG,MAAM,qBAAqB,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;IAClE,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,SAAS,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,uBAAuB,EAAE,CAAC,CAAC;IAC/E,CAAC;IAED,MAAM,eAAe,GACnB,KAAK,CAAC,eAAe,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC;IACrF,MAAM,aAAa,GACjB,KAAK,CAAC,aAAa,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC;IAE/E,MAAM,iBAAiB,CAAC,KAAK,CAAC,OAAO,EAAE;QACrC,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,WAAW,EAAE,KAAK,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW;QACnF,KAAK,EAAE,KAAK,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK;QAC3D,eAAe;QACf,aAAa;KACd,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC,gBAAgB,CAAC,MAAM,CAAC;QACpC,KAAK,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,EAAE,EAAE;QACvB,IAAI,EAAE;YACJ,GAAG,CAAC,KAAK,CAAC,KAAK,KAAK,SAAS,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC;YACxD,GAAG,CAAC,KAAK,CAAC,WAAW,KAAK,SAAS,IAAI,EAAE,WAAW,EAAE,KAAK,CAAC,WAAW,EAAE,CAAC;YAC1E,GAAG,CAAC,KAAK,CAAC,MAAM,KAAK,SAAS,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC;YAC3D,GAAG,CAAC,KAAK,CAAC,eAAe,KAAK,SAAS,IAAI;gBACzC,eAAe,EAAE,KAAK,CAAC,eAAe;aACvC,CAAC;YACF,aAAa,EACX,eAAe,KAAK,yBAAyB,CAAC,cAAc;gBAC1D,CAAC,CAAC,aAAa;gBACf,CAAC,CAAC,IAAI;SACX;KACF,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,MAAc,EACd,KAAsC;IAEtC,MAAM,aAAa,CAAC,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;IAC3C,MAAM,IAAI,GAAG,MAAM,qBAAqB,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;IAClE,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,SAAS,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,uBAAuB,EAAE,CAAC,CAAC;IAC/E,CAAC;IACD,MAAM,MAAM,CAAC,gBAAgB,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;IAClE,OAAO,EAAE,EAAE,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC;AAC1B,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAC5C,MAAc,EACd,KAAqE;IAErE,MAAM,aAAa,CAAC,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;IAE3C,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAC7C,MAAM,SAAS,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;IAEnE,MAAM,CAAC,OAAO,EAAE,SAAS,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QAC7C,MAAM,CAAC,gBAAgB,CAAC,QAAQ,CAAC;YAC/B,KAAK,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,EAAE;YAClE,MAAM,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE;SACrB,CAAC;QACF,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC;YACrB,KAAK,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE;YACxD,MAAM,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE;SACrB,CAAC;KACH,CAAC,CAAC;IACH,IAAI,OAAO,CAAC,MAAM,KAAK,OAAO,CAAC,MAAM,IAAI,SAAS,CAAC,MAAM,KAAK,SAAS,CAAC,MAAM,EAAE,CAAC;QAC/E,MAAM,IAAI,SAAS,CAAC;YAClB,IAAI,EAAE,aAAa;YACnB,OAAO,EAAE,wCAAwC;SAClD,CAAC,CAAC;IACL,CAAC;IAED,MAAM,gBAAgB,GAAG,IAAI,GAAG,EAAkB,CAAC;IACnD,MAAM,MAAM,CAAC,YAAY,CACvB,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QACvB,MAAM,IAAI,GAAG,CAAC,gBAAgB,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;QAC5D,gBAAgB,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QAC1C,OAAO,MAAM,CAAC,gBAAgB,CAAC,MAAM,CAAC;YACpC,KAAK,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE;YACtB,IAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE;SAClD,CAAC,CAAC;IACL,CAAC,CAAC,CACH,CAAC;IACF,OAAO,oBAAoB,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;AAC7C,CAAC;AAUD,SAAS,gBAAgB,CACvB,MAAuB,EACvB,gBAA6B;IAE7B,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC;IAC/D,OAAO,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAC3D,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAAC,OAAe,EAAE,SAAiB;IAC7E,MAAM,CAAC,UAAU,EAAE,QAAQ,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QAC/C,oBAAoB,CAAC,OAAO,EAAE,IAAI,CAAC;QACnC,2BAA2B,CAAC,OAAO,EAAE,SAAS,CAAC;KAChD,CAAC,CAAC;IAEH,wDAAwD;IACxD,MAAM,gBAAgB,GAAG,UAAU;SAChC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC;SACvB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,cAAc,CAAC,aAAa,IAAI,CAAC,CAAC,MAAM,CAAC;SACtE,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAgB,CAAC,CAAC;IAClC,MAAM,yBAAyB,GAAG,IAAI,GAAG,CACvC,gBAAgB,CAAC,MAAM;QACrB,CAAC,CAAC,CACE,MAAM,MAAM,CAAC,aAAa,CAAC,QAAQ,CAAC;YAClC,KAAK,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,gBAAgB,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE;YACjE,MAAM,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE;SACrB,CAAC,CACH,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACpB,CAAC,CAAC,EAAE,CACP,CAAC;IACF,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACrC,GAAG,CAAC;QACJ,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,MAAM,CACnB,CAAC,CAAC,EAAE,EAAE,CACJ,CAAC,CAAC,QAAQ,KAAK,cAAc,CAAC,aAAa;YAC3C,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,IAAI,yBAAyB,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAC1D;KACF,CAAC,CAAC,CAAC;IAEJ,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAC9B,QAAQ;SACL,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,wBAAwB,CAAC,QAAQ,CAAC;SAC7D,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,CAC9B,CAAC;IAEF,2EAA2E;IAC3E,2EAA2E;IAC3E,MAAM,YAAY,GAAG;QACnB,GAAG,IAAI,GAAG,CACR,OAAO;aACJ,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC;aACvB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,cAAc,CAAC,SAAS,IAAI,CAAC,CAAC,MAAM,CAAC;aAClE,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAgB,CAAC,CAClC;KACF,CAAC;IACF,MAAM,UAAU,GAAG,YAAY,CAAC,MAAM;QACpC,CAAC,CAAC,MAAM,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC;YAC9B,KAAK,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE;YACnC,MAAM,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,EAAE;SACrE,CAAC;QACJ,CAAC,CAAC,EAAE,CAAC;IACP,MAAM,yBAAyB,GAAG,IAAI,GAAG,CACvC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,EAAE,IAAI,IAAI,CAAC,CAAC,CAC5D,CAAC;IAEF,MAAM,iBAAiB,GAAG,IAAI,GAAG,EAAU,CAAC;IAC5C,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,IAAI,gBAAgB,CAAC,MAAM,EAAE,gBAAgB,CAAC,EAAE,CAAC;YAC/C,iBAAiB,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACnC,CAAC;IACH,CAAC;IAED,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;IACvB,IAAI,cAAc,GAAkB,IAAI,CAAC;IACzC,IAAI,gBAAgB,GAAkB,IAAI,CAAC;IAE3C,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE;QACpC,MAAM,kBAAkB,GAAG,MAAM,CAAC,aAAa;aAC5C,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,cAAc,CAAC;aAC5B,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;QACpD,MAAM,oBAAoB,GAAG,kBAAkB,CAAC,IAAI,CAClD,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,iBAAiB,CAAC,GAAG,CAAC,EAAE,CAAC,CACnC,CAAC;QACF,MAAM,YAAY,GAAG,CAAC,CAAC,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC,QAAQ,GAAG,GAAG,CAAC;QAChE,MAAM,MAAM,GAAG,oBAAoB,IAAI,YAAY,CAAC;QAEpD,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC;QAC/D,MAAM,iBAAiB,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAC7E,MAAM,eAAe,GAAG,QAAQ,CAAC,MAAM;YACrC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,iBAAiB,CAAC,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,GAAG,GAAG,CAAC;YAChE,CAAC,CAAC,iBAAiB,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;gBAChC,CAAC,CAAC,GAAG;gBACL,CAAC,CAAC,CAAC,CAAC;QAER,yEAAyE;QACzE,+BAA+B;QAC/B,IAAI,iBAAiB,GAAG,KAAK,CAAC;QAC9B,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;YACtC,MAAM,SAAS,GAAG,gBAAgB,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAChD,MAAM,UAAU,GACd,CAAC,MAAM,IAAI,CAAC,CAAC,MAAM,CAAC,iBAAiB,IAAI,CAAC,iBAAiB,CAAC,CAAC;YAC/D,IACE,MAAM,CAAC,iBAAiB;gBACxB,IAAI,CAAC,eAAe;gBACpB,CAAC,SAAS;gBACV,CAAC,iBAAiB,EAClB,CAAC;gBACD,iBAAiB,GAAG,IAAI,CAAC;YAC3B,CAAC;YACD,IACE,CAAC,cAAc;gBACf,CAAC,MAAM;gBACP,UAAU;gBACV,IAAI,CAAC,eAAe;gBACpB,CAAC,SAAS,EACV,CAAC;gBACD,cAAc,GAAG,IAAI,CAAC,EAAE,CAAC;gBACzB,gBAAgB,GAAG,MAAM,CAAC,EAAE,CAAC;YAC/B,CAAC;YACD,OAAO;gBACL,GAAG,IAAI;gBACP,SAAS;gBACT,UAAU;gBACV,qBAAqB,EACnB,IAAI,CAAC,QAAQ,KAAK,cAAc,CAAC,SAAS,IAAI,IAAI,CAAC,MAAM;oBACvD,CAAC,CAAC,CAAC,yBAAyB,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC;oBACtD,CAAC,CAAC,IAAI;aACX,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,OAAO;YACL,GAAG,MAAM;YACT,KAAK;YACL,MAAM;YACN,oBAAoB;YACpB,YAAY;YACZ,SAAS,EAAE,iBAAiB,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;YAC3C,aAAa,EAAE,QAAQ,CAAC,MAAM;YAC9B,sBAAsB,EAAE,iBAAiB,CAAC,MAAM;YAChD,eAAe;SAChB,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,OAAO;QACL,OAAO,EAAE,MAAM;QACf,cAAc;QACd,gBAAgB;KACjB,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,SAAiB,EACjB,KAA0C;IAE1C,MAAM,IAAI,GAAG,MAAM,qBAAqB,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;IACtE,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;QACpC,MAAM,IAAI,SAAS,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,uBAAuB,EAAE,CAAC,CAAC;IAC/E,CAAC;IAED,MAAM,KAAK,GAAG,MAAM,sBAAsB,CAAC,KAAK,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;IACrE,MAAM,WAAW,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC,QAAQ,CAAC,CAAC;IACtE,MAAM,SAAS,GAAG,WAAW,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC,CAAC;IACnE,IAAI,CAAC,WAAW,IAAI,CAAC,SAAS,IAAI,WAAW,CAAC,MAAM,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,CAAC;QAC9E,MAAM,IAAI,SAAS,CAAC;YAClB,IAAI,EAAE,WAAW;YACjB,OAAO,EAAE,4BAA4B;SACtC,CAAC,CAAC;IACL,CAAC;IAED,IAAI,IAAI,CAAC,eAAe,KAAK,yBAAyB,CAAC,IAAI,EAAE,CAAC;QAC5D,OAAO,kBAAkB,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC;IAC5D,CAAC;IACD,OAAO,kBAAkB,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,YAAY,CAAC,CAAC;AAC9D,CAAC;AAED,gFAAgF;AAChF,gFAAgF;AAChF,gFAAgF;AAEhF;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,4BAA4B,CAChD,SAAiB,EACjB,YAAoB;IAEpB,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,gBAAgB,CAAC,QAAQ,CAAC;YACnD,KAAK,EAAE;gBACL,QAAQ,EAAE,cAAc,CAAC,UAAU;gBACnC,MAAM,EAAE,YAAY;gBACpB,eAAe,EAAE;oBACf,EAAE,EAAE;wBACF,yBAAyB,CAAC,MAAM;wBAChC,yBAAyB,CAAC,cAAc;qBACzC;iBACF;aACF;SACF,CAAC,CAAC;QACH,IAAI,CAAC,KAAK,CAAC,MAAM;YAAE,OAAO;QAE1B,MAAM,UAAU,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,SAAS,CAAC;YACnD,KAAK,EAAE,EAAE,YAAY,EAAE,SAAS,EAAE;YAClC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE;SACjE,CAAC,CAAC;QACH,IAAI,CAAC,UAAU;YAAE,OAAO;QAExB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,MAAM,QAAQ,GACZ,IAAI,CAAC,eAAe,KAAK,yBAAyB,CAAC,MAAM;gBACvD,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,SAAS;gBACxB,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,QAAQ;oBACrB,UAAU,CAAC,aAAa,IAAI,IAAI;oBAChC,UAAU,CAAC,aAAa,IAAI,CAAC,IAAI,CAAC,aAAa,IAAI,CAAC,CAAC,CAAC;YAC5D,IAAI,QAAQ,EAAE,CAAC;gBACb,MAAM,kBAAkB,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC;YAC3D,CAAC;QACH,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,KAAK,CAAC,2CAA2C,EAAE;YACxD,KAAK;YACL,SAAS;YACT,YAAY;SACb,CAAC,CAAC;IACL,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,2BAA2B,CAC/C,SAAiB,EACjB,WAAmB;IAEnB,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,gBAAgB,CAAC,QAAQ,CAAC;YACnD,KAAK,EAAE;gBACL,QAAQ,EAAE,cAAc,CAAC,SAAS;gBAClC,MAAM,EAAE,WAAW;gBACnB,eAAe,EAAE;oBACf,EAAE,EAAE;wBACF,yBAAyB,CAAC,MAAM;wBAChC,yBAAyB,CAAC,cAAc;qBACzC;iBACF;aACF;SACF,CAAC,CAAC;QACH,IAAI,CAAC,KAAK,CAAC,MAAM;YAAE,OAAO;QAE1B,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,uBAAuB,CAAC,SAAS,CAAC;YAC5D,KAAK,EAAE,EAAE,SAAS,EAAE,QAAQ,EAAE,EAAE,WAAW,EAAE,EAAE;YAC/C,IAAI,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE;SACvB,CAAC,CAAC;QACH,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC;QAE5C,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,MAAM,QAAQ,GACZ,IAAI,CAAC,eAAe,KAAK,yBAAyB,CAAC,MAAM;gBACzD,WAAW,IAAI,CAAC,IAAI,CAAC,aAAa,IAAI,CAAC,CAAC,CAAC;YAC3C,IAAI,QAAQ,EAAE,CAAC;gBACb,MAAM,kBAAkB,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC;YAC3D,CAAC;QACH,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,KAAK,CAAC,0CAA0C,EAAE;YACvD,KAAK;YACL,SAAS;YACT,WAAW;SACZ,CAAC,CAAC;IACL,CAAC;AACH,CAAC","debug_id":"69f3db7b-9e94-5f6e-9e8b-f196705cf116"}
|
|
@@ -12,6 +12,20 @@ export declare function linkParentToStudent(userId: string, input: {
|
|
|
12
12
|
studentUserId: string;
|
|
13
13
|
};
|
|
14
14
|
}>;
|
|
15
|
+
export declare function addParentToStudent(userId: string, input: {
|
|
16
|
+
schoolId: string;
|
|
17
|
+
studentUserId: string;
|
|
18
|
+
parentIdentifier: string;
|
|
19
|
+
}): Promise<{
|
|
20
|
+
link: {
|
|
21
|
+
id: string;
|
|
22
|
+
createdAt: Date;
|
|
23
|
+
updatedAt: Date;
|
|
24
|
+
schoolId: string;
|
|
25
|
+
parentUserId: string;
|
|
26
|
+
studentUserId: string;
|
|
27
|
+
};
|
|
28
|
+
}>;
|
|
15
29
|
export declare function unlinkParentFromStudent(userId: string, input: {
|
|
16
30
|
schoolId: string;
|
|
17
31
|
parentUserId: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parentPortal.d.ts","sourceRoot":"/","sources":["services/parentPortal.ts"],"names":[],"mappings":"AAkCA,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE;;;;;;;;;GAiDzE;AAED,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE;;GAWzE;AAED,wBAAsB,eAAe,CACnC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAA;CAAE;;;;;;;;;;;;;;;;;;;;;;;;;;GA8B3E;AAED,wBAAsB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;;;;;;;;;;;;;;;;;;;GAgDpE;AA0BD,6EAA6E;AAC7E,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IACL,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,EAAE,CAAC,EAAE,IAAI,CAAC;CACX;;;;;;;;;;;;;;;;;;;;GA2DF;AAED;;GAEG;AACH,wBAAsB,cAAc,CAClC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE;;;;;;;;;;;;;;;;;;;;GA8DrE;AAED,uEAAuE;AACvE,wBAAsB,iBAAiB,CACrC,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,EACpB,aAAa,EAAE,MAAM,oBAatB"}
|
|
1
|
+
{"version":3,"file":"parentPortal.d.ts","sourceRoot":"/","sources":["services/parentPortal.ts"],"names":[],"mappings":"AAkCA,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE;;;;;;;;;GAiDzE;AAED,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,gBAAgB,EAAE,MAAM,CAAA;CAAE;;;;;;;;;GAiF7E;AAED,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE;;GAWzE;AAED,wBAAsB,eAAe,CACnC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAA;CAAE;;;;;;;;;;;;;;;;;;;;;;;;;;GA8B3E;AAED,wBAAsB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;;;;;;;;;;;;;;;;;;;GAgDpE;AA0BD,6EAA6E;AAC7E,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IACL,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,EAAE,CAAC,EAAE,IAAI,CAAC;CACX;;;;;;;;;;;;;;;;;;;;GA2DF;AAED;;GAEG;AACH,wBAAsB,cAAc,CAClC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE;;;;;;;;;;;;;;;;;;;;GA8DrE;AAED,uEAAuE;AACvE,wBAAsB,iBAAiB,CACrC,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,EACpB,aAAa,EAAE,MAAM,oBAatB"}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* read-only attendance / grades stubs for linked children.
|
|
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]="c36b5d67-b3e7-5291-a5f4-da0e2f96f1b3")}catch(e){}}();
|
|
7
7
|
import { TRPCError } from "@trpc/server";
|
|
8
8
|
import { prisma } from "../lib/prisma.js";
|
|
9
9
|
import { findActiveSchoolAdminMembership, findActiveSchoolMembership, findSchoolMembership, } from "../models/school.js";
|
|
@@ -70,6 +70,76 @@ export async function linkParentToStudent(userId, input) {
|
|
|
70
70
|
});
|
|
71
71
|
return { link };
|
|
72
72
|
}
|
|
73
|
+
export async function addParentToStudent(userId, input) {
|
|
74
|
+
await requireSchoolAdmin(input.schoolId, userId);
|
|
75
|
+
const studentMembership = await findSchoolMembership(input.schoolId, input.studentUserId);
|
|
76
|
+
if (!studentMembership ||
|
|
77
|
+
studentMembership.status !== "ACTIVE" ||
|
|
78
|
+
studentMembership.role !== "STUDENT") {
|
|
79
|
+
throw new TRPCError({
|
|
80
|
+
code: "BAD_REQUEST",
|
|
81
|
+
message: "studentUserId must be an active STUDENT member",
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
const identifier = input.parentIdentifier.trim();
|
|
85
|
+
const parentUser = await prisma.user.findFirst({
|
|
86
|
+
where: {
|
|
87
|
+
OR: [{ email: identifier }, { username: identifier }],
|
|
88
|
+
},
|
|
89
|
+
select: { id: true },
|
|
90
|
+
});
|
|
91
|
+
if (!parentUser) {
|
|
92
|
+
throw new TRPCError({
|
|
93
|
+
code: "NOT_FOUND",
|
|
94
|
+
message: "No account found with that email or username",
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
if (parentUser.id === input.studentUserId) {
|
|
98
|
+
throw new TRPCError({
|
|
99
|
+
code: "BAD_REQUEST",
|
|
100
|
+
message: "A student cannot be their own parent",
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
const parentMembership = await findSchoolMembership(input.schoolId, parentUser.id);
|
|
104
|
+
if (!parentMembership) {
|
|
105
|
+
await prisma.schoolMembership.create({
|
|
106
|
+
data: {
|
|
107
|
+
schoolId: input.schoolId,
|
|
108
|
+
userId: parentUser.id,
|
|
109
|
+
role: "PARENT",
|
|
110
|
+
status: "ACTIVE",
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
else if (parentMembership.role !== "PARENT") {
|
|
115
|
+
throw new TRPCError({
|
|
116
|
+
code: "BAD_REQUEST",
|
|
117
|
+
message: "That account already belongs to this school with another role",
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
else if (parentMembership.status !== "ACTIVE") {
|
|
121
|
+
throw new TRPCError({
|
|
122
|
+
code: "BAD_REQUEST",
|
|
123
|
+
message: "Parent must be an active school member",
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
const link = await prisma.parentStudentLink.upsert({
|
|
127
|
+
where: {
|
|
128
|
+
schoolId_parentUserId_studentUserId: {
|
|
129
|
+
schoolId: input.schoolId,
|
|
130
|
+
parentUserId: parentUser.id,
|
|
131
|
+
studentUserId: input.studentUserId,
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
update: {},
|
|
135
|
+
create: {
|
|
136
|
+
schoolId: input.schoolId,
|
|
137
|
+
parentUserId: parentUser.id,
|
|
138
|
+
studentUserId: input.studentUserId,
|
|
139
|
+
},
|
|
140
|
+
});
|
|
141
|
+
return { link };
|
|
142
|
+
}
|
|
73
143
|
export async function unlinkParentFromStudent(userId, input) {
|
|
74
144
|
await requireSchoolAdmin(input.schoolId, userId);
|
|
75
145
|
await prisma.parentStudentLink.deleteMany({
|
|
@@ -311,4 +381,4 @@ export async function isParentOfStudent(schoolId, parentUserId, studentUserId) {
|
|
|
311
381
|
return Boolean(link);
|
|
312
382
|
}
|
|
313
383
|
//# sourceMappingURL=parentPortal.js.map
|
|
314
|
-
//# debugId=
|
|
384
|
+
//# debugId=c36b5d67-b3e7-5291-a5f4-da0e2f96f1b3
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parentPortal.js","sources":["services/parentPortal.ts"],"sourceRoot":"/","sourcesContent":["/**\n * C4: Minimal parent portal — link parents to students and expose\n * read-only attendance / grades stubs for linked children.\n */\nimport { TRPCError } from \"@trpc/server\";\nimport { prisma } from \"../lib/prisma.js\";\nimport {\n findActiveSchoolAdminMembership,\n findActiveSchoolMembership,\n findSchoolMembership,\n} from \"../models/school.js\";\n\nasync function requireSchoolAdmin(schoolId: string, userId: string) {\n const membership = await findActiveSchoolAdminMembership(schoolId, userId);\n if (!membership) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Only school admins can perform this action\",\n });\n }\n return membership;\n}\n\nasync function requireParentMembership(schoolId: string, userId: string) {\n const membership = await findActiveSchoolMembership(schoolId, userId);\n if (!membership || membership.role !== \"PARENT\") {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Only parents can access this resource\",\n });\n }\n return membership;\n}\n\nexport async function linkParentToStudent(\n userId: string,\n input: { schoolId: string; parentUserId: string; studentUserId: string }\n) {\n await requireSchoolAdmin(input.schoolId, userId);\n\n const [parentMembership, studentMembership] = await Promise.all([\n findSchoolMembership(input.schoolId, input.parentUserId),\n findSchoolMembership(input.schoolId, input.studentUserId),\n ]);\n\n if (!parentMembership || parentMembership.status !== \"ACTIVE\") {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"Parent must be an active school member\",\n });\n }\n if (parentMembership.role !== \"PARENT\") {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"parentUserId must have the PARENT role\",\n });\n }\n if (\n !studentMembership ||\n studentMembership.status !== \"ACTIVE\" ||\n studentMembership.role !== \"STUDENT\"\n ) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"studentUserId must be an active STUDENT member\",\n });\n }\n\n const link = await prisma.parentStudentLink.upsert({\n where: {\n schoolId_parentUserId_studentUserId: {\n schoolId: input.schoolId,\n parentUserId: input.parentUserId,\n studentUserId: input.studentUserId,\n },\n },\n update: {},\n create: {\n schoolId: input.schoolId,\n parentUserId: input.parentUserId,\n studentUserId: input.studentUserId,\n },\n });\n\n return { link };\n}\n\nexport async function unlinkParentFromStudent(\n userId: string,\n input: { schoolId: string; parentUserId: string; studentUserId: string }\n) {\n await requireSchoolAdmin(input.schoolId, userId);\n await prisma.parentStudentLink.deleteMany({\n where: {\n schoolId: input.schoolId,\n parentUserId: input.parentUserId,\n studentUserId: input.studentUserId,\n },\n });\n return { ok: true as const };\n}\n\nexport async function listParentLinks(\n userId: string,\n input: { schoolId: string; parentUserId?: string; studentUserId?: string }\n) {\n await requireSchoolAdmin(input.schoolId, userId);\n const links = await prisma.parentStudentLink.findMany({\n where: {\n schoolId: input.schoolId,\n ...(input.parentUserId ? { parentUserId: input.parentUserId } : {}),\n ...(input.studentUserId ? { studentUserId: input.studentUserId } : {}),\n },\n include: {\n parent: {\n select: {\n id: true,\n username: true,\n email: true,\n profile: { select: { displayName: true } },\n },\n },\n student: {\n select: {\n id: true,\n username: true,\n email: true,\n profile: { select: { displayName: true } },\n },\n },\n },\n orderBy: { createdAt: \"desc\" },\n });\n return { links };\n}\n\nexport async function listMyChildren(userId: string, schoolId: string) {\n await requireParentMembership(schoolId, userId);\n const links = await prisma.parentStudentLink.findMany({\n where: { schoolId, parentUserId: userId },\n include: {\n student: {\n select: {\n id: true,\n username: true,\n email: true,\n profile: {\n select: {\n displayName: true,\n profilePicture: true,\n profilePictureThumbnail: true,\n },\n },\n studentIn: {\n where: { schoolId },\n select: {\n id: true,\n name: true,\n subject: true,\n section: true,\n color: true,\n },\n },\n },\n },\n },\n orderBy: { createdAt: \"asc\" },\n });\n\n return {\n children: links.map((link) => ({\n linkId: link.id,\n student: {\n id: link.student.id,\n username: link.student.username,\n email: link.student.email,\n displayName: link.student.profile?.displayName ?? null,\n profilePicture: link.student.profile?.profilePicture ?? null,\n profilePictureThumbnail:\n link.student.profile?.profilePictureThumbnail ?? null,\n classes: link.student.studentIn,\n },\n })),\n };\n}\n\nasync function assertParentOfStudent(\n schoolId: string,\n parentUserId: string,\n studentUserId: string\n) {\n await requireParentMembership(schoolId, parentUserId);\n const link = await prisma.parentStudentLink.findUnique({\n where: {\n schoolId_parentUserId_studentUserId: {\n schoolId,\n parentUserId,\n studentUserId,\n },\n },\n });\n if (!link) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"You are not linked to this student\",\n });\n }\n return link;\n}\n\n/** Read-only attendance view for a linked child (reads AttendanceRecord). */\nexport async function getChildAttendance(\n userId: string,\n input: {\n schoolId: string;\n studentUserId: string;\n classId?: string;\n from?: Date;\n to?: Date;\n }\n) {\n await assertParentOfStudent(input.schoolId, userId, input.studentUserId);\n\n const classes = await prisma.class.findMany({\n where: {\n schoolId: input.schoolId,\n students: { some: { id: input.studentUserId } },\n ...(input.classId ? { id: input.classId } : {}),\n },\n select: { id: true, name: true, subject: true, section: true },\n });\n const classIds = classes.map((c) => c.id);\n\n const attendanceRecords = await prisma.attendanceRecord.findMany({\n where: {\n studentId: input.studentUserId,\n attendance: {\n classId: { in: classIds },\n ...(input.from || input.to\n ? {\n date: {\n ...(input.from ? { gte: input.from } : {}),\n ...(input.to ? { lte: input.to } : {}),\n },\n }\n : {}),\n },\n },\n select: {\n status: true,\n attendance: { select: { id: true, date: true, classId: true } },\n },\n orderBy: { attendance: { date: \"desc\" } },\n take: 200,\n });\n\n const classMap = new Map(classes.map((c) => [c.id, c]));\n const records = attendanceRecords.map((row) => ({\n attendanceId: row.attendance.id,\n date: row.attendance.date,\n classId: row.attendance.classId,\n class: classMap.get(row.attendance.classId) ?? null,\n status: row.status,\n }));\n\n const summary = records.reduce(\n (acc, r) => {\n acc.total += 1;\n if (r.status === \"PRESENT\") acc.present += 1;\n else if (r.status === \"LATE\") acc.late += 1;\n else if (r.status === \"EXCUSED\") acc.excused += 1;\n else acc.absent += 1;\n return acc;\n },\n { present: 0, late: 0, absent: 0, excused: 0, total: 0 }\n );\n\n return { summary, records };\n}\n\n/**\n * Read-only grades stub — returns assignment score rows for the linked child.\n */\nexport async function getChildGrades(\n userId: string,\n input: { schoolId: string; studentUserId: string; classId?: string }\n) {\n await assertParentOfStudent(input.schoolId, userId, input.studentUserId);\n\n const classes = await prisma.class.findMany({\n where: {\n schoolId: input.schoolId,\n students: { some: { id: input.studentUserId } },\n ...(input.classId ? { id: input.classId } : {}),\n },\n select: {\n id: true,\n name: true,\n subject: true,\n section: true,\n assignments: {\n select: {\n id: true,\n title: true,\n type: true,\n dueDate: true,\n maxGrade: true,\n submissions: {\n where: { studentId: input.studentUserId },\n select: {\n id: true,\n gradeReceived: true,\n returned: true,\n submittedAt: true,\n },\n take: 1,\n },\n },\n orderBy: { dueDate: \"desc\" },\n take: 100,\n },\n },\n });\n\n return {\n classes: classes.map((cls) => ({\n id: cls.id,\n name: cls.name,\n subject: cls.subject,\n section: cls.section,\n assignments: cls.assignments.map((a) => ({\n id: a.id,\n title: a.title,\n type: a.type,\n dueDate: a.dueDate,\n maxGrade: a.maxGrade,\n submission: a.submissions[0]\n ? {\n id: a.submissions[0].id,\n gradeReceived: a.submissions[0].gradeReceived,\n returned: a.submissions[0].returned,\n submittedAt: a.submissions[0].submittedAt,\n }\n : null,\n })),\n })),\n };\n}\n\n/** Helper for other services: is this user a parent of the student? */\nexport async function isParentOfStudent(\n schoolId: string,\n parentUserId: string,\n studentUserId: string\n) {\n const link = await prisma.parentStudentLink.findUnique({\n where: {\n schoolId_parentUserId_studentUserId: {\n schoolId,\n parentUserId,\n studentUserId,\n },\n },\n select: { id: true },\n });\n return Boolean(link);\n}\n"],"names":[],"mappings":"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","debug_id":"e0a84844-f117-5700-89b0-1d460b45e236"}
|
|
1
|
+
{"version":3,"file":"parentPortal.js","sources":["services/parentPortal.ts"],"sourceRoot":"/","sourcesContent":["/**\n * C4: Minimal parent portal — link parents to students and expose\n * read-only attendance / grades stubs for linked children.\n */\nimport { TRPCError } from \"@trpc/server\";\nimport { prisma } from \"../lib/prisma.js\";\nimport {\n findActiveSchoolAdminMembership,\n findActiveSchoolMembership,\n findSchoolMembership,\n} from \"../models/school.js\";\n\nasync function requireSchoolAdmin(schoolId: string, userId: string) {\n const membership = await findActiveSchoolAdminMembership(schoolId, userId);\n if (!membership) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Only school admins can perform this action\",\n });\n }\n return membership;\n}\n\nasync function requireParentMembership(schoolId: string, userId: string) {\n const membership = await findActiveSchoolMembership(schoolId, userId);\n if (!membership || membership.role !== \"PARENT\") {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Only parents can access this resource\",\n });\n }\n return membership;\n}\n\nexport async function linkParentToStudent(\n userId: string,\n input: { schoolId: string; parentUserId: string; studentUserId: string }\n) {\n await requireSchoolAdmin(input.schoolId, userId);\n\n const [parentMembership, studentMembership] = await Promise.all([\n findSchoolMembership(input.schoolId, input.parentUserId),\n findSchoolMembership(input.schoolId, input.studentUserId),\n ]);\n\n if (!parentMembership || parentMembership.status !== \"ACTIVE\") {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"Parent must be an active school member\",\n });\n }\n if (parentMembership.role !== \"PARENT\") {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"parentUserId must have the PARENT role\",\n });\n }\n if (\n !studentMembership ||\n studentMembership.status !== \"ACTIVE\" ||\n studentMembership.role !== \"STUDENT\"\n ) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"studentUserId must be an active STUDENT member\",\n });\n }\n\n const link = await prisma.parentStudentLink.upsert({\n where: {\n schoolId_parentUserId_studentUserId: {\n schoolId: input.schoolId,\n parentUserId: input.parentUserId,\n studentUserId: input.studentUserId,\n },\n },\n update: {},\n create: {\n schoolId: input.schoolId,\n parentUserId: input.parentUserId,\n studentUserId: input.studentUserId,\n },\n });\n\n return { link };\n}\n\nexport async function addParentToStudent(\n userId: string,\n input: { schoolId: string; studentUserId: string; parentIdentifier: string }\n) {\n await requireSchoolAdmin(input.schoolId, userId);\n\n const studentMembership = await findSchoolMembership(\n input.schoolId,\n input.studentUserId\n );\n if (\n !studentMembership ||\n studentMembership.status !== \"ACTIVE\" ||\n studentMembership.role !== \"STUDENT\"\n ) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"studentUserId must be an active STUDENT member\",\n });\n }\n\n const identifier = input.parentIdentifier.trim();\n const parentUser = await prisma.user.findFirst({\n where: {\n OR: [{ email: identifier }, { username: identifier }],\n },\n select: { id: true },\n });\n if (!parentUser) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"No account found with that email or username\",\n });\n }\n if (parentUser.id === input.studentUserId) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"A student cannot be their own parent\",\n });\n }\n\n const parentMembership = await findSchoolMembership(\n input.schoolId,\n parentUser.id\n );\n if (!parentMembership) {\n await prisma.schoolMembership.create({\n data: {\n schoolId: input.schoolId,\n userId: parentUser.id,\n role: \"PARENT\",\n status: \"ACTIVE\",\n },\n });\n } else if (parentMembership.role !== \"PARENT\") {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"That account already belongs to this school with another role\",\n });\n } else if (parentMembership.status !== \"ACTIVE\") {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"Parent must be an active school member\",\n });\n }\n\n const link = await prisma.parentStudentLink.upsert({\n where: {\n schoolId_parentUserId_studentUserId: {\n schoolId: input.schoolId,\n parentUserId: parentUser.id,\n studentUserId: input.studentUserId,\n },\n },\n update: {},\n create: {\n schoolId: input.schoolId,\n parentUserId: parentUser.id,\n studentUserId: input.studentUserId,\n },\n });\n\n return { link };\n}\n\nexport async function unlinkParentFromStudent(\n userId: string,\n input: { schoolId: string; parentUserId: string; studentUserId: string }\n) {\n await requireSchoolAdmin(input.schoolId, userId);\n await prisma.parentStudentLink.deleteMany({\n where: {\n schoolId: input.schoolId,\n parentUserId: input.parentUserId,\n studentUserId: input.studentUserId,\n },\n });\n return { ok: true as const };\n}\n\nexport async function listParentLinks(\n userId: string,\n input: { schoolId: string; parentUserId?: string; studentUserId?: string }\n) {\n await requireSchoolAdmin(input.schoolId, userId);\n const links = await prisma.parentStudentLink.findMany({\n where: {\n schoolId: input.schoolId,\n ...(input.parentUserId ? { parentUserId: input.parentUserId } : {}),\n ...(input.studentUserId ? { studentUserId: input.studentUserId } : {}),\n },\n include: {\n parent: {\n select: {\n id: true,\n username: true,\n email: true,\n profile: { select: { displayName: true } },\n },\n },\n student: {\n select: {\n id: true,\n username: true,\n email: true,\n profile: { select: { displayName: true } },\n },\n },\n },\n orderBy: { createdAt: \"desc\" },\n });\n return { links };\n}\n\nexport async function listMyChildren(userId: string, schoolId: string) {\n await requireParentMembership(schoolId, userId);\n const links = await prisma.parentStudentLink.findMany({\n where: { schoolId, parentUserId: userId },\n include: {\n student: {\n select: {\n id: true,\n username: true,\n email: true,\n profile: {\n select: {\n displayName: true,\n profilePicture: true,\n profilePictureThumbnail: true,\n },\n },\n studentIn: {\n where: { schoolId },\n select: {\n id: true,\n name: true,\n subject: true,\n section: true,\n color: true,\n },\n },\n },\n },\n },\n orderBy: { createdAt: \"asc\" },\n });\n\n return {\n children: links.map((link) => ({\n linkId: link.id,\n student: {\n id: link.student.id,\n username: link.student.username,\n email: link.student.email,\n displayName: link.student.profile?.displayName ?? null,\n profilePicture: link.student.profile?.profilePicture ?? null,\n profilePictureThumbnail:\n link.student.profile?.profilePictureThumbnail ?? null,\n classes: link.student.studentIn,\n },\n })),\n };\n}\n\nasync function assertParentOfStudent(\n schoolId: string,\n parentUserId: string,\n studentUserId: string\n) {\n await requireParentMembership(schoolId, parentUserId);\n const link = await prisma.parentStudentLink.findUnique({\n where: {\n schoolId_parentUserId_studentUserId: {\n schoolId,\n parentUserId,\n studentUserId,\n },\n },\n });\n if (!link) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"You are not linked to this student\",\n });\n }\n return link;\n}\n\n/** Read-only attendance view for a linked child (reads AttendanceRecord). */\nexport async function getChildAttendance(\n userId: string,\n input: {\n schoolId: string;\n studentUserId: string;\n classId?: string;\n from?: Date;\n to?: Date;\n }\n) {\n await assertParentOfStudent(input.schoolId, userId, input.studentUserId);\n\n const classes = await prisma.class.findMany({\n where: {\n schoolId: input.schoolId,\n students: { some: { id: input.studentUserId } },\n ...(input.classId ? { id: input.classId } : {}),\n },\n select: { id: true, name: true, subject: true, section: true },\n });\n const classIds = classes.map((c) => c.id);\n\n const attendanceRecords = await prisma.attendanceRecord.findMany({\n where: {\n studentId: input.studentUserId,\n attendance: {\n classId: { in: classIds },\n ...(input.from || input.to\n ? {\n date: {\n ...(input.from ? { gte: input.from } : {}),\n ...(input.to ? { lte: input.to } : {}),\n },\n }\n : {}),\n },\n },\n select: {\n status: true,\n attendance: { select: { id: true, date: true, classId: true } },\n },\n orderBy: { attendance: { date: \"desc\" } },\n take: 200,\n });\n\n const classMap = new Map(classes.map((c) => [c.id, c]));\n const records = attendanceRecords.map((row) => ({\n attendanceId: row.attendance.id,\n date: row.attendance.date,\n classId: row.attendance.classId,\n class: classMap.get(row.attendance.classId) ?? null,\n status: row.status,\n }));\n\n const summary = records.reduce(\n (acc, r) => {\n acc.total += 1;\n if (r.status === \"PRESENT\") acc.present += 1;\n else if (r.status === \"LATE\") acc.late += 1;\n else if (r.status === \"EXCUSED\") acc.excused += 1;\n else acc.absent += 1;\n return acc;\n },\n { present: 0, late: 0, absent: 0, excused: 0, total: 0 }\n );\n\n return { summary, records };\n}\n\n/**\n * Read-only grades stub — returns assignment score rows for the linked child.\n */\nexport async function getChildGrades(\n userId: string,\n input: { schoolId: string; studentUserId: string; classId?: string }\n) {\n await assertParentOfStudent(input.schoolId, userId, input.studentUserId);\n\n const classes = await prisma.class.findMany({\n where: {\n schoolId: input.schoolId,\n students: { some: { id: input.studentUserId } },\n ...(input.classId ? { id: input.classId } : {}),\n },\n select: {\n id: true,\n name: true,\n subject: true,\n section: true,\n assignments: {\n select: {\n id: true,\n title: true,\n type: true,\n dueDate: true,\n maxGrade: true,\n submissions: {\n where: { studentId: input.studentUserId },\n select: {\n id: true,\n gradeReceived: true,\n returned: true,\n submittedAt: true,\n },\n take: 1,\n },\n },\n orderBy: { dueDate: \"desc\" },\n take: 100,\n },\n },\n });\n\n return {\n classes: classes.map((cls) => ({\n id: cls.id,\n name: cls.name,\n subject: cls.subject,\n section: cls.section,\n assignments: cls.assignments.map((a) => ({\n id: a.id,\n title: a.title,\n type: a.type,\n dueDate: a.dueDate,\n maxGrade: a.maxGrade,\n submission: a.submissions[0]\n ? {\n id: a.submissions[0].id,\n gradeReceived: a.submissions[0].gradeReceived,\n returned: a.submissions[0].returned,\n submittedAt: a.submissions[0].submittedAt,\n }\n : null,\n })),\n })),\n };\n}\n\n/** Helper for other services: is this user a parent of the student? */\nexport async function isParentOfStudent(\n schoolId: string,\n parentUserId: string,\n studentUserId: string\n) {\n const link = await prisma.parentStudentLink.findUnique({\n where: {\n schoolId_parentUserId_studentUserId: {\n schoolId,\n parentUserId,\n studentUserId,\n },\n },\n select: { id: true },\n });\n return Boolean(link);\n}\n"],"names":[],"mappings":"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","debug_id":"c36b5d67-b3e7-5291-a5f4-da0e2f96f1b3"}
|
|
@@ -180,6 +180,56 @@ export declare function dismissStudentRecommendation(viewerId: string, input: {
|
|
|
180
180
|
}): Promise<{
|
|
181
181
|
success: boolean;
|
|
182
182
|
}>;
|
|
183
|
+
declare const followUpAssignmentSchema: z.ZodObject<{
|
|
184
|
+
title: z.ZodString;
|
|
185
|
+
instructions: z.ZodString;
|
|
186
|
+
}, "strip", z.ZodTypeAny, {
|
|
187
|
+
title: string;
|
|
188
|
+
instructions: string;
|
|
189
|
+
}, {
|
|
190
|
+
title: string;
|
|
191
|
+
instructions: string;
|
|
192
|
+
}>;
|
|
193
|
+
export type FollowUpAssignmentDraft = z.infer<typeof followUpAssignmentSchema>;
|
|
194
|
+
export declare function generateFollowUpAssignment(viewerId: string, input: {
|
|
195
|
+
classId: string;
|
|
196
|
+
studentId: string;
|
|
197
|
+
submissionId: string;
|
|
198
|
+
prompt: string;
|
|
199
|
+
dueDate?: string;
|
|
200
|
+
}): Promise<{
|
|
201
|
+
assignment: {
|
|
202
|
+
type: import(".prisma/client").$Enums.AssignmentType;
|
|
203
|
+
class: {
|
|
204
|
+
id: string;
|
|
205
|
+
name: string;
|
|
206
|
+
};
|
|
207
|
+
section: {
|
|
208
|
+
id: string;
|
|
209
|
+
name: string;
|
|
210
|
+
} | null;
|
|
211
|
+
id: string;
|
|
212
|
+
title: string;
|
|
213
|
+
dueDate: Date;
|
|
214
|
+
maxGrade: number | null;
|
|
215
|
+
attachments: {
|
|
216
|
+
type: string;
|
|
217
|
+
id: string;
|
|
218
|
+
name: string;
|
|
219
|
+
}[];
|
|
220
|
+
instructions: string;
|
|
221
|
+
graded: boolean;
|
|
222
|
+
weight: number;
|
|
223
|
+
teacher: {
|
|
224
|
+
id: string;
|
|
225
|
+
username: string;
|
|
226
|
+
};
|
|
227
|
+
};
|
|
228
|
+
draft: {
|
|
229
|
+
title: string;
|
|
230
|
+
instructions: string;
|
|
231
|
+
};
|
|
232
|
+
}>;
|
|
183
233
|
export declare function getStudentProgressInsights(viewerId: string, classId: string, studentId: string): Promise<{
|
|
184
234
|
summary: string;
|
|
185
235
|
strengths: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"studentProgress.d.ts","sourceRoot":"/","sources":["services/studentProgress.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"studentProgress.d.ts","sourceRoot":"/","sources":["services/studentProgress.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAWxB,KAAK,mBAAmB,GAAG;IACzB,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAgCF,QAAA,MAAM,6BAA6B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAKjC,CAAC;AAEH,QAAA,MAAM,6BAA6B;;;;;;;;;;;;;;;EAKjC,CAAC;AAEH,QAAA,MAAM,0BAA0B;;;;;;EAE9B,CAAC;AAEH,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAC3C,OAAO,6BAA6B,CACrC,CAAC;AACF,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAC3C,OAAO,6BAA6B,CACrC,CAAC;AACF,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAE9E,KAAK,kBAAkB,GAAG,SAAS,GAAG,WAAW,GAAG,mBAAmB,CAAC;AAwXxE,wBAAsB,iCAAiC,CACrD,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsBlB;AAED,wBAAsB,+BAA+B,CACnD,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2FhB;AAED,wBAAsB,4BAA4B,CAChD,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE;IACL,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;CACtB;;GAqCF;AAED,QAAA,MAAM,wBAAwB;;;;;;;;;EAG5B,CAAC;AAEH,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAE/E,wBAAsB,0BAA0B,CAC9C,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE;IACL,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqHF;AAyFD,wBAAsB,0BAA0B,CAC9C,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM;;;;;;;;;;;GAgDlB;AAED,wBAAsB,0BAA0B,CAC9C,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM;;;;;GA2ClB;AAED,wBAAsB,uBAAuB,CAC3C,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM;;GA2ClB;AAED,wBAAsB,wBAAwB,CAC5C,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE;IACL,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,mBAAmB,EAAE,CAAC;CACjC;;;GAkGF"}
|
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
* Student progress service - assignment recommendations and progress chat.
|
|
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]="95393c1d-f8f7-5a00-adf1-4b143ba57f36")}catch(e){}}();
|
|
6
6
|
import { TRPCError } from "@trpc/server";
|
|
7
7
|
import { z } from "zod";
|
|
8
8
|
import { prisma } from "../lib/prisma.js";
|
|
9
9
|
import { isTeacherInClass } from "../models/class.js";
|
|
10
|
+
import { createAssignmentRecord } from "./assignment.js";
|
|
10
11
|
import { inference } from "../utils/inference.js";
|
|
11
12
|
import { logger } from "../utils/logger.js";
|
|
12
13
|
import { getStudentProgressInferenceCacheHit, setStudentProgressInferenceCache, } from "./studentProgressInferenceCache.js";
|
|
@@ -415,6 +416,110 @@ export async function dismissStudentRecommendation(viewerId, input) {
|
|
|
415
416
|
});
|
|
416
417
|
return { success: true };
|
|
417
418
|
}
|
|
419
|
+
const followUpAssignmentSchema = z.object({
|
|
420
|
+
title: z.string().min(1).max(200),
|
|
421
|
+
instructions: z.string().min(1),
|
|
422
|
+
});
|
|
423
|
+
export async function generateFollowUpAssignment(viewerId, input) {
|
|
424
|
+
const teacherInClass = await isTeacherInClass(input.classId, viewerId);
|
|
425
|
+
if (!teacherInClass) {
|
|
426
|
+
throw new TRPCError({
|
|
427
|
+
code: "UNAUTHORIZED",
|
|
428
|
+
message: "Only teachers can generate follow-up assignments",
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
const submission = await prisma.submission.findFirst({
|
|
432
|
+
where: {
|
|
433
|
+
id: input.submissionId,
|
|
434
|
+
studentId: input.studentId,
|
|
435
|
+
assignment: { classId: input.classId },
|
|
436
|
+
},
|
|
437
|
+
select: {
|
|
438
|
+
id: true,
|
|
439
|
+
gradeReceived: true,
|
|
440
|
+
teacherComments: true,
|
|
441
|
+
student: {
|
|
442
|
+
select: {
|
|
443
|
+
id: true,
|
|
444
|
+
username: true,
|
|
445
|
+
profile: { select: { displayName: true } },
|
|
446
|
+
},
|
|
447
|
+
},
|
|
448
|
+
assignment: {
|
|
449
|
+
select: {
|
|
450
|
+
id: true,
|
|
451
|
+
title: true,
|
|
452
|
+
instructions: true,
|
|
453
|
+
type: true,
|
|
454
|
+
maxGrade: true,
|
|
455
|
+
weight: true,
|
|
456
|
+
section: { select: { id: true, name: true } },
|
|
457
|
+
},
|
|
458
|
+
},
|
|
459
|
+
},
|
|
460
|
+
});
|
|
461
|
+
if (!submission) {
|
|
462
|
+
throw new TRPCError({
|
|
463
|
+
code: "NOT_FOUND",
|
|
464
|
+
message: "Submission not found for this student in this class",
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
const displayName = submission.student.profile?.displayName ?? submission.student.username;
|
|
468
|
+
const percentage = submission.gradeReceived != null && submission.assignment.maxGrade
|
|
469
|
+
? Math.round((submission.gradeReceived / submission.assignment.maxGrade) * 1000) / 10
|
|
470
|
+
: null;
|
|
471
|
+
const draft = await inference([
|
|
472
|
+
{
|
|
473
|
+
role: "system",
|
|
474
|
+
content: "You are an experienced teacher creating a targeted follow-up assignment for one student. Ground the task in the source assignment and the teacher's request. Do not fabricate material beyond the source assignment's topic. Return a concise title and clear, student-facing instructions.",
|
|
475
|
+
},
|
|
476
|
+
{
|
|
477
|
+
role: "user",
|
|
478
|
+
content: JSON.stringify({
|
|
479
|
+
student: { displayName },
|
|
480
|
+
sourceAssignment: {
|
|
481
|
+
title: submission.assignment.title,
|
|
482
|
+
instructions: submission.assignment.instructions,
|
|
483
|
+
type: submission.assignment.type,
|
|
484
|
+
maxGrade: submission.assignment.maxGrade,
|
|
485
|
+
section: submission.assignment.section?.name ?? null,
|
|
486
|
+
},
|
|
487
|
+
submission: {
|
|
488
|
+
gradeReceived: submission.gradeReceived,
|
|
489
|
+
percentage,
|
|
490
|
+
teacherFeedback: submission.teacherComments,
|
|
491
|
+
},
|
|
492
|
+
}),
|
|
493
|
+
},
|
|
494
|
+
{ role: "user", content: input.prompt },
|
|
495
|
+
], followUpAssignmentSchema, {
|
|
496
|
+
reason: "student_progress_follow_up_assignment",
|
|
497
|
+
metadata: {
|
|
498
|
+
viewerId,
|
|
499
|
+
classId: input.classId,
|
|
500
|
+
studentId: input.studentId,
|
|
501
|
+
submissionId: input.submissionId,
|
|
502
|
+
},
|
|
503
|
+
attribution: { userId: viewerId, classId: input.classId },
|
|
504
|
+
});
|
|
505
|
+
const dueDate = input.dueDate ??
|
|
506
|
+
new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString();
|
|
507
|
+
const assignment = await createAssignmentRecord(viewerId, {
|
|
508
|
+
classId: input.classId,
|
|
509
|
+
title: draft.title,
|
|
510
|
+
instructions: draft.instructions,
|
|
511
|
+
dueDate,
|
|
512
|
+
studentIds: [input.studentId],
|
|
513
|
+
maxGrade: submission.assignment.maxGrade ?? 100,
|
|
514
|
+
graded: true,
|
|
515
|
+
weight: submission.assignment.weight || 1,
|
|
516
|
+
sectionId: submission.assignment.section?.id ?? undefined,
|
|
517
|
+
type: normalizeAssignmentType(submission.assignment.type),
|
|
518
|
+
acceptExtendedResponse: true,
|
|
519
|
+
sourceAssignmentId: submission.assignment.id,
|
|
520
|
+
});
|
|
521
|
+
return { assignment, draft };
|
|
522
|
+
}
|
|
418
523
|
function buildProgressSummary(submissions) {
|
|
419
524
|
return submissions.map((submission) => ({
|
|
420
525
|
title: submission.assignment.title,
|
|
@@ -598,4 +703,4 @@ export async function chatAboutStudentProgress(viewerId, input) {
|
|
|
598
703
|
}
|
|
599
704
|
}
|
|
600
705
|
//# sourceMappingURL=studentProgress.js.map
|
|
601
|
-
//# debugId=
|
|
706
|
+
//# debugId=95393c1d-f8f7-5a00-adf1-4b143ba57f36
|