@studious-lms/server 1.24.0 → 1.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/data/curriculum/nepal-cdc-g4-g5/subjects/english.json +4 -4
- package/data/curriculum/nepal-cdc-g4-g5/subjects/mathematics.json +1 -1
- package/data/curriculum/nepal-cdc-g4-g5/subjects/science-technology.json +4 -4
- package/data/curriculum/nepal-cdc-g4-g5/subjects/social-studies.json +4 -4
- package/dist/models/attendance.d.ts.map +1 -1
- package/dist/models/attendance.js +17 -14
- package/dist/models/attendance.js.map +1 -1
- package/dist/models/marketplace.d.ts +2 -0
- package/dist/models/marketplace.d.ts.map +1 -1
- package/dist/models/marketplace.js +3 -2
- package/dist/models/marketplace.js.map +1 -1
- package/dist/models/moduleItem.d.ts +5 -1
- package/dist/models/moduleItem.d.ts.map +1 -1
- package/dist/models/moduleItem.js +19 -4
- package/dist/models/moduleItem.js.map +1 -1
- package/dist/routers/_app.d.ts +8 -6
- package/dist/routers/_app.d.ts.map +1 -1
- package/dist/routers/fees.d.ts +1 -1
- package/dist/routers/moduleItem.d.ts.map +1 -1
- package/dist/routers/moduleItem.js +7 -6
- package/dist/routers/moduleItem.js.map +1 -1
- package/dist/routers/parent.d.ts +2 -1
- package/dist/routers/parent.d.ts.map +1 -1
- package/dist/routers/school.d.ts +1 -1
- package/dist/routers/school.d.ts.map +1 -1
- package/dist/routers/school.js +14 -4
- package/dist/routers/school.js.map +1 -1
- package/dist/services/academicCalendar.d.ts.map +1 -1
- package/dist/services/academicCalendar.js +30 -2
- package/dist/services/academicCalendar.js.map +1 -1
- package/dist/services/attendance.d.ts +6 -2
- package/dist/services/attendance.d.ts.map +1 -1
- package/dist/services/attendance.js +8 -4
- package/dist/services/attendance.js.map +1 -1
- package/dist/services/class.d.ts +14 -9
- package/dist/services/class.d.ts.map +1 -1
- package/dist/services/class.js +4 -4
- package/dist/services/class.js.map +1 -1
- package/dist/services/fees.d.ts.map +1 -1
- package/dist/services/fees.js +24 -4
- package/dist/services/fees.js.map +1 -1
- package/dist/services/feesAuthz.d.ts +2 -5
- package/dist/services/feesAuthz.d.ts.map +1 -1
- package/dist/services/feesAuthz.js +5 -8
- package/dist/services/feesAuthz.js.map +1 -1
- package/dist/services/marketplace.d.ts.map +1 -1
- package/dist/services/marketplace.js +8 -5
- package/dist/services/marketplace.js.map +1 -1
- package/dist/services/moduleItem.d.ts.map +1 -1
- package/dist/services/moduleItem.js +23 -7
- package/dist/services/moduleItem.js.map +1 -1
- package/dist/services/parentPortal.d.ts +3 -5
- package/dist/services/parentPortal.d.ts.map +1 -1
- package/dist/services/parentPortal.js +29 -43
- package/dist/services/parentPortal.js.map +1 -1
- package/dist/services/school.d.ts.map +1 -1
- package/dist/services/school.js +37 -13
- package/dist/services/school.js.map +1 -1
- package/dist/services/studentImport.d.ts.map +1 -1
- package/dist/services/studentImport.js +38 -25
- package/dist/services/studentImport.js.map +1 -1
- package/dist/services/transcript.d.ts +2 -3
- package/dist/services/transcript.d.ts.map +1 -1
- package/dist/services/transcript.js +13 -25
- package/dist/services/transcript.js.map +1 -1
- package/package.json +1 -1
- package/scripts/import-nepal-cdc-curriculum.ts +29 -6
- package/src/models/attendance.ts +10 -12
- package/src/models/marketplace.ts +3 -0
- package/src/models/moduleItem.ts +20 -2
- package/src/routers/moduleItem.ts +6 -4
- package/src/routers/school.ts +16 -6
- package/src/services/academicCalendar.ts +28 -0
- package/src/services/attendance.ts +7 -3
- package/src/services/class.ts +2 -2
- package/src/services/fees.ts +25 -2
- package/src/services/feesAuthz.ts +3 -7
- package/src/services/marketplace.ts +8 -3
- package/src/services/moduleItem.ts +28 -4
- package/src/services/parentPortal.ts +26 -39
- package/src/services/school.ts +37 -12
- package/src/services/studentImport.ts +37 -25
- package/src/services/transcript.ts +11 -23
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"academicCalendar.js","sources":["services/academicCalendar.ts"],"sourceRoot":"/","sourcesContent":["/**\n * Academic year / term service (C1) + student promotion (C2).\n */\nimport { TRPCError } from \"@trpc/server\";\nimport { prisma } from \"../lib/prisma.js\";\nimport {\n createAcademicTerm,\n createAcademicYear,\n deleteAcademicTerm,\n deleteAcademicYear,\n getAcademicYearById,\n getCurrentAcademicYear,\n listAcademicYears,\n updateAcademicTerm,\n updateAcademicYear,\n} from \"../models/academicCalendar.js\";\nimport {\n findActiveSchoolAdminMembership,\n findSchoolClass,\n listActiveSchoolMemberships,\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\nexport async function listYears(userId: string, schoolId: string) {\n await requireSchoolAdmin(schoolId, userId);\n return { years: await listAcademicYears(schoolId) };\n}\n\nexport async function getCurrentYear(userId: string, schoolId: string) {\n await requireSchoolAdmin(schoolId, userId);\n return { year: await getCurrentAcademicYear(schoolId) };\n}\n\nexport async function createYear(\n userId: string,\n input: {\n schoolId: string;\n name: string;\n startDate: Date;\n endDate: Date;\n isCurrent?: boolean;\n }\n) {\n await requireSchoolAdmin(input.schoolId, userId);\n if (input.endDate < input.startDate) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"endDate must be on or after startDate\",\n });\n }\n try {\n const year = await createAcademicYear(input);\n return { year };\n } catch (err: unknown) {\n if (\n err &&\n typeof err === \"object\" &&\n \"code\" in err &&\n (err as { code: string }).code === \"P2002\"\n ) {\n throw new TRPCError({\n code: \"CONFLICT\",\n message: \"An academic year with this name already exists\",\n });\n }\n throw err;\n }\n}\n\nexport async function updateYear(\n userId: string,\n input: {\n schoolId: string;\n yearId: string;\n name?: string;\n startDate?: Date;\n endDate?: Date;\n isCurrent?: boolean;\n }\n) {\n await requireSchoolAdmin(input.schoolId, userId);\n const year = await updateAcademicYear(input.yearId, input.schoolId, {\n name: input.name,\n startDate: input.startDate,\n endDate: input.endDate,\n isCurrent: input.isCurrent,\n });\n if (!year) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Academic year not found\" });\n }\n return { year };\n}\n\nexport async function removeYear(\n userId: string,\n input: { schoolId: string; yearId: string }\n) {\n await requireSchoolAdmin(input.schoolId, userId);\n const result = await deleteAcademicYear(input.yearId, input.schoolId);\n if (result.count === 0) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Academic year not found\" });\n }\n return { ok: true as const };\n}\n\nexport async function createTerm(\n userId: string,\n input: {\n schoolId: string;\n yearId: string;\n name: string;\n startDate: Date;\n endDate: Date;\n isCurrent?: boolean;\n }\n) {\n await requireSchoolAdmin(input.schoolId, userId);\n const year = await getAcademicYearById(input.yearId, input.schoolId);\n if (!year) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Academic year not found\" });\n }\n if (input.endDate < input.startDate) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"endDate must be on or after startDate\",\n });\n }\n try {\n const term = await createAcademicTerm({\n academicYearId: input.yearId,\n name: input.name,\n startDate: input.startDate,\n endDate: input.endDate,\n isCurrent: input.isCurrent,\n });\n return { term };\n } catch (err: unknown) {\n if (\n err &&\n typeof err === \"object\" &&\n \"code\" in err &&\n (err as { code: string }).code === \"P2002\"\n ) {\n throw new TRPCError({\n code: \"CONFLICT\",\n message: \"A term with this name already exists in the year\",\n });\n }\n throw err;\n }\n}\n\nexport async function updateTerm(\n userId: string,\n input: {\n schoolId: string;\n yearId: string;\n termId: string;\n name?: string;\n startDate?: Date;\n endDate?: Date;\n isCurrent?: boolean;\n }\n) {\n await requireSchoolAdmin(input.schoolId, userId);\n const year = await getAcademicYearById(input.yearId, input.schoolId);\n if (!year) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Academic year not found\" });\n }\n const term = await updateAcademicTerm(input.termId, input.yearId, {\n name: input.name,\n startDate: input.startDate,\n endDate: input.endDate,\n isCurrent: input.isCurrent,\n });\n if (!term) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Academic term not found\" });\n }\n return { term };\n}\n\nexport async function removeTerm(\n userId: string,\n input: { schoolId: string; yearId: string; termId: string }\n) {\n await requireSchoolAdmin(input.schoolId, userId);\n const year = await getAcademicYearById(input.yearId, input.schoolId);\n if (!year) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Academic year not found\" });\n }\n const result = await deleteAcademicTerm(input.termId, input.yearId);\n if (result.count === 0) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Academic term not found\" });\n }\n return { ok: true as const };\n}\n\n/**\n * C2: Promote / move students from one class to another (basic rollover).\n * Disconnects from `fromClassId` and connects to `toClassId`.\n */\nexport async function promoteStudents(\n userId: string,\n input: {\n schoolId: string;\n fromClassId: string;\n toClassId: string;\n studentUserIds: string[];\n }\n) {\n await requireSchoolAdmin(input.schoolId, userId);\n\n if (input.fromClassId === input.toClassId) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"fromClassId and toClassId must differ\",\n });\n }\n if (input.studentUserIds.length === 0) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"Select at least one student\",\n });\n }\n\n const [fromClass, toClass] = await Promise.all([\n findSchoolClass(input.schoolId, input.fromClassId),\n findSchoolClass(input.schoolId, input.toClassId),\n ]);\n if (!fromClass || !toClass) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Source or destination class not found in this school\",\n });\n }\n\n const memberships = await listActiveSchoolMemberships(\n input.schoolId,\n input.studentUserIds\n );\n const studentIds = memberships\n .filter((m) => m.role === \"STUDENT\")\n .map((m) => m.userId);\n if (studentIds.length === 0) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"No active student memberships found for the given users\",\n });\n }\n\n // Only move students currently enrolled in the source class.\n const enrolled = await prisma.class.findFirst({\n where: {\n id: input.fromClassId,\n schoolId: input.schoolId,\n },\n select: {\n students: {\n where: { id: { in: studentIds } },\n select: { id: true },\n },\n },\n });\n const moveIds = (enrolled?.students ?? []).map((s) => s.id);\n if (moveIds.length === 0) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"None of the selected students are in the source class\",\n });\n }\n\n await prisma.$transaction([\n prisma.class.update({\n where: { id: input.fromClassId },\n data: { students: { disconnect: moveIds.map((id) => ({ id })) } },\n }),\n prisma.class.update({\n where: { id: input.toClassId },\n data: { students: { connect: moveIds.map((id) => ({ id })) } },\n }),\n ]);\n\n return {\n promotedCount: moveIds.length,\n studentUserIds: moveIds,\n fromClassId: input.fromClassId,\n toClassId: input.toClassId,\n };\n}\n"],"names":[],"mappings":"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","debug_id":"97525cba-13be-5926-93dc-e0cbf27e0ef6"}
|
|
1
|
+
{"version":3,"file":"academicCalendar.js","sources":["services/academicCalendar.ts"],"sourceRoot":"/","sourcesContent":["/**\n * Academic year / term service (C1) + student promotion (C2).\n */\nimport { TRPCError } from \"@trpc/server\";\nimport { prisma } from \"../lib/prisma.js\";\nimport {\n createAcademicTerm,\n createAcademicYear,\n deleteAcademicTerm,\n deleteAcademicYear,\n getAcademicYearById,\n getCurrentAcademicYear,\n listAcademicYears,\n updateAcademicTerm,\n updateAcademicYear,\n} from \"../models/academicCalendar.js\";\nimport {\n findActiveSchoolAdminMembership,\n findSchoolClass,\n listActiveSchoolMemberships,\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\nexport async function listYears(userId: string, schoolId: string) {\n await requireSchoolAdmin(schoolId, userId);\n return { years: await listAcademicYears(schoolId) };\n}\n\nexport async function getCurrentYear(userId: string, schoolId: string) {\n await requireSchoolAdmin(schoolId, userId);\n return { year: await getCurrentAcademicYear(schoolId) };\n}\n\nexport async function createYear(\n userId: string,\n input: {\n schoolId: string;\n name: string;\n startDate: Date;\n endDate: Date;\n isCurrent?: boolean;\n }\n) {\n await requireSchoolAdmin(input.schoolId, userId);\n if (input.endDate < input.startDate) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"endDate must be on or after startDate\",\n });\n }\n try {\n const year = await createAcademicYear(input);\n return { year };\n } catch (err: unknown) {\n if (\n err &&\n typeof err === \"object\" &&\n \"code\" in err &&\n (err as { code: string }).code === \"P2002\"\n ) {\n throw new TRPCError({\n code: \"CONFLICT\",\n message: \"An academic year with this name already exists\",\n });\n }\n throw err;\n }\n}\n\nexport async function updateYear(\n userId: string,\n input: {\n schoolId: string;\n yearId: string;\n name?: string;\n startDate?: Date;\n endDate?: Date;\n isCurrent?: boolean;\n }\n) {\n await requireSchoolAdmin(input.schoolId, userId);\n if (input.startDate !== undefined || input.endDate !== undefined) {\n const existing = await getAcademicYearById(input.yearId, input.schoolId);\n if (!existing) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Academic year not found\" });\n }\n const nextStart = input.startDate ?? existing.startDate;\n const nextEnd = input.endDate ?? existing.endDate;\n if (nextEnd < nextStart) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"endDate must be on or after startDate\",\n });\n }\n }\n const year = await updateAcademicYear(input.yearId, input.schoolId, {\n name: input.name,\n startDate: input.startDate,\n endDate: input.endDate,\n isCurrent: input.isCurrent,\n });\n if (!year) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Academic year not found\" });\n }\n return { year };\n}\n\nexport async function removeYear(\n userId: string,\n input: { schoolId: string; yearId: string }\n) {\n await requireSchoolAdmin(input.schoolId, userId);\n const result = await deleteAcademicYear(input.yearId, input.schoolId);\n if (result.count === 0) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Academic year not found\" });\n }\n return { ok: true as const };\n}\n\nexport async function createTerm(\n userId: string,\n input: {\n schoolId: string;\n yearId: string;\n name: string;\n startDate: Date;\n endDate: Date;\n isCurrent?: boolean;\n }\n) {\n await requireSchoolAdmin(input.schoolId, userId);\n const year = await getAcademicYearById(input.yearId, input.schoolId);\n if (!year) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Academic year not found\" });\n }\n if (input.endDate < input.startDate) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"endDate must be on or after startDate\",\n });\n }\n try {\n const term = await createAcademicTerm({\n academicYearId: input.yearId,\n name: input.name,\n startDate: input.startDate,\n endDate: input.endDate,\n isCurrent: input.isCurrent,\n });\n return { term };\n } catch (err: unknown) {\n if (\n err &&\n typeof err === \"object\" &&\n \"code\" in err &&\n (err as { code: string }).code === \"P2002\"\n ) {\n throw new TRPCError({\n code: \"CONFLICT\",\n message: \"A term with this name already exists in the year\",\n });\n }\n throw err;\n }\n}\n\nexport async function updateTerm(\n userId: string,\n input: {\n schoolId: string;\n yearId: string;\n termId: string;\n name?: string;\n startDate?: Date;\n endDate?: Date;\n isCurrent?: boolean;\n }\n) {\n await requireSchoolAdmin(input.schoolId, userId);\n const year = await getAcademicYearById(input.yearId, input.schoolId);\n if (!year) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Academic year not found\" });\n }\n if (input.startDate !== undefined || input.endDate !== undefined) {\n const existingTerm = year.terms.find((t) => t.id === input.termId);\n if (!existingTerm) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Academic term not found\" });\n }\n const nextStart = input.startDate ?? existingTerm.startDate;\n const nextEnd = input.endDate ?? existingTerm.endDate;\n if (nextEnd < nextStart) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"endDate must be on or after startDate\",\n });\n }\n }\n const term = await updateAcademicTerm(input.termId, input.yearId, {\n name: input.name,\n startDate: input.startDate,\n endDate: input.endDate,\n isCurrent: input.isCurrent,\n });\n if (!term) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Academic term not found\" });\n }\n return { term };\n}\n\nexport async function removeTerm(\n userId: string,\n input: { schoolId: string; yearId: string; termId: string }\n) {\n await requireSchoolAdmin(input.schoolId, userId);\n const year = await getAcademicYearById(input.yearId, input.schoolId);\n if (!year) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Academic year not found\" });\n }\n const result = await deleteAcademicTerm(input.termId, input.yearId);\n if (result.count === 0) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Academic term not found\" });\n }\n return { ok: true as const };\n}\n\n/**\n * C2: Promote / move students from one class to another (basic rollover).\n * Disconnects from `fromClassId` and connects to `toClassId`.\n */\nexport async function promoteStudents(\n userId: string,\n input: {\n schoolId: string;\n fromClassId: string;\n toClassId: string;\n studentUserIds: string[];\n }\n) {\n await requireSchoolAdmin(input.schoolId, userId);\n\n if (input.fromClassId === input.toClassId) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"fromClassId and toClassId must differ\",\n });\n }\n if (input.studentUserIds.length === 0) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"Select at least one student\",\n });\n }\n\n const [fromClass, toClass] = await Promise.all([\n findSchoolClass(input.schoolId, input.fromClassId),\n findSchoolClass(input.schoolId, input.toClassId),\n ]);\n if (!fromClass || !toClass) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Source or destination class not found in this school\",\n });\n }\n\n const memberships = await listActiveSchoolMemberships(\n input.schoolId,\n input.studentUserIds\n );\n const studentIds = memberships\n .filter((m) => m.role === \"STUDENT\")\n .map((m) => m.userId);\n if (studentIds.length === 0) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"No active student memberships found for the given users\",\n });\n }\n\n // Only move students currently enrolled in the source class.\n const enrolled = await prisma.class.findFirst({\n where: {\n id: input.fromClassId,\n schoolId: input.schoolId,\n },\n select: {\n students: {\n where: { id: { in: studentIds } },\n select: { id: true },\n },\n },\n });\n const moveIds = (enrolled?.students ?? []).map((s) => s.id);\n if (moveIds.length === 0) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"None of the selected students are in the source class\",\n });\n }\n\n await prisma.$transaction([\n prisma.class.update({\n where: { id: input.fromClassId },\n data: { students: { disconnect: moveIds.map((id) => ({ id })) } },\n }),\n prisma.class.update({\n where: { id: input.toClassId },\n data: { students: { connect: moveIds.map((id) => ({ id })) } },\n }),\n ]);\n\n return {\n promotedCount: moveIds.length,\n studentUserIds: moveIds,\n fromClassId: input.fromClassId,\n toClassId: input.toClassId,\n };\n}\n"],"names":[],"mappings":"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","debug_id":"d8dde89c-b5a2-5b31-be33-b365078aeaa2"}
|
|
@@ -183,7 +183,11 @@ export declare function updateAttendanceRecord(userId: string, classId: string,
|
|
|
183
183
|
} | null;
|
|
184
184
|
}[];
|
|
185
185
|
}>;
|
|
186
|
-
/**
|
|
186
|
+
/**
|
|
187
|
+
* School-admin: today's attendance rollup across classes.
|
|
188
|
+
* `percentage` is session-weighted (share of attended class-session records),
|
|
189
|
+
* not a share of `totalStudents`; a student in six classes contributes six records.
|
|
190
|
+
*/
|
|
187
191
|
export declare function listTodayAttendanceSummary(userId: string, schoolId: string): Promise<{
|
|
188
192
|
date: Date;
|
|
189
193
|
totalStudents: number;
|
|
@@ -244,7 +248,7 @@ export declare function listExcusals(userId: string, input: {
|
|
|
244
248
|
revokedAt: Date | null;
|
|
245
249
|
createdAt: Date;
|
|
246
250
|
updatedAt: Date;
|
|
247
|
-
status: "ACTIVE" | "REVOKED" | "UPCOMING";
|
|
251
|
+
status: "ACTIVE" | "REVOKED" | "EXPIRED" | "UPCOMING";
|
|
248
252
|
studentIds: string[];
|
|
249
253
|
students: {
|
|
250
254
|
id: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attendance.d.ts","sourceRoot":"/","sources":["services/attendance.ts"],"names":[],"mappings":"AAMA,OAAO,EAqBL,UAAU,EACV,aAAa,EACb,WAAW,EACZ,MAAM,yBAAyB,CAAC;AAIjC,OAAO,EAGL,KAAK,UAAU,EAChB,MAAM,sBAAsB,CAAC;AAyB9B,0FAA0F;AAC1F,wBAAsB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;YAiGhD,MAAM;kBAAY,MAAM;;;;;;YAPnC,MAAM;mBACC,MAAM;gBACT,MAAM;cACR,MAAM,GAAG,IAAI;4BACC,MAAM,GAAG,IAAI;qBACpB,MAAM,GAAG,IAAI;mBACf,IAAI;iBACN;YAAE,EAAE,EAAE,MAAM,CAAC;YAAC,QAAQ,EAAE,MAAM,CAAA;SAAE;0BACvB;YAChB,EAAE,EAAE,MAAM,CAAC;YACX,MAAM,EAAE,MAAM,CAAC;YACf,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;SACrB,GAAG,IAAI;;MAlBb;AA0CD,4EAA4E;AAC5E,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAClC,UAAU,EAAE;IACV,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,IAAI,EAAE,UAAU,EAAE,CAAC;IACnB,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,OAAO,CAAC,EAAE,UAAU,EAAE,CAAC;CACxB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;YAvCkB,MAAM;kBAAY,MAAM;;;;;;YAPnC,MAAM;mBACC,MAAM;gBACT,MAAM;cACR,MAAM,GAAG,IAAI;4BACC,MAAM,GAAG,IAAI;qBACpB,MAAM,GAAG,IAAI;mBACf,IAAI;iBACN;YAAE,EAAE,EAAE,MAAM,CAAC;YAAC,QAAQ,EAAE,MAAM,CAAA;SAAE;0BACvB;YAChB,EAAE,EAAE,MAAM,CAAC;YACX,MAAM,EAAE,MAAM,CAAC;YACf,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;SACrB,GAAG,IAAI;;GA6Hb;AAED
|
|
1
|
+
{"version":3,"file":"attendance.d.ts","sourceRoot":"/","sources":["services/attendance.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Active StudentExcusal rows force EXCUSED (locked for teachers).
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
!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]="
|
|
7
|
+
!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]="a0baad8d-f286-5756-9dfd-397505318152")}catch(e){}}();
|
|
8
8
|
import { TRPCError } from "@trpc/server";
|
|
9
9
|
import { findClassWithStudents, findClassWithTeacher, findEventsByClassId, findAttendanceByEventId, findAttendanceByClassAndEvent, findManyAttendance, createAttendance, createAttendanceForEvent, updateAttendance, findActiveExcusalsForStudents, listExcusalsForSchool, findExcusalById, createStudentExcusal, updateStudentExcusal, revokeStudentExcusal, applyExcusalToAttendance, clearExcusalLocks, listSchoolStudentMemberships, listAttendanceRecordsForSchoolStudents, listTodaySchoolAttendanceSessions, toDateOnly, startOfUtcDay, endOfUtcDay, } from "../models/attendance.js";
|
|
10
10
|
import { findActiveSchoolAdminMembership } from "../models/school.js";
|
|
@@ -192,7 +192,11 @@ export async function updateAttendanceRecord(userId, classId, eventId, attendanc
|
|
|
192
192
|
});
|
|
193
193
|
return shapeAttendanceSession(updated);
|
|
194
194
|
}
|
|
195
|
-
/**
|
|
195
|
+
/**
|
|
196
|
+
* School-admin: today's attendance rollup across classes.
|
|
197
|
+
* `percentage` is session-weighted (share of attended class-session records),
|
|
198
|
+
* not a share of `totalStudents`; a student in six classes contributes six records.
|
|
199
|
+
*/
|
|
196
200
|
export async function listTodayAttendanceSummary(userId, schoolId) {
|
|
197
201
|
await requireSchoolAdmin(schoolId, userId);
|
|
198
202
|
const today = new Date();
|
|
@@ -303,7 +307,7 @@ export async function listExcusals(userId, input) {
|
|
|
303
307
|
status = "UPCOMING";
|
|
304
308
|
}
|
|
305
309
|
else if (toDateOnly(excusal.endDate) < now) {
|
|
306
|
-
status = "
|
|
310
|
+
status = "EXPIRED";
|
|
307
311
|
}
|
|
308
312
|
return {
|
|
309
313
|
id: excusal.id,
|
|
@@ -490,4 +494,4 @@ async function sendAttendanceNotification(input) {
|
|
|
490
494
|
// Re-export date helpers used by callers/tests.
|
|
491
495
|
export { toDateOnly, startOfUtcDay, endOfUtcDay };
|
|
492
496
|
//# sourceMappingURL=attendance.js.map
|
|
493
|
-
//# debugId=
|
|
497
|
+
//# debugId=a0baad8d-f286-5756-9dfd-397505318152
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attendance.js","sources":["services/attendance.ts"],"sourceRoot":"/","sourcesContent":["/**\n * Attendance service – class attendance + school-admin excusals / stats.\n * Dual-writes legacy present/late/absent lists and per-student AttendanceRecord.\n * Active StudentExcusal rows force EXCUSED (locked for teachers).\n */\nimport { TRPCError } from \"@trpc/server\";\nimport {\n findClassWithStudents,\n findClassWithTeacher,\n findEventsByClassId,\n findAttendanceByEventId,\n findAttendanceByClassAndEvent,\n findManyAttendance,\n createAttendance,\n createAttendanceForEvent,\n updateAttendance,\n findActiveExcusalsForStudents,\n listExcusalsForSchool,\n findExcusalById,\n createStudentExcusal,\n updateStudentExcusal,\n revokeStudentExcusal,\n applyExcusalToAttendance,\n clearExcusalLocks,\n listSchoolStudentMemberships,\n listAttendanceRecordsForSchoolStudents,\n listTodaySchoolAttendanceSessions,\n toDateOnly,\n startOfUtcDay,\n endOfUtcDay,\n} from \"../models/attendance.js\";\nimport { findActiveSchoolAdminMembership } from \"../models/school.js\";\nimport { sendToMultiple } from \"./notification.js\";\nimport { logger } from \"../utils/logger.js\";\nimport {\n buildExcusalLocks,\n mergeAttendanceUpdateWithLocks,\n type StudentRef,\n} from \"./attendanceMerge.js\";\nimport {\n computeAttendancePercentage,\n emptyAttendanceCounts,\n tallyAttendanceStatuses,\n} from \"./attendanceStats.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\nfunction sessionDate(attendance: {\n date: Date;\n event?: { startTime: Date } | null;\n}): Date {\n return attendance.event?.startTime ?? attendance.date;\n}\n\n/** Get attendance for a class (optionally filtered by event). Creates missing records. */\nexport async function getAttendance(classId: string, eventId?: string) {\n const classData = await findClassWithStudents(classId);\n if (!classData) {\n throw new TRPCError({\n code: \"UNAUTHORIZED\",\n message: \"You are not authorized to view this class's attendance\",\n });\n }\n\n const events = await findEventsByClassId(classId);\n const studentIds = classData.students.map((s) => s.id);\n\n for (const event of events) {\n const existing = await findAttendanceByEventId(event.id);\n if (!existing) {\n const locks =\n classData.schoolId && studentIds.length > 0\n ? buildExcusalLocks(\n await findActiveExcusalsForStudents({\n schoolId: classData.schoolId,\n studentIds,\n date: event.startTime,\n }),\n studentIds\n )\n : [];\n\n const merged = mergeAttendanceUpdateWithLocks({\n present: classData.students.map((s) => ({\n id: s.id,\n username: s.username,\n })),\n late: [],\n absent: [],\n locks,\n });\n\n await createAttendanceForEvent({\n classId,\n eventId: event.id,\n date: event.startTime,\n presentIds: merged.presentIds,\n lateIds: merged.lateIds,\n absentIds: merged.absentIds,\n excusedIds: merged.excusedIds,\n records: merged.records,\n });\n } else if (classData.schoolId && studentIds.length > 0) {\n // Ensure active excusals are reflected on existing sessions.\n const locks = buildExcusalLocks(\n await findActiveExcusalsForStudents({\n schoolId: classData.schoolId,\n studentIds,\n date: event.startTime,\n }),\n studentIds\n );\n const needsLock = locks.filter((lock) => {\n const current = existing.records.find((r) => r.studentId === lock.studentId);\n return !(\n current?.status === \"EXCUSED\" &&\n current.excusedByExcusalId === lock.excusalId\n );\n });\n const byExcusal = new Map<string, string[]>();\n for (const lock of needsLock) {\n const list = byExcusal.get(lock.excusalId) ?? [];\n list.push(lock.studentId);\n byExcusal.set(lock.excusalId, list);\n }\n for (const [excusalId, lockedStudentIds] of byExcusal) {\n await applyExcusalToAttendance({\n schoolId: classData.schoolId,\n excusalId,\n studentIds: lockedStudentIds,\n startDate: event.startTime,\n endDate: event.startTime,\n });\n }\n }\n }\n\n const sessions = await findManyAttendance(classId, eventId);\n return sessions.map(shapeAttendanceSession);\n}\n\n/** Additive shape: keep present/late/absent; expose excused + lock metadata for UI. */\nfunction shapeAttendanceSession<\n T extends {\n records: Array<{\n id: string;\n studentId: string;\n status: string;\n note: string | null;\n excusedByExcusalId: string | null;\n updatedById: string | null;\n updatedAt: Date;\n student: { id: string; username: string };\n excusedByExcusal: {\n id: string;\n reason: string;\n note: string | null;\n } | null;\n }>;\n present: unknown[];\n late: unknown[];\n absent: unknown[];\n },\n>(session: T) {\n const records = session.records.map((r) => ({\n ...r,\n lockedFromExcusal: Boolean(r.excusedByExcusalId),\n reason: r.excusedByExcusal?.reason ?? null,\n excusalNote: r.excusedByExcusal?.note ?? null,\n }));\n const excused = records\n .filter((r) => r.status === \"EXCUSED\")\n .map((r) => r.student);\n\n return {\n ...session,\n excused,\n records,\n };\n}\n\n/** Update attendance record (present/late/absent/excused). Teacher-only. */\nexport async function updateAttendanceRecord(\n userId: string,\n classId: string,\n eventId: string | null | undefined,\n attendance: {\n present: StudentRef[];\n late: StudentRef[];\n absent: StudentRef[];\n excused?: StudentRef[];\n }\n) {\n const classData = await findClassWithTeacher(classId, userId);\n if (!classData) {\n throw new TRPCError({\n code: \"UNAUTHORIZED\",\n message: \"You are not authorized to update this class's attendance\",\n });\n }\n\n const existing = await findAttendanceByClassAndEvent(classId, eventId ?? null);\n const dateForExcusal = existing\n ? sessionDate(existing)\n : new Date();\n\n const classStudentIds = classData.students.map((s) => s.id);\n const locks =\n classData.schoolId && classStudentIds.length > 0\n ? buildExcusalLocks(\n await findActiveExcusalsForStudents({\n schoolId: classData.schoolId,\n studentIds: classStudentIds,\n date: dateForExcusal,\n }),\n classStudentIds\n )\n : [];\n\n const existingNotes = new Map(\n (existing?.records ?? []).map((r) => [r.studentId, r.note])\n );\n\n const merged = mergeAttendanceUpdateWithLocks({\n present: attendance.present,\n late: attendance.late,\n absent: attendance.absent,\n excused: attendance.excused,\n locks,\n updatedById: userId,\n existingNotesByStudentId: existingNotes,\n });\n\n const previousLateIds = new Set(existing?.late.map((student) => student.id) ?? []);\n const previousAbsentIds = new Set(\n existing?.absent.map((student) => student.id) ?? []\n );\n const classStudentIdSet = new Set(classStudentIds);\n\n if (!existing) {\n const created = await createAttendance({\n classId,\n eventId: eventId ?? null,\n presentIds: merged.presentIds,\n lateIds: merged.lateIds,\n absentIds: merged.absentIds,\n excusedIds: merged.excusedIds,\n records: merged.records,\n updatedById: userId,\n });\n await notifyMarkedLateOrAbsent({\n classId,\n className: classData.name,\n eventId: eventId ?? null,\n eventName: created.event?.name ?? null,\n classStudentIds: classStudentIdSet,\n lateIds: merged.lateIds,\n absentIds: merged.absentIds,\n previousLateIds,\n previousAbsentIds,\n });\n return shapeAttendanceSession(created);\n }\n\n const updated = await updateAttendance(existing.id, {\n presentIds: merged.presentIds,\n lateIds: merged.lateIds,\n absentIds: merged.absentIds,\n records: merged.records,\n });\n await notifyMarkedLateOrAbsent({\n classId,\n className: classData.name,\n eventId: eventId ?? null,\n eventName: existing.event?.name ?? null,\n classStudentIds: classStudentIdSet,\n lateIds: merged.lateIds,\n absentIds: merged.absentIds,\n previousLateIds,\n previousAbsentIds,\n });\n return shapeAttendanceSession(updated);\n}\n\n/** School-admin: today's attendance rollup across classes. */\nexport async function listTodayAttendanceSummary(\n userId: string,\n schoolId: string\n) {\n await requireSchoolAdmin(schoolId, userId);\n\n const today = new Date();\n const sessions = await listTodaySchoolAttendanceSessions(schoolId, today);\n const memberships = await listSchoolStudentMemberships(schoolId);\n const totalStudents = memberships.length;\n\n const counts = emptyAttendanceCounts();\n const rows: Array<{\n attendanceId: string;\n classId: string;\n className: string;\n section: string;\n subject: string;\n eventId: string | null;\n eventName: string | null;\n date: Date;\n present: number;\n late: number;\n absent: number;\n excused: number;\n unmarked: number;\n classStudentCount: number;\n }> = [];\n\n for (const session of sessions) {\n const tallied = tallyAttendanceStatuses(session.records.map((r) => r.status));\n counts.present += tallied.present;\n counts.late += tallied.late;\n counts.absent += tallied.absent;\n counts.excused += tallied.excused;\n const classStudentCount = session.class._count.students;\n const marked =\n tallied.present + tallied.late + tallied.absent + tallied.excused;\n rows.push({\n attendanceId: session.id,\n classId: session.classId,\n className: session.class.name,\n section: session.class.section,\n subject: session.class.subject,\n eventId: session.eventId,\n eventName: session.event?.name ?? null,\n date: session.event?.startTime ?? session.date,\n present: tallied.present,\n late: tallied.late,\n absent: tallied.absent,\n excused: tallied.excused,\n unmarked: Math.max(0, classStudentCount - marked),\n classStudentCount,\n });\n }\n\n return {\n date: toDateOnly(today),\n totalStudents,\n totals: counts,\n percentage: computeAttendancePercentage(counts),\n sessions: rows,\n };\n}\n\n/** School-admin: per-student attendance counts + % (excused excluded from %). */\nexport async function listStudentAttendanceStats(\n userId: string,\n input: {\n schoolId: string;\n from?: Date;\n to?: Date;\n studentId?: string;\n }\n) {\n await requireSchoolAdmin(input.schoolId, userId);\n\n const memberships = await listSchoolStudentMemberships(input.schoolId);\n const filtered = input.studentId\n ? memberships.filter((m) => m.userId === input.studentId)\n : memberships;\n const studentIds = filtered.map((m) => m.userId);\n\n const records = await listAttendanceRecordsForSchoolStudents({\n schoolId: input.schoolId,\n studentIds,\n from: input.from,\n to: input.to,\n });\n\n const byStudent = new Map<string, ReturnType<typeof emptyAttendanceCounts>>();\n for (const id of studentIds) {\n byStudent.set(id, emptyAttendanceCounts());\n }\n for (const record of records) {\n const counts = byStudent.get(record.studentId);\n if (!counts) continue;\n switch (record.status) {\n case \"PRESENT\":\n counts.present += 1;\n break;\n case \"LATE\":\n counts.late += 1;\n break;\n case \"ABSENT\":\n counts.absent += 1;\n break;\n case \"EXCUSED\":\n counts.excused += 1;\n break;\n default:\n break;\n }\n }\n\n return filtered.map((membership) => {\n const counts = byStudent.get(membership.userId) ?? emptyAttendanceCounts();\n return {\n studentId: membership.userId,\n username: membership.user.username,\n displayName: membership.user.profile?.displayName ?? null,\n profilePicture: membership.user.profile?.profilePicture ?? null,\n profilePictureThumbnail:\n membership.user.profile?.profilePictureThumbnail ?? null,\n classes: membership.user.studentIn,\n present: counts.present,\n late: counts.late,\n absent: counts.absent,\n excused: counts.excused,\n attendancePercentage: computeAttendancePercentage(counts),\n };\n });\n}\n\nexport async function listExcusals(\n userId: string,\n input: {\n schoolId: string;\n includeRevoked?: boolean;\n studentId?: string;\n }\n) {\n await requireSchoolAdmin(input.schoolId, userId);\n\n const excusals = await listExcusalsForSchool(input);\n const now = toDateOnly(new Date());\n\n return excusals.map((excusal) => {\n let status: \"ACTIVE\" | \"UPCOMING\" | \"REVOKED\" = \"ACTIVE\";\n if (excusal.revokedAt) {\n status = \"REVOKED\";\n } else if (toDateOnly(excusal.startDate) > now) {\n status = \"UPCOMING\";\n } else if (toDateOnly(excusal.endDate) < now) {\n status = \"ACTIVE\"; // past but not revoked — still listed as ACTIVE historically\n }\n\n return {\n id: excusal.id,\n schoolId: excusal.schoolId,\n startDate: excusal.startDate,\n endDate: excusal.endDate,\n reason: excusal.reason,\n note: excusal.note,\n revokedAt: excusal.revokedAt,\n createdAt: excusal.createdAt,\n updatedAt: excusal.updatedAt,\n status,\n studentIds: excusal.students.map((s) => s.studentId),\n students: excusal.students.map((s) => ({\n id: s.student.id,\n username: s.student.username,\n displayName: s.student.profile?.displayName ?? null,\n profilePicture: s.student.profile?.profilePicture ?? null,\n profilePictureThumbnail:\n s.student.profile?.profilePictureThumbnail ?? null,\n })),\n createdBy: {\n id: excusal.createdBy.id,\n username: excusal.createdBy.username,\n displayName: excusal.createdBy.profile?.displayName ?? null,\n },\n revokedBy: excusal.revokedBy\n ? {\n id: excusal.revokedBy.id,\n username: excusal.revokedBy.username,\n displayName: excusal.revokedBy.profile?.displayName ?? null,\n }\n : null,\n };\n });\n}\n\nexport async function createExcusal(\n userId: string,\n input: {\n schoolId: string;\n studentIds: string[];\n startDate: Date;\n endDate: Date;\n reason: string;\n note?: string | null;\n }\n) {\n await requireSchoolAdmin(input.schoolId, userId);\n\n if (toDateOnly(input.endDate) < toDateOnly(input.startDate)) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"endDate must be on or after startDate\",\n });\n }\n\n const uniqueStudentIds = [...new Set(input.studentIds)];\n const memberships = await listSchoolStudentMemberships(input.schoolId);\n const schoolStudentIds = new Set(memberships.map((m) => m.userId));\n const invalid = uniqueStudentIds.filter((id) => !schoolStudentIds.has(id));\n if (invalid.length > 0) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"One or more students are not active members of this school\",\n });\n }\n\n const excusal = await createStudentExcusal({\n schoolId: input.schoolId,\n studentIds: uniqueStudentIds,\n startDate: input.startDate,\n endDate: input.endDate,\n reason: input.reason,\n note: input.note,\n createdById: userId,\n });\n\n await applyExcusalToAttendance({\n schoolId: input.schoolId,\n excusalId: excusal.id,\n studentIds: uniqueStudentIds,\n startDate: input.startDate,\n endDate: input.endDate,\n updatedById: userId,\n });\n\n return excusal;\n}\n\nexport async function updateExcusal(\n userId: string,\n input: {\n schoolId: string;\n excusalId: string;\n studentIds?: string[];\n startDate?: Date;\n endDate?: Date;\n reason?: string;\n note?: string | null;\n }\n) {\n await requireSchoolAdmin(input.schoolId, userId);\n\n const existing = await findExcusalById(input.excusalId);\n if (!existing || existing.schoolId !== input.schoolId) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Excusal not found\" });\n }\n if (existing.revokedAt) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"Cannot update a revoked excusal\",\n });\n }\n\n const startDate = input.startDate ?? existing.startDate;\n const endDate = input.endDate ?? existing.endDate;\n if (toDateOnly(endDate) < toDateOnly(startDate)) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"endDate must be on or after startDate\",\n });\n }\n\n let studentIds = input.studentIds\n ? [...new Set(input.studentIds)]\n : existing.students.map((s) => s.studentId);\n\n if (input.studentIds) {\n const memberships = await listSchoolStudentMemberships(input.schoolId);\n const schoolStudentIds = new Set(memberships.map((m) => m.userId));\n const invalid = studentIds.filter((id) => !schoolStudentIds.has(id));\n if (invalid.length > 0) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"One or more students are not active members of this school\",\n });\n }\n }\n\n // Clear previous locks, then re-apply for the new window/students.\n await clearExcusalLocks(input.excusalId);\n\n const updated = await updateStudentExcusal(input.excusalId, {\n studentIds: input.studentIds ? studentIds : undefined,\n startDate: input.startDate,\n endDate: input.endDate,\n reason: input.reason,\n note: input.note,\n });\n\n await applyExcusalToAttendance({\n schoolId: input.schoolId,\n excusalId: updated.id,\n studentIds,\n startDate,\n endDate,\n updatedById: userId,\n });\n\n return updated;\n}\n\nexport async function revokeExcusal(\n userId: string,\n input: { schoolId: string; excusalId: string }\n) {\n await requireSchoolAdmin(input.schoolId, userId);\n\n const existing = await findExcusalById(input.excusalId);\n if (!existing || existing.schoolId !== input.schoolId) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Excusal not found\" });\n }\n if (existing.revokedAt) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"Excusal is already revoked\",\n });\n }\n\n await clearExcusalLocks(input.excusalId);\n return revokeStudentExcusal(input.excusalId, userId);\n}\n\n/** Notify only students whose late or absent status is newly added. */\nasync function notifyMarkedLateOrAbsent(input: {\n classId: string;\n className: string;\n eventId: string | null;\n eventName: string | null;\n classStudentIds: Set<string>;\n lateIds: string[];\n absentIds: string[];\n previousLateIds: Set<string>;\n previousAbsentIds: Set<string>;\n}) {\n const lateReceiverIds = [...new Set(input.lateIds)].filter(\n (id) => input.classStudentIds.has(id) && !input.previousLateIds.has(id)\n );\n const absentReceiverIds = [...new Set(input.absentIds)].filter(\n (id) => input.classStudentIds.has(id) && !input.previousAbsentIds.has(id)\n );\n const link = input.eventId\n ? `/class/${input.classId}/attendance/${input.eventId}`\n : `/class/${input.classId}/attendance`;\n const attendanceLabel = input.eventName\n ? `\"${input.eventName}\" in ${input.className}`\n : input.className;\n\n await Promise.all([\n sendAttendanceNotification({\n receiverIds: lateReceiverIds,\n title: `Marked late in ${input.className}`,\n content: `You were marked late for ${attendanceLabel}.\\n[View attendance](${link})`,\n type: \"ATTENDANCE\",\n entityType: \"ATTENDANCE\",\n entityId: input.eventId,\n classId: input.classId,\n }),\n sendAttendanceNotification({\n receiverIds: absentReceiverIds,\n title: `Marked absent in ${input.className}`,\n content: `You were marked absent for ${attendanceLabel}.\\n[View attendance](${link})`,\n type: \"ATTENDANCE\",\n entityType: \"ATTENDANCE\",\n entityId: input.eventId,\n classId: input.classId,\n }),\n ]);\n}\n\n/** Send attendance notifications without failing the attendance update itself. */\nasync function sendAttendanceNotification(input: {\n receiverIds: string[];\n title: string;\n content: string;\n type: \"ATTENDANCE\";\n entityType: string;\n entityId: string | null;\n classId: string;\n}) {\n if (input.receiverIds.length === 0) {\n return;\n }\n\n try {\n await sendToMultiple(input);\n } catch (error) {\n logger.error(\"Failed to send attendance notifications:\", { error });\n }\n}\n\n// Re-export date helpers used by callers/tests.\nexport { toDateOnly, startOfUtcDay, endOfUtcDay };\n"],"names":[],"mappings":"AAAA;;;;GAIG;;;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EACL,qBAAqB,EACrB,oBAAoB,EACpB,mBAAmB,EACnB,uBAAuB,EACvB,6BAA6B,EAC7B,kBAAkB,EAClB,gBAAgB,EAChB,wBAAwB,EACxB,gBAAgB,EAChB,6BAA6B,EAC7B,qBAAqB,EACrB,eAAe,EACf,oBAAoB,EACpB,oBAAoB,EACpB,oBAAoB,EACpB,wBAAwB,EACxB,iBAAiB,EACjB,4BAA4B,EAC5B,sCAAsC,EACtC,iCAAiC,EACjC,UAAU,EACV,aAAa,EACb,WAAW,GACZ,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,+BAA+B,EAAE,MAAM,qBAAqB,CAAC;AACtE,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EACL,iBAAiB,EACjB,8BAA8B,GAE/B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,2BAA2B,EAC3B,qBAAqB,EACrB,uBAAuB,GACxB,MAAM,sBAAsB,CAAC;AAE9B,KAAK,UAAU,kBAAkB,CAAC,QAAgB,EAAE,MAAc;IAChE,MAAM,UAAU,GAAG,MAAM,+BAA+B,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAC3E,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,MAAM,IAAI,SAAS,CAAC;YAClB,IAAI,EAAE,WAAW;YACjB,OAAO,EAAE,4CAA4C;SACtD,CAAC,CAAC;IACL,CAAC;IACD,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,SAAS,WAAW,CAAC,UAGpB;IACC,OAAO,UAAU,CAAC,KAAK,EAAE,SAAS,IAAI,UAAU,CAAC,IAAI,CAAC;AACxD,CAAC;AAED,0FAA0F;AAC1F,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,OAAe,EAAE,OAAgB;IACnE,MAAM,SAAS,GAAG,MAAM,qBAAqB,CAAC,OAAO,CAAC,CAAC;IACvD,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,MAAM,IAAI,SAAS,CAAC;YAClB,IAAI,EAAE,cAAc;YACpB,OAAO,EAAE,wDAAwD;SAClE,CAAC,CAAC;IACL,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,mBAAmB,CAAC,OAAO,CAAC,CAAC;IAClD,MAAM,UAAU,GAAG,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAEvD,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,QAAQ,GAAG,MAAM,uBAAuB,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACzD,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,KAAK,GACT,SAAS,CAAC,QAAQ,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC;gBACzC,CAAC,CAAC,iBAAiB,CACf,MAAM,6BAA6B,CAAC;oBAClC,QAAQ,EAAE,SAAS,CAAC,QAAQ;oBAC5B,UAAU;oBACV,IAAI,EAAE,KAAK,CAAC,SAAS;iBACtB,CAAC,EACF,UAAU,CACX;gBACH,CAAC,CAAC,EAAE,CAAC;YAET,MAAM,MAAM,GAAG,8BAA8B,CAAC;gBAC5C,OAAO,EAAE,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;oBACtC,EAAE,EAAE,CAAC,CAAC,EAAE;oBACR,QAAQ,EAAE,CAAC,CAAC,QAAQ;iBACrB,CAAC,CAAC;gBACH,IAAI,EAAE,EAAE;gBACR,MAAM,EAAE,EAAE;gBACV,KAAK;aACN,CAAC,CAAC;YAEH,MAAM,wBAAwB,CAAC;gBAC7B,OAAO;gBACP,OAAO,EAAE,KAAK,CAAC,EAAE;gBACjB,IAAI,EAAE,KAAK,CAAC,SAAS;gBACrB,UAAU,EAAE,MAAM,CAAC,UAAU;gBAC7B,OAAO,EAAE,MAAM,CAAC,OAAO;gBACvB,SAAS,EAAE,MAAM,CAAC,SAAS;gBAC3B,UAAU,EAAE,MAAM,CAAC,UAAU;gBAC7B,OAAO,EAAE,MAAM,CAAC,OAAO;aACxB,CAAC,CAAC;QACL,CAAC;aAAM,IAAI,SAAS,CAAC,QAAQ,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACvD,6DAA6D;YAC7D,MAAM,KAAK,GAAG,iBAAiB,CAC7B,MAAM,6BAA6B,CAAC;gBAClC,QAAQ,EAAE,SAAS,CAAC,QAAQ;gBAC5B,UAAU;gBACV,IAAI,EAAE,KAAK,CAAC,SAAS;aACtB,CAAC,EACF,UAAU,CACX,CAAC;YACF,MAAM,SAAS,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE;gBACtC,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,IAAI,CAAC,SAAS,CAAC,CAAC;gBAC7E,OAAO,CAAC,CACN,OAAO,EAAE,MAAM,KAAK,SAAS;oBAC7B,OAAO,CAAC,kBAAkB,KAAK,IAAI,CAAC,SAAS,CAC9C,CAAC;YACJ,CAAC,CAAC,CAAC;YACH,MAAM,SAAS,GAAG,IAAI,GAAG,EAAoB,CAAC;YAC9C,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;gBAC7B,MAAM,IAAI,GAAG,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;gBACjD,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;gBAC1B,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;YACtC,CAAC;YACD,KAAK,MAAM,CAAC,SAAS,EAAE,gBAAgB,CAAC,IAAI,SAAS,EAAE,CAAC;gBACtD,MAAM,wBAAwB,CAAC;oBAC7B,QAAQ,EAAE,SAAS,CAAC,QAAQ;oBAC5B,SAAS;oBACT,UAAU,EAAE,gBAAgB;oBAC5B,SAAS,EAAE,KAAK,CAAC,SAAS;oBAC1B,OAAO,EAAE,KAAK,CAAC,SAAS;iBACzB,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,kBAAkB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAC5D,OAAO,QAAQ,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAC;AAC9C,CAAC;AAED,uFAAuF;AACvF,SAAS,sBAAsB,CAqB7B,OAAU;IACV,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC1C,GAAG,CAAC;QACJ,iBAAiB,EAAE,OAAO,CAAC,CAAC,CAAC,kBAAkB,CAAC;QAChD,MAAM,EAAE,CAAC,CAAC,gBAAgB,EAAE,MAAM,IAAI,IAAI;QAC1C,WAAW,EAAE,CAAC,CAAC,gBAAgB,EAAE,IAAI,IAAI,IAAI;KAC9C,CAAC,CAAC,CAAC;IACJ,MAAM,OAAO,GAAG,OAAO;SACpB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,SAAS,CAAC;SACrC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;IAEzB,OAAO;QACL,GAAG,OAAO;QACV,OAAO;QACP,OAAO;KACR,CAAC;AACJ,CAAC;AAED,4EAA4E;AAC5E,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,MAAc,EACd,OAAe,EACf,OAAkC,EAClC,UAKC;IAED,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,cAAc;YACpB,OAAO,EAAE,0DAA0D;SACpE,CAAC,CAAC;IACL,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,6BAA6B,CAAC,OAAO,EAAE,OAAO,IAAI,IAAI,CAAC,CAAC;IAC/E,MAAM,cAAc,GAAG,QAAQ;QAC7B,CAAC,CAAC,WAAW,CAAC,QAAQ,CAAC;QACvB,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC;IAEf,MAAM,eAAe,GAAG,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAC5D,MAAM,KAAK,GACT,SAAS,CAAC,QAAQ,IAAI,eAAe,CAAC,MAAM,GAAG,CAAC;QAC9C,CAAC,CAAC,iBAAiB,CACf,MAAM,6BAA6B,CAAC;YAClC,QAAQ,EAAE,SAAS,CAAC,QAAQ;YAC5B,UAAU,EAAE,eAAe;YAC3B,IAAI,EAAE,cAAc;SACrB,CAAC,EACF,eAAe,CAChB;QACH,CAAC,CAAC,EAAE,CAAC;IAET,MAAM,aAAa,GAAG,IAAI,GAAG,CAC3B,CAAC,QAAQ,EAAE,OAAO,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAC5D,CAAC;IAEF,MAAM,MAAM,GAAG,8BAA8B,CAAC;QAC5C,OAAO,EAAE,UAAU,CAAC,OAAO;QAC3B,IAAI,EAAE,UAAU,CAAC,IAAI;QACrB,MAAM,EAAE,UAAU,CAAC,MAAM;QACzB,OAAO,EAAE,UAAU,CAAC,OAAO;QAC3B,KAAK;QACL,WAAW,EAAE,MAAM;QACnB,wBAAwB,EAAE,aAAa;KACxC,CAAC,CAAC;IAEH,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;IACnF,MAAM,iBAAiB,GAAG,IAAI,GAAG,CAC/B,QAAQ,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,CACpD,CAAC;IACF,MAAM,iBAAiB,GAAG,IAAI,GAAG,CAAC,eAAe,CAAC,CAAC;IAEnD,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,OAAO,GAAG,MAAM,gBAAgB,CAAC;YACrC,OAAO;YACP,OAAO,EAAE,OAAO,IAAI,IAAI;YACxB,UAAU,EAAE,MAAM,CAAC,UAAU;YAC7B,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,SAAS,EAAE,MAAM,CAAC,SAAS;YAC3B,UAAU,EAAE,MAAM,CAAC,UAAU;YAC7B,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,WAAW,EAAE,MAAM;SACpB,CAAC,CAAC;QACH,MAAM,wBAAwB,CAAC;YAC7B,OAAO;YACP,SAAS,EAAE,SAAS,CAAC,IAAI;YACzB,OAAO,EAAE,OAAO,IAAI,IAAI;YACxB,SAAS,EAAE,OAAO,CAAC,KAAK,EAAE,IAAI,IAAI,IAAI;YACtC,eAAe,EAAE,iBAAiB;YAClC,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,SAAS,EAAE,MAAM,CAAC,SAAS;YAC3B,eAAe;YACf,iBAAiB;SAClB,CAAC,CAAC;QACH,OAAO,sBAAsB,CAAC,OAAO,CAAC,CAAC;IACzC,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,gBAAgB,CAAC,QAAQ,CAAC,EAAE,EAAE;QAClD,UAAU,EAAE,MAAM,CAAC,UAAU;QAC7B,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,OAAO,EAAE,MAAM,CAAC,OAAO;KACxB,CAAC,CAAC;IACH,MAAM,wBAAwB,CAAC;QAC7B,OAAO;QACP,SAAS,EAAE,SAAS,CAAC,IAAI;QACzB,OAAO,EAAE,OAAO,IAAI,IAAI;QACxB,SAAS,EAAE,QAAQ,CAAC,KAAK,EAAE,IAAI,IAAI,IAAI;QACvC,eAAe,EAAE,iBAAiB;QAClC,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,eAAe;QACf,iBAAiB;KAClB,CAAC,CAAC;IACH,OAAO,sBAAsB,CAAC,OAAO,CAAC,CAAC;AACzC,CAAC;AAED,8DAA8D;AAC9D,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAC9C,MAAc,EACd,QAAgB;IAEhB,MAAM,kBAAkB,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAE3C,MAAM,KAAK,GAAG,IAAI,IAAI,EAAE,CAAC;IACzB,MAAM,QAAQ,GAAG,MAAM,iCAAiC,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IAC1E,MAAM,WAAW,GAAG,MAAM,4BAA4B,CAAC,QAAQ,CAAC,CAAC;IACjE,MAAM,aAAa,GAAG,WAAW,CAAC,MAAM,CAAC;IAEzC,MAAM,MAAM,GAAG,qBAAqB,EAAE,CAAC;IACvC,MAAM,IAAI,GAeL,EAAE,CAAC;IAER,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,MAAM,OAAO,GAAG,uBAAuB,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;QAC9E,MAAM,CAAC,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC;QAClC,MAAM,CAAC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;QAC5B,MAAM,CAAC,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC;QAChC,MAAM,CAAC,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC;QAClC,MAAM,iBAAiB,GAAG,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC;QACxD,MAAM,MAAM,GACV,OAAO,CAAC,OAAO,GAAG,OAAO,CAAC,IAAI,GAAG,OAAO,CAAC,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;QACpE,IAAI,CAAC,IAAI,CAAC;YACR,YAAY,EAAE,OAAO,CAAC,EAAE;YACxB,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,SAAS,EAAE,OAAO,CAAC,KAAK,CAAC,IAAI;YAC7B,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,OAAO;YAC9B,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,OAAO;YAC9B,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,SAAS,EAAE,OAAO,CAAC,KAAK,EAAE,IAAI,IAAI,IAAI;YACtC,IAAI,EAAE,OAAO,CAAC,KAAK,EAAE,SAAS,IAAI,OAAO,CAAC,IAAI;YAC9C,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,QAAQ,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,iBAAiB,GAAG,MAAM,CAAC;YACjD,iBAAiB;SAClB,CAAC,CAAC;IACL,CAAC;IAED,OAAO;QACL,IAAI,EAAE,UAAU,CAAC,KAAK,CAAC;QACvB,aAAa;QACb,MAAM,EAAE,MAAM;QACd,UAAU,EAAE,2BAA2B,CAAC,MAAM,CAAC;QAC/C,QAAQ,EAAE,IAAI;KACf,CAAC;AACJ,CAAC;AAED,iFAAiF;AACjF,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAC9C,MAAc,EACd,KAKC;IAED,MAAM,kBAAkB,CAAC,KAAK,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAEjD,MAAM,WAAW,GAAG,MAAM,4BAA4B,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IACvE,MAAM,QAAQ,GAAG,KAAK,CAAC,SAAS;QAC9B,CAAC,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,KAAK,CAAC,SAAS,CAAC;QACzD,CAAC,CAAC,WAAW,CAAC;IAChB,MAAM,UAAU,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IAEjD,MAAM,OAAO,GAAG,MAAM,sCAAsC,CAAC;QAC3D,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,UAAU;QACV,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,EAAE,EAAE,KAAK,CAAC,EAAE;KACb,CAAC,CAAC;IAEH,MAAM,SAAS,GAAG,IAAI,GAAG,EAAoD,CAAC;IAC9E,KAAK,MAAM,EAAE,IAAI,UAAU,EAAE,CAAC;QAC5B,SAAS,CAAC,GAAG,CAAC,EAAE,EAAE,qBAAqB,EAAE,CAAC,CAAC;IAC7C,CAAC;IACD,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,MAAM,MAAM,GAAG,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAC/C,IAAI,CAAC,MAAM;YAAE,SAAS;QACtB,QAAQ,MAAM,CAAC,MAAM,EAAE,CAAC;YACtB,KAAK,SAAS;gBACZ,MAAM,CAAC,OAAO,IAAI,CAAC,CAAC;gBACpB,MAAM;YACR,KAAK,MAAM;gBACT,MAAM,CAAC,IAAI,IAAI,CAAC,CAAC;gBACjB,MAAM;YACR,KAAK,QAAQ;gBACX,MAAM,CAAC,MAAM,IAAI,CAAC,CAAC;gBACnB,MAAM;YACR,KAAK,SAAS;gBACZ,MAAM,CAAC,OAAO,IAAI,CAAC,CAAC;gBACpB,MAAM;YACR;gBACE,MAAM;QACV,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE;QACjC,MAAM,MAAM,GAAG,SAAS,CAAC,GAAG,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,qBAAqB,EAAE,CAAC;QAC3E,OAAO;YACL,SAAS,EAAE,UAAU,CAAC,MAAM;YAC5B,QAAQ,EAAE,UAAU,CAAC,IAAI,CAAC,QAAQ;YAClC,WAAW,EAAE,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,IAAI,IAAI;YACzD,cAAc,EAAE,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,cAAc,IAAI,IAAI;YAC/D,uBAAuB,EACrB,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,uBAAuB,IAAI,IAAI;YAC1D,OAAO,EAAE,UAAU,CAAC,IAAI,CAAC,SAAS;YAClC,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,oBAAoB,EAAE,2BAA2B,CAAC,MAAM,CAAC;SAC1D,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,MAAc,EACd,KAIC;IAED,MAAM,kBAAkB,CAAC,KAAK,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAEjD,MAAM,QAAQ,GAAG,MAAM,qBAAqB,CAAC,KAAK,CAAC,CAAC;IACpD,MAAM,GAAG,GAAG,UAAU,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;IAEnC,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE;QAC9B,IAAI,MAAM,GAAsC,QAAQ,CAAC;QACzD,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;YACtB,MAAM,GAAG,SAAS,CAAC;QACrB,CAAC;aAAM,IAAI,UAAU,CAAC,OAAO,CAAC,SAAS,CAAC,GAAG,GAAG,EAAE,CAAC;YAC/C,MAAM,GAAG,UAAU,CAAC;QACtB,CAAC;aAAM,IAAI,UAAU,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,GAAG,EAAE,CAAC;YAC7C,MAAM,GAAG,QAAQ,CAAC,CAAC,6DAA6D;QAClF,CAAC;QAED,OAAO;YACL,EAAE,EAAE,OAAO,CAAC,EAAE;YACd,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,SAAS,EAAE,OAAO,CAAC,SAAS;YAC5B,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,SAAS,EAAE,OAAO,CAAC,SAAS;YAC5B,SAAS,EAAE,OAAO,CAAC,SAAS;YAC5B,SAAS,EAAE,OAAO,CAAC,SAAS;YAC5B,MAAM;YACN,UAAU,EAAE,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;YACpD,QAAQ,EAAE,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBACrC,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE;gBAChB,QAAQ,EAAE,CAAC,CAAC,OAAO,CAAC,QAAQ;gBAC5B,WAAW,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,WAAW,IAAI,IAAI;gBACnD,cAAc,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,cAAc,IAAI,IAAI;gBACzD,uBAAuB,EACrB,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,uBAAuB,IAAI,IAAI;aACrD,CAAC,CAAC;YACH,SAAS,EAAE;gBACT,EAAE,EAAE,OAAO,CAAC,SAAS,CAAC,EAAE;gBACxB,QAAQ,EAAE,OAAO,CAAC,SAAS,CAAC,QAAQ;gBACpC,WAAW,EAAE,OAAO,CAAC,SAAS,CAAC,OAAO,EAAE,WAAW,IAAI,IAAI;aAC5D;YACD,SAAS,EAAE,OAAO,CAAC,SAAS;gBAC1B,CAAC,CAAC;oBACE,EAAE,EAAE,OAAO,CAAC,SAAS,CAAC,EAAE;oBACxB,QAAQ,EAAE,OAAO,CAAC,SAAS,CAAC,QAAQ;oBACpC,WAAW,EAAE,OAAO,CAAC,SAAS,CAAC,OAAO,EAAE,WAAW,IAAI,IAAI;iBAC5D;gBACH,CAAC,CAAC,IAAI;SACT,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,MAAc,EACd,KAOC;IAED,MAAM,kBAAkB,CAAC,KAAK,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAEjD,IAAI,UAAU,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,UAAU,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC;QAC5D,MAAM,IAAI,SAAS,CAAC;YAClB,IAAI,EAAE,aAAa;YACnB,OAAO,EAAE,uCAAuC;SACjD,CAAC,CAAC;IACL,CAAC;IAED,MAAM,gBAAgB,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC;IACxD,MAAM,WAAW,GAAG,MAAM,4BAA4B,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IACvE,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;IACnE,MAAM,OAAO,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,gBAAgB,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;IAC3E,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,SAAS,CAAC;YAClB,IAAI,EAAE,aAAa;YACnB,OAAO,EAAE,4DAA4D;SACtE,CAAC,CAAC;IACL,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,oBAAoB,CAAC;QACzC,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,UAAU,EAAE,gBAAgB;QAC5B,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,WAAW,EAAE,MAAM;KACpB,CAAC,CAAC;IAEH,MAAM,wBAAwB,CAAC;QAC7B,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,SAAS,EAAE,OAAO,CAAC,EAAE;QACrB,UAAU,EAAE,gBAAgB;QAC5B,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,WAAW,EAAE,MAAM;KACpB,CAAC,CAAC;IAEH,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,MAAc,EACd,KAQC;IAED,MAAM,kBAAkB,CAAC,KAAK,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAEjD,MAAM,QAAQ,GAAG,MAAM,eAAe,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IACxD,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,QAAQ,KAAK,KAAK,CAAC,QAAQ,EAAE,CAAC;QACtD,MAAM,IAAI,SAAS,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,mBAAmB,EAAE,CAAC,CAAC;IAC3E,CAAC;IACD,IAAI,QAAQ,CAAC,SAAS,EAAE,CAAC;QACvB,MAAM,IAAI,SAAS,CAAC;YAClB,IAAI,EAAE,aAAa;YACnB,OAAO,EAAE,iCAAiC;SAC3C,CAAC,CAAC;IACL,CAAC;IAED,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,IAAI,QAAQ,CAAC,SAAS,CAAC;IACxD,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,IAAI,QAAQ,CAAC,OAAO,CAAC;IAClD,IAAI,UAAU,CAAC,OAAO,CAAC,GAAG,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAChD,MAAM,IAAI,SAAS,CAAC;YAClB,IAAI,EAAE,aAAa;YACnB,OAAO,EAAE,uCAAuC;SACjD,CAAC,CAAC;IACL,CAAC;IAED,IAAI,UAAU,GAAG,KAAK,CAAC,UAAU;QAC/B,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QAChC,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IAE9C,IAAI,KAAK,CAAC,UAAU,EAAE,CAAC;QACrB,MAAM,WAAW,GAAG,MAAM,4BAA4B,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QACvE,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;QACnE,MAAM,OAAO,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,gBAAgB,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;QACrE,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACvB,MAAM,IAAI,SAAS,CAAC;gBAClB,IAAI,EAAE,aAAa;gBACnB,OAAO,EAAE,4DAA4D;aACtE,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,mEAAmE;IACnE,MAAM,iBAAiB,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IAEzC,MAAM,OAAO,GAAG,MAAM,oBAAoB,CAAC,KAAK,CAAC,SAAS,EAAE;QAC1D,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS;QACrD,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,IAAI,EAAE,KAAK,CAAC,IAAI;KACjB,CAAC,CAAC;IAEH,MAAM,wBAAwB,CAAC;QAC7B,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,SAAS,EAAE,OAAO,CAAC,EAAE;QACrB,UAAU;QACV,SAAS;QACT,OAAO;QACP,WAAW,EAAE,MAAM;KACpB,CAAC,CAAC;IAEH,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,MAAc,EACd,KAA8C;IAE9C,MAAM,kBAAkB,CAAC,KAAK,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAEjD,MAAM,QAAQ,GAAG,MAAM,eAAe,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IACxD,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,QAAQ,KAAK,KAAK,CAAC,QAAQ,EAAE,CAAC;QACtD,MAAM,IAAI,SAAS,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,mBAAmB,EAAE,CAAC,CAAC;IAC3E,CAAC;IACD,IAAI,QAAQ,CAAC,SAAS,EAAE,CAAC;QACvB,MAAM,IAAI,SAAS,CAAC;YAClB,IAAI,EAAE,aAAa;YACnB,OAAO,EAAE,4BAA4B;SACtC,CAAC,CAAC;IACL,CAAC;IAED,MAAM,iBAAiB,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IACzC,OAAO,oBAAoB,CAAC,KAAK,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;AACvD,CAAC;AAED,uEAAuE;AACvE,KAAK,UAAU,wBAAwB,CAAC,KAUvC;IACC,MAAM,eAAe,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CACxD,CAAC,EAAE,EAAE,EAAE,CAAC,KAAK,CAAC,eAAe,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,GAAG,CAAC,EAAE,CAAC,CACxE,CAAC;IACF,MAAM,iBAAiB,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAC5D,CAAC,EAAE,EAAE,EAAE,CAAC,KAAK,CAAC,eAAe,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,GAAG,CAAC,EAAE,CAAC,CAC1E,CAAC;IACF,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO;QACxB,CAAC,CAAC,UAAU,KAAK,CAAC,OAAO,eAAe,KAAK,CAAC,OAAO,EAAE;QACvD,CAAC,CAAC,UAAU,KAAK,CAAC,OAAO,aAAa,CAAC;IACzC,MAAM,eAAe,GAAG,KAAK,CAAC,SAAS;QACrC,CAAC,CAAC,IAAI,KAAK,CAAC,SAAS,QAAQ,KAAK,CAAC,SAAS,EAAE;QAC9C,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC;IAEpB,MAAM,OAAO,CAAC,GAAG,CAAC;QAChB,0BAA0B,CAAC;YACzB,WAAW,EAAE,eAAe;YAC5B,KAAK,EAAE,kBAAkB,KAAK,CAAC,SAAS,EAAE;YAC1C,OAAO,EAAE,4BAA4B,eAAe,wBAAwB,IAAI,GAAG;YACnF,IAAI,EAAE,YAAY;YAClB,UAAU,EAAE,YAAY;YACxB,QAAQ,EAAE,KAAK,CAAC,OAAO;YACvB,OAAO,EAAE,KAAK,CAAC,OAAO;SACvB,CAAC;QACF,0BAA0B,CAAC;YACzB,WAAW,EAAE,iBAAiB;YAC9B,KAAK,EAAE,oBAAoB,KAAK,CAAC,SAAS,EAAE;YAC5C,OAAO,EAAE,8BAA8B,eAAe,wBAAwB,IAAI,GAAG;YACrF,IAAI,EAAE,YAAY;YAClB,UAAU,EAAE,YAAY;YACxB,QAAQ,EAAE,KAAK,CAAC,OAAO;YACvB,OAAO,EAAE,KAAK,CAAC,OAAO;SACvB,CAAC;KACH,CAAC,CAAC;AACL,CAAC;AAED,kFAAkF;AAClF,KAAK,UAAU,0BAA0B,CAAC,KAQzC;IACC,IAAI,KAAK,CAAC,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACnC,OAAO;IACT,CAAC;IAED,IAAI,CAAC;QACH,MAAM,cAAc,CAAC,KAAK,CAAC,CAAC;IAC9B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,KAAK,CAAC,0CAA0C,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;IACtE,CAAC;AACH,CAAC;AAED,gDAAgD;AAChD,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,WAAW,EAAE,CAAC","debug_id":"61646503-b6ff-5d4c-8251-5ecf346dad65"}
|
|
1
|
+
{"version":3,"file":"attendance.js","sources":["services/attendance.ts"],"sourceRoot":"/","sourcesContent":["/**\n * Attendance service – class attendance + school-admin excusals / stats.\n * Dual-writes legacy present/late/absent lists and per-student AttendanceRecord.\n * Active StudentExcusal rows force EXCUSED (locked for teachers).\n */\nimport { TRPCError } from \"@trpc/server\";\nimport {\n findClassWithStudents,\n findClassWithTeacher,\n findEventsByClassId,\n findAttendanceByEventId,\n findAttendanceByClassAndEvent,\n findManyAttendance,\n createAttendance,\n createAttendanceForEvent,\n updateAttendance,\n findActiveExcusalsForStudents,\n listExcusalsForSchool,\n findExcusalById,\n createStudentExcusal,\n updateStudentExcusal,\n revokeStudentExcusal,\n applyExcusalToAttendance,\n clearExcusalLocks,\n listSchoolStudentMemberships,\n listAttendanceRecordsForSchoolStudents,\n listTodaySchoolAttendanceSessions,\n toDateOnly,\n startOfUtcDay,\n endOfUtcDay,\n} from \"../models/attendance.js\";\nimport { findActiveSchoolAdminMembership } from \"../models/school.js\";\nimport { sendToMultiple } from \"./notification.js\";\nimport { logger } from \"../utils/logger.js\";\nimport {\n buildExcusalLocks,\n mergeAttendanceUpdateWithLocks,\n type StudentRef,\n} from \"./attendanceMerge.js\";\nimport {\n computeAttendancePercentage,\n emptyAttendanceCounts,\n tallyAttendanceStatuses,\n} from \"./attendanceStats.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\nfunction sessionDate(attendance: {\n date: Date;\n event?: { startTime: Date } | null;\n}): Date {\n return attendance.event?.startTime ?? attendance.date;\n}\n\n/** Get attendance for a class (optionally filtered by event). Creates missing records. */\nexport async function getAttendance(classId: string, eventId?: string) {\n const classData = await findClassWithStudents(classId);\n if (!classData) {\n throw new TRPCError({\n code: \"UNAUTHORIZED\",\n message: \"You are not authorized to view this class's attendance\",\n });\n }\n\n const events = await findEventsByClassId(classId);\n const studentIds = classData.students.map((s) => s.id);\n\n for (const event of events) {\n const existing = await findAttendanceByEventId(event.id);\n if (!existing) {\n const locks =\n classData.schoolId && studentIds.length > 0\n ? buildExcusalLocks(\n await findActiveExcusalsForStudents({\n schoolId: classData.schoolId,\n studentIds,\n date: event.startTime,\n }),\n studentIds\n )\n : [];\n\n const merged = mergeAttendanceUpdateWithLocks({\n present: classData.students.map((s) => ({\n id: s.id,\n username: s.username,\n })),\n late: [],\n absent: [],\n locks,\n });\n\n await createAttendanceForEvent({\n classId,\n eventId: event.id,\n date: event.startTime,\n presentIds: merged.presentIds,\n lateIds: merged.lateIds,\n absentIds: merged.absentIds,\n excusedIds: merged.excusedIds,\n records: merged.records,\n });\n } else if (classData.schoolId && studentIds.length > 0) {\n // Ensure active excusals are reflected on existing sessions.\n const locks = buildExcusalLocks(\n await findActiveExcusalsForStudents({\n schoolId: classData.schoolId,\n studentIds,\n date: event.startTime,\n }),\n studentIds\n );\n const needsLock = locks.filter((lock) => {\n const current = existing.records.find((r) => r.studentId === lock.studentId);\n return !(\n current?.status === \"EXCUSED\" &&\n current.excusedByExcusalId === lock.excusalId\n );\n });\n const byExcusal = new Map<string, string[]>();\n for (const lock of needsLock) {\n const list = byExcusal.get(lock.excusalId) ?? [];\n list.push(lock.studentId);\n byExcusal.set(lock.excusalId, list);\n }\n for (const [excusalId, lockedStudentIds] of byExcusal) {\n await applyExcusalToAttendance({\n schoolId: classData.schoolId,\n excusalId,\n studentIds: lockedStudentIds,\n startDate: event.startTime,\n endDate: event.startTime,\n });\n }\n }\n }\n\n const sessions = await findManyAttendance(classId, eventId);\n return sessions.map(shapeAttendanceSession);\n}\n\n/** Additive shape: keep present/late/absent; expose excused + lock metadata for UI. */\nfunction shapeAttendanceSession<\n T extends {\n records: Array<{\n id: string;\n studentId: string;\n status: string;\n note: string | null;\n excusedByExcusalId: string | null;\n updatedById: string | null;\n updatedAt: Date;\n student: { id: string; username: string };\n excusedByExcusal: {\n id: string;\n reason: string;\n note: string | null;\n } | null;\n }>;\n present: unknown[];\n late: unknown[];\n absent: unknown[];\n },\n>(session: T) {\n const records = session.records.map((r) => ({\n ...r,\n lockedFromExcusal: Boolean(r.excusedByExcusalId),\n reason: r.excusedByExcusal?.reason ?? null,\n excusalNote: r.excusedByExcusal?.note ?? null,\n }));\n const excused = records\n .filter((r) => r.status === \"EXCUSED\")\n .map((r) => r.student);\n\n return {\n ...session,\n excused,\n records,\n };\n}\n\n/** Update attendance record (present/late/absent/excused). Teacher-only. */\nexport async function updateAttendanceRecord(\n userId: string,\n classId: string,\n eventId: string | null | undefined,\n attendance: {\n present: StudentRef[];\n late: StudentRef[];\n absent: StudentRef[];\n excused?: StudentRef[];\n }\n) {\n const classData = await findClassWithTeacher(classId, userId);\n if (!classData) {\n throw new TRPCError({\n code: \"UNAUTHORIZED\",\n message: \"You are not authorized to update this class's attendance\",\n });\n }\n\n const existing = await findAttendanceByClassAndEvent(classId, eventId ?? null);\n const dateForExcusal = existing\n ? sessionDate(existing)\n : new Date();\n\n const classStudentIds = classData.students.map((s) => s.id);\n const locks =\n classData.schoolId && classStudentIds.length > 0\n ? buildExcusalLocks(\n await findActiveExcusalsForStudents({\n schoolId: classData.schoolId,\n studentIds: classStudentIds,\n date: dateForExcusal,\n }),\n classStudentIds\n )\n : [];\n\n const existingNotes = new Map(\n (existing?.records ?? []).map((r) => [r.studentId, r.note])\n );\n\n const merged = mergeAttendanceUpdateWithLocks({\n present: attendance.present,\n late: attendance.late,\n absent: attendance.absent,\n excused: attendance.excused,\n locks,\n updatedById: userId,\n existingNotesByStudentId: existingNotes,\n });\n\n const previousLateIds = new Set(existing?.late.map((student) => student.id) ?? []);\n const previousAbsentIds = new Set(\n existing?.absent.map((student) => student.id) ?? []\n );\n const classStudentIdSet = new Set(classStudentIds);\n\n if (!existing) {\n const created = await createAttendance({\n classId,\n eventId: eventId ?? null,\n presentIds: merged.presentIds,\n lateIds: merged.lateIds,\n absentIds: merged.absentIds,\n excusedIds: merged.excusedIds,\n records: merged.records,\n updatedById: userId,\n });\n await notifyMarkedLateOrAbsent({\n classId,\n className: classData.name,\n eventId: eventId ?? null,\n eventName: created.event?.name ?? null,\n classStudentIds: classStudentIdSet,\n lateIds: merged.lateIds,\n absentIds: merged.absentIds,\n previousLateIds,\n previousAbsentIds,\n });\n return shapeAttendanceSession(created);\n }\n\n const updated = await updateAttendance(existing.id, {\n presentIds: merged.presentIds,\n lateIds: merged.lateIds,\n absentIds: merged.absentIds,\n records: merged.records,\n });\n await notifyMarkedLateOrAbsent({\n classId,\n className: classData.name,\n eventId: eventId ?? null,\n eventName: existing.event?.name ?? null,\n classStudentIds: classStudentIdSet,\n lateIds: merged.lateIds,\n absentIds: merged.absentIds,\n previousLateIds,\n previousAbsentIds,\n });\n return shapeAttendanceSession(updated);\n}\n\n/**\n * School-admin: today's attendance rollup across classes.\n * `percentage` is session-weighted (share of attended class-session records),\n * not a share of `totalStudents`; a student in six classes contributes six records.\n */\nexport async function listTodayAttendanceSummary(\n userId: string,\n schoolId: string\n) {\n await requireSchoolAdmin(schoolId, userId);\n\n const today = new Date();\n const sessions = await listTodaySchoolAttendanceSessions(schoolId, today);\n const memberships = await listSchoolStudentMemberships(schoolId);\n const totalStudents = memberships.length;\n\n const counts = emptyAttendanceCounts();\n const rows: Array<{\n attendanceId: string;\n classId: string;\n className: string;\n section: string;\n subject: string;\n eventId: string | null;\n eventName: string | null;\n date: Date;\n present: number;\n late: number;\n absent: number;\n excused: number;\n unmarked: number;\n classStudentCount: number;\n }> = [];\n\n for (const session of sessions) {\n const tallied = tallyAttendanceStatuses(session.records.map((r) => r.status));\n counts.present += tallied.present;\n counts.late += tallied.late;\n counts.absent += tallied.absent;\n counts.excused += tallied.excused;\n const classStudentCount = session.class._count.students;\n const marked =\n tallied.present + tallied.late + tallied.absent + tallied.excused;\n rows.push({\n attendanceId: session.id,\n classId: session.classId,\n className: session.class.name,\n section: session.class.section,\n subject: session.class.subject,\n eventId: session.eventId,\n eventName: session.event?.name ?? null,\n date: session.event?.startTime ?? session.date,\n present: tallied.present,\n late: tallied.late,\n absent: tallied.absent,\n excused: tallied.excused,\n unmarked: Math.max(0, classStudentCount - marked),\n classStudentCount,\n });\n }\n\n return {\n date: toDateOnly(today),\n totalStudents,\n totals: counts,\n percentage: computeAttendancePercentage(counts),\n sessions: rows,\n };\n}\n\n/** School-admin: per-student attendance counts + % (excused excluded from %). */\nexport async function listStudentAttendanceStats(\n userId: string,\n input: {\n schoolId: string;\n from?: Date;\n to?: Date;\n studentId?: string;\n }\n) {\n await requireSchoolAdmin(input.schoolId, userId);\n\n const memberships = await listSchoolStudentMemberships(input.schoolId);\n const filtered = input.studentId\n ? memberships.filter((m) => m.userId === input.studentId)\n : memberships;\n const studentIds = filtered.map((m) => m.userId);\n\n const records = await listAttendanceRecordsForSchoolStudents({\n schoolId: input.schoolId,\n studentIds,\n from: input.from,\n to: input.to,\n });\n\n const byStudent = new Map<string, ReturnType<typeof emptyAttendanceCounts>>();\n for (const id of studentIds) {\n byStudent.set(id, emptyAttendanceCounts());\n }\n for (const record of records) {\n const counts = byStudent.get(record.studentId);\n if (!counts) continue;\n switch (record.status) {\n case \"PRESENT\":\n counts.present += 1;\n break;\n case \"LATE\":\n counts.late += 1;\n break;\n case \"ABSENT\":\n counts.absent += 1;\n break;\n case \"EXCUSED\":\n counts.excused += 1;\n break;\n default:\n break;\n }\n }\n\n return filtered.map((membership) => {\n const counts = byStudent.get(membership.userId) ?? emptyAttendanceCounts();\n return {\n studentId: membership.userId,\n username: membership.user.username,\n displayName: membership.user.profile?.displayName ?? null,\n profilePicture: membership.user.profile?.profilePicture ?? null,\n profilePictureThumbnail:\n membership.user.profile?.profilePictureThumbnail ?? null,\n classes: membership.user.studentIn,\n present: counts.present,\n late: counts.late,\n absent: counts.absent,\n excused: counts.excused,\n attendancePercentage: computeAttendancePercentage(counts),\n };\n });\n}\n\nexport async function listExcusals(\n userId: string,\n input: {\n schoolId: string;\n includeRevoked?: boolean;\n studentId?: string;\n }\n) {\n await requireSchoolAdmin(input.schoolId, userId);\n\n const excusals = await listExcusalsForSchool(input);\n const now = toDateOnly(new Date());\n\n return excusals.map((excusal) => {\n let status: \"ACTIVE\" | \"UPCOMING\" | \"EXPIRED\" | \"REVOKED\" = \"ACTIVE\";\n if (excusal.revokedAt) {\n status = \"REVOKED\";\n } else if (toDateOnly(excusal.startDate) > now) {\n status = \"UPCOMING\";\n } else if (toDateOnly(excusal.endDate) < now) {\n status = \"EXPIRED\";\n }\n\n return {\n id: excusal.id,\n schoolId: excusal.schoolId,\n startDate: excusal.startDate,\n endDate: excusal.endDate,\n reason: excusal.reason,\n note: excusal.note,\n revokedAt: excusal.revokedAt,\n createdAt: excusal.createdAt,\n updatedAt: excusal.updatedAt,\n status,\n studentIds: excusal.students.map((s) => s.studentId),\n students: excusal.students.map((s) => ({\n id: s.student.id,\n username: s.student.username,\n displayName: s.student.profile?.displayName ?? null,\n profilePicture: s.student.profile?.profilePicture ?? null,\n profilePictureThumbnail:\n s.student.profile?.profilePictureThumbnail ?? null,\n })),\n createdBy: {\n id: excusal.createdBy.id,\n username: excusal.createdBy.username,\n displayName: excusal.createdBy.profile?.displayName ?? null,\n },\n revokedBy: excusal.revokedBy\n ? {\n id: excusal.revokedBy.id,\n username: excusal.revokedBy.username,\n displayName: excusal.revokedBy.profile?.displayName ?? null,\n }\n : null,\n };\n });\n}\n\nexport async function createExcusal(\n userId: string,\n input: {\n schoolId: string;\n studentIds: string[];\n startDate: Date;\n endDate: Date;\n reason: string;\n note?: string | null;\n }\n) {\n await requireSchoolAdmin(input.schoolId, userId);\n\n if (toDateOnly(input.endDate) < toDateOnly(input.startDate)) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"endDate must be on or after startDate\",\n });\n }\n\n const uniqueStudentIds = [...new Set(input.studentIds)];\n const memberships = await listSchoolStudentMemberships(input.schoolId);\n const schoolStudentIds = new Set(memberships.map((m) => m.userId));\n const invalid = uniqueStudentIds.filter((id) => !schoolStudentIds.has(id));\n if (invalid.length > 0) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"One or more students are not active members of this school\",\n });\n }\n\n const excusal = await createStudentExcusal({\n schoolId: input.schoolId,\n studentIds: uniqueStudentIds,\n startDate: input.startDate,\n endDate: input.endDate,\n reason: input.reason,\n note: input.note,\n createdById: userId,\n });\n\n await applyExcusalToAttendance({\n schoolId: input.schoolId,\n excusalId: excusal.id,\n studentIds: uniqueStudentIds,\n startDate: input.startDate,\n endDate: input.endDate,\n updatedById: userId,\n });\n\n return excusal;\n}\n\nexport async function updateExcusal(\n userId: string,\n input: {\n schoolId: string;\n excusalId: string;\n studentIds?: string[];\n startDate?: Date;\n endDate?: Date;\n reason?: string;\n note?: string | null;\n }\n) {\n await requireSchoolAdmin(input.schoolId, userId);\n\n const existing = await findExcusalById(input.excusalId);\n if (!existing || existing.schoolId !== input.schoolId) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Excusal not found\" });\n }\n if (existing.revokedAt) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"Cannot update a revoked excusal\",\n });\n }\n\n const startDate = input.startDate ?? existing.startDate;\n const endDate = input.endDate ?? existing.endDate;\n if (toDateOnly(endDate) < toDateOnly(startDate)) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"endDate must be on or after startDate\",\n });\n }\n\n let studentIds = input.studentIds\n ? [...new Set(input.studentIds)]\n : existing.students.map((s) => s.studentId);\n\n if (input.studentIds) {\n const memberships = await listSchoolStudentMemberships(input.schoolId);\n const schoolStudentIds = new Set(memberships.map((m) => m.userId));\n const invalid = studentIds.filter((id) => !schoolStudentIds.has(id));\n if (invalid.length > 0) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"One or more students are not active members of this school\",\n });\n }\n }\n\n // Clear previous locks, then re-apply for the new window/students.\n await clearExcusalLocks(input.excusalId);\n\n const updated = await updateStudentExcusal(input.excusalId, {\n studentIds: input.studentIds ? studentIds : undefined,\n startDate: input.startDate,\n endDate: input.endDate,\n reason: input.reason,\n note: input.note,\n });\n\n await applyExcusalToAttendance({\n schoolId: input.schoolId,\n excusalId: updated.id,\n studentIds,\n startDate,\n endDate,\n updatedById: userId,\n });\n\n return updated;\n}\n\nexport async function revokeExcusal(\n userId: string,\n input: { schoolId: string; excusalId: string }\n) {\n await requireSchoolAdmin(input.schoolId, userId);\n\n const existing = await findExcusalById(input.excusalId);\n if (!existing || existing.schoolId !== input.schoolId) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Excusal not found\" });\n }\n if (existing.revokedAt) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"Excusal is already revoked\",\n });\n }\n\n await clearExcusalLocks(input.excusalId);\n return revokeStudentExcusal(input.excusalId, userId);\n}\n\n/** Notify only students whose late or absent status is newly added. */\nasync function notifyMarkedLateOrAbsent(input: {\n classId: string;\n className: string;\n eventId: string | null;\n eventName: string | null;\n classStudentIds: Set<string>;\n lateIds: string[];\n absentIds: string[];\n previousLateIds: Set<string>;\n previousAbsentIds: Set<string>;\n}) {\n const lateReceiverIds = [...new Set(input.lateIds)].filter(\n (id) => input.classStudentIds.has(id) && !input.previousLateIds.has(id)\n );\n const absentReceiverIds = [...new Set(input.absentIds)].filter(\n (id) => input.classStudentIds.has(id) && !input.previousAbsentIds.has(id)\n );\n const link = input.eventId\n ? `/class/${input.classId}/attendance/${input.eventId}`\n : `/class/${input.classId}/attendance`;\n const attendanceLabel = input.eventName\n ? `\"${input.eventName}\" in ${input.className}`\n : input.className;\n\n await Promise.all([\n sendAttendanceNotification({\n receiverIds: lateReceiverIds,\n title: `Marked late in ${input.className}`,\n content: `You were marked late for ${attendanceLabel}.\\n[View attendance](${link})`,\n type: \"ATTENDANCE\",\n entityType: \"ATTENDANCE\",\n entityId: input.eventId,\n classId: input.classId,\n }),\n sendAttendanceNotification({\n receiverIds: absentReceiverIds,\n title: `Marked absent in ${input.className}`,\n content: `You were marked absent for ${attendanceLabel}.\\n[View attendance](${link})`,\n type: \"ATTENDANCE\",\n entityType: \"ATTENDANCE\",\n entityId: input.eventId,\n classId: input.classId,\n }),\n ]);\n}\n\n/** Send attendance notifications without failing the attendance update itself. */\nasync function sendAttendanceNotification(input: {\n receiverIds: string[];\n title: string;\n content: string;\n type: \"ATTENDANCE\";\n entityType: string;\n entityId: string | null;\n classId: string;\n}) {\n if (input.receiverIds.length === 0) {\n return;\n }\n\n try {\n await sendToMultiple(input);\n } catch (error) {\n logger.error(\"Failed to send attendance notifications:\", { error });\n }\n}\n\n// Re-export date helpers used by callers/tests.\nexport { toDateOnly, startOfUtcDay, endOfUtcDay };\n"],"names":[],"mappings":"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","debug_id":"a0baad8d-f286-5756-9dfd-397505318152"}
|
package/dist/services/class.d.ts
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Class service – CRUD for classes, members, grades, mark schemes, grading boundaries,
|
|
3
|
+
* syllabus, lab drafts, and invite codes.
|
|
4
|
+
*/
|
|
5
|
+
import { Prisma } from "@prisma/client";
|
|
1
6
|
import { Class, Folder } from "@prisma/client";
|
|
2
7
|
/** Get all classes where user is teacher or student, with assignments and members. */
|
|
3
8
|
export declare function getAllClasses(userId: string): Promise<{
|
|
@@ -226,7 +231,7 @@ export declare function getClass(userId: string, classId: string): Promise<{
|
|
|
226
231
|
discussionsEnabled: boolean;
|
|
227
232
|
studentsCanCreateDiscussions: boolean;
|
|
228
233
|
studentsCanReply: boolean;
|
|
229
|
-
sidebarFeatures:
|
|
234
|
+
sidebarFeatures: Prisma.JsonValue | null;
|
|
230
235
|
treatMissingAsZero: boolean;
|
|
231
236
|
gradeRoundingMode: import(".prisma/client").$Enums.GradeRoundingMode;
|
|
232
237
|
};
|
|
@@ -256,7 +261,7 @@ export declare function updateClass(classId: string, data: {
|
|
|
256
261
|
discussionsEnabled: boolean;
|
|
257
262
|
studentsCanCreateDiscussions: boolean;
|
|
258
263
|
studentsCanReply: boolean;
|
|
259
|
-
sidebarFeatures:
|
|
264
|
+
sidebarFeatures: Prisma.JsonValue | null;
|
|
260
265
|
treatMissingAsZero: boolean;
|
|
261
266
|
gradeRoundingMode: import(".prisma/client").$Enums.GradeRoundingMode;
|
|
262
267
|
};
|
|
@@ -305,7 +310,7 @@ export declare function createClass(userId: string, input: {
|
|
|
305
310
|
discussionsEnabled: boolean;
|
|
306
311
|
studentsCanCreateDiscussions: boolean;
|
|
307
312
|
studentsCanReply: boolean;
|
|
308
|
-
sidebarFeatures:
|
|
313
|
+
sidebarFeatures: Prisma.JsonValue | null;
|
|
309
314
|
treatMissingAsZero: boolean;
|
|
310
315
|
gradeRoundingMode: import(".prisma/client").$Enums.GradeRoundingMode;
|
|
311
316
|
}>;
|
|
@@ -340,7 +345,7 @@ export declare function changeRole(classId: string, userId: string, type: "teach
|
|
|
340
345
|
discussionsEnabled: boolean;
|
|
341
346
|
studentsCanCreateDiscussions: boolean;
|
|
342
347
|
studentsCanReply: boolean;
|
|
343
|
-
sidebarFeatures:
|
|
348
|
+
sidebarFeatures: Prisma.JsonValue | null;
|
|
344
349
|
treatMissingAsZero: boolean;
|
|
345
350
|
gradeRoundingMode: import(".prisma/client").$Enums.GradeRoundingMode;
|
|
346
351
|
};
|
|
@@ -364,7 +369,7 @@ export declare function removeMember(classId: string, userId: string): Promise<{
|
|
|
364
369
|
discussionsEnabled: boolean;
|
|
365
370
|
studentsCanCreateDiscussions: boolean;
|
|
366
371
|
studentsCanReply: boolean;
|
|
367
|
-
sidebarFeatures:
|
|
372
|
+
sidebarFeatures: Prisma.JsonValue | null;
|
|
368
373
|
treatMissingAsZero: boolean;
|
|
369
374
|
gradeRoundingMode: import(".prisma/client").$Enums.GradeRoundingMode;
|
|
370
375
|
};
|
|
@@ -539,7 +544,7 @@ export declare function updateSyllabus(classId: string, contents: string): Promi
|
|
|
539
544
|
discussionsEnabled: boolean;
|
|
540
545
|
studentsCanCreateDiscussions: boolean;
|
|
541
546
|
studentsCanReply: boolean;
|
|
542
|
-
sidebarFeatures:
|
|
547
|
+
sidebarFeatures: Prisma.JsonValue | null;
|
|
543
548
|
treatMissingAsZero: boolean;
|
|
544
549
|
gradeRoundingMode: import(".prisma/client").$Enums.GradeRoundingMode;
|
|
545
550
|
}>;
|
|
@@ -814,8 +819,8 @@ export declare function exportClass(classId: string, userId: string): Promise<{
|
|
|
814
819
|
classId: string;
|
|
815
820
|
questions: {
|
|
816
821
|
type: import(".prisma/client").$Enums.WorksheetQuestionType;
|
|
817
|
-
options:
|
|
818
|
-
markScheme:
|
|
822
|
+
options: Prisma.JsonValue;
|
|
823
|
+
markScheme: Prisma.JsonValue;
|
|
819
824
|
points: number;
|
|
820
825
|
question: string;
|
|
821
826
|
worksheetId: string;
|
|
@@ -845,7 +850,7 @@ export declare function exportClass(classId: string, userId: string): Promise<{
|
|
|
845
850
|
discussionsEnabled: boolean;
|
|
846
851
|
studentsCanCreateDiscussions: boolean;
|
|
847
852
|
studentsCanReply: boolean;
|
|
848
|
-
sidebarFeatures:
|
|
853
|
+
sidebarFeatures: Prisma.JsonValue | null;
|
|
849
854
|
treatMissingAsZero: boolean;
|
|
850
855
|
gradeRoundingMode: import(".prisma/client").$Enums.GradeRoundingMode;
|
|
851
856
|
}>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"class.d.ts","sourceRoot":"/","sources":["services/class.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"class.d.ts","sourceRoot":"/","sources":["services/class.ts"],"names":[],"mappings":"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"}
|
package/dist/services/class.js
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
* syllabus, lab drafts, and invite codes.
|
|
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]="
|
|
7
|
-
import { SessionType } from "@prisma/client";
|
|
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]="838e82cf-2053-5dee-a9f1-bc5d20761f0d")}catch(e){}}();
|
|
7
|
+
import { Prisma, SessionType } from "@prisma/client";
|
|
8
8
|
import { TRPCError } from "@trpc/server";
|
|
9
9
|
import { prisma } from "../lib/prisma.js";
|
|
10
10
|
import { generateInviteCode } from "../utils/generateInviteCode.js";
|
|
@@ -193,7 +193,7 @@ export async function updateClass(classId, data) {
|
|
|
193
193
|
studentsCanReply: data.studentsCanReply,
|
|
194
194
|
}),
|
|
195
195
|
...(data.sidebarFeatures !== undefined && {
|
|
196
|
-
sidebarFeatures: data.sidebarFeatures,
|
|
196
|
+
sidebarFeatures: data.sidebarFeatures ?? Prisma.DbNull,
|
|
197
197
|
}),
|
|
198
198
|
},
|
|
199
199
|
select: {
|
|
@@ -678,4 +678,4 @@ export async function importClass(classId, userId, year, classData) {
|
|
|
678
678
|
}
|
|
679
679
|
}
|
|
680
680
|
//# sourceMappingURL=class.js.map
|
|
681
|
-
//# debugId=
|
|
681
|
+
//# debugId=838e82cf-2053-5dee-a9f1-bc5d20761f0d
|