- Implemented AbsenceNotifyView for notifying absence with session details. - Created AnalyticsView for displaying group analytics and managing students at risk. - Added ClassAttendanceView for marking attendance in classes. - Defined new schemas for analytics and attendance in shared package.
56 lines
1.6 KiB
TypeScript
56 lines
1.6 KiB
TypeScript
export type AttendanceStreamEntry = {
|
|
status: 'PRESENT' | 'ABSENT' | 'EXCUSED';
|
|
occurredAt: Date;
|
|
};
|
|
|
|
export type RiskLevel = 'HIGH' | 'MEDIUM';
|
|
|
|
// Cuenta las ausencias consecutivas sin justificar desde la clase más reciente.
|
|
// Recibe la lista ordenada de más reciente a más antigua; PRESENT y EXCUSED rompen la cadena.
|
|
export function trailingAbsentStreak(entries: AttendanceStreamEntry[]): number {
|
|
let streak = 0;
|
|
for (const entry of entries) {
|
|
if (entry.status !== 'ABSENT') {
|
|
break;
|
|
}
|
|
streak += 1;
|
|
}
|
|
return streak;
|
|
}
|
|
|
|
// Fecha de la última clase a la que el alumno asistió (PRESENT), o null.
|
|
export function lastAttendedAt(entries: AttendanceStreamEntry[]): Date | null {
|
|
for (const entry of entries) {
|
|
if (entry.status === 'PRESENT') {
|
|
return entry.occurredAt;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
// Porcentaje de asistencia sobre el total de sesiones del período.
|
|
// Las sesiones sin registrar cuentan como no asistidas.
|
|
export function monthlyAttendanceRate(
|
|
entries: AttendanceStreamEntry[],
|
|
totalSessions: number,
|
|
): number {
|
|
if (totalSessions <= 0) {
|
|
return 0;
|
|
}
|
|
const present = entries.filter((entry) => entry.status === 'PRESENT').length;
|
|
return Math.round((present / totalSessions) * 100);
|
|
}
|
|
|
|
// HIGH >= 3 ausencias consecutivas; MEDIUM == 2 consecutivas o asistencia mensual < 50%.
|
|
export function classifyRisk(
|
|
consecutiveAbsences: number,
|
|
monthlyRate: number,
|
|
): RiskLevel | null {
|
|
if (consecutiveAbsences >= 3) {
|
|
return 'HIGH';
|
|
}
|
|
if (consecutiveAbsences === 2 || monthlyRate < 50) {
|
|
return 'MEDIUM';
|
|
}
|
|
return null;
|
|
} |