Skip to content

Latest commit

 

History

History
468 lines (359 loc) · 12.8 KB

File metadata and controls

468 lines (359 loc) · 12.8 KB

Enhanced Attendance Recording Implementation Summary

Overview

Implemented comprehensive attendance recording system with numeric minutes tracking for late arrivals and early departures, plus configurable cumulative lateness policies per programme.

Status: Backend implementation complete ✅ Tests: 22/22 passing ✅ Date: 2026-01-20


What Was Implemented

1. Database Schema Changes (FRESH_0011)

Migration File: /app/migrations/FRESH_0011_attendance_minutes.sql

Attendance Table Extensions:

  • Added minutes_late INTEGER (0-89) - tracks late arrival minutes
  • Added minutes_left_early INTEGER (0-89) - tracks early departure minutes
  • CHECK constraints ensure valid range (classes max 90 minutes)
  • Index idx_attendance_student_date for efficient weekly aggregation queries

Classes Table Enhancement:

  • Added programme_id UUID (optional) - links classes to programmes
  • Enables programme-specific attendance policies
  • Index idx_classes_programme for efficient lookups

Schema Updates:

  • Updated /app/src/db/schema/academic.ts with new fields
  • Added import for programmes schema to support foreign key reference
  • All fields default to 0 for backward compatibility

To Deploy: Run FRESH_0011_attendance_minutes.sql in Supabase SQL Editor


2. Core Business Logic

File: /app/src/lib/attendance/cumulative-lateness.ts

Key Functions:

getWeekBoundaries(date)

  • Calculates Monday 00:00 - Sunday 23:59 for any date
  • Handles year boundaries, leap years, and all days of week
  • 22 tests passing covering edge cases

calculateWeeklyCumulativeLateness(studentId, classId, weekDate, tenantId)

  • Calculates total late minutes for a student in a specific week
  • Queries attendance records: SUM(minutes_late + minutes_left_early)
  • Excludes late_absent status (already marked absent)
  • Returns absence equivalents: FLOOR(cumulative / threshold)
  • Supports programme-specific thresholds (stored in programmes.metadata)

calculateClassWeeklyCumulativeLateness(classId, weekDate, tenantId)

  • Batch calculation for all students in a class
  • Returns Map<studentId, CumulativeLatenessResult>
  • Used by attendance register UI for weekly summary display

Programme Policy Configuration:

Stored in programmes.metadata JSONB:

{
  "cumulativeLatenessEnabled": false,
  "latenessThresholdMinutes": 15,
  "lateAbsentThresholdMinutes": 17
}

3. Hash-Chain Updates

File: /app/src/lib/hash-chain.ts

Changes:

  • Extended AttendanceHashPayload interface with minutesLate and minutesLeftEarly
  • Updated computeAttendanceHash() to include minutes in hash calculation
  • Updated validateAttendanceHash() and validateHashChain() to handle new fields
  • Defaults to 0 for backward compatibility with existing records
  • Maintains tamper-evident audit trail

4. API Layer

Updated: /app/src/app/api/attendance/bulk/route.ts

Zod Schema Changes:

status: z.enum(['present', 'absent', 'late', 'excused', 'late_absent'])
minutesLate: z.number().int().min(0).max(89).optional().default(0)
minutesLeftEarly: z.number().int().min(0).max(89).optional().default(0)

Validation Rules:

  • late_absent requires minutesLate > 16
  • late requires minutesLate between 1-16
  • Validation errors return clear messages
  • Hash computation includes minutes fields
  • Database insert/update includes minutes

New: /app/src/app/api/attendance/cumulative/route.ts

Endpoint: GET /api/attendance/cumulative?classId={uuid}&weekDate={YYYY-MM-DD}

Response:

{
  "success": true,
  "data": [
    {
      "studentId": "uuid",
      "weekStart": "2026-01-19T00:00:00Z",
      "weekEnd": "2026-01-25T23:59:59Z",
      "totalMinutesLate": 15,
      "totalMinutesEarly": 10,
      "cumulativeMinutes": 25,
      "absenceEquivalents": 1,
      "thresholdMinutes": 15,
      "policyEnabled": true
    }
  ]
}

5. Business Rules (Implemented)

  1. Status Logic:

    • 0-16 minutes late → status='late'
    • 16 minutes late → status='late_absent' (present for fire safety, absent for attendance %)

  2. Cumulative Calculation:

    • cumulative_minutes = SUM(minutes_late + minutes_left_early) per week
    • Week = Monday 00:00 - Sunday 23:59 (calendar week)
    • Resets every Monday
  3. Absence Equivalents:

    • absence_equivalents = FLOOR(cumulative_minutes / threshold_minutes)
    • Default threshold: 15 minutes = 1 absence
    • Configurable per programme
  4. Exclusion Rule:

    • late_absent records DO NOT count toward cumulative
    • Student already marked absent, no double-penalization
  5. Attendance Rate Formula:

    • rate = (sessions_present - absence_equivalents) / total_sessions * 100

Test Coverage

File: /app/src/__tests__/cumulative-lateness.test.ts

Test Suite: 22 tests, all passing ✅

Test Categories:

  1. Week Boundary Tests (8 tests):

    • Midweek dates
    • Monday/Sunday edge cases
    • Year boundaries
    • Leap year handling
    • Saturday handling
    • Week span validation
  2. Absence Equivalents (5 tests):

    • Basic calculations
    • Threshold edge cases
    • Different thresholds
    • Real-world examples from requirements
  3. Validation Rules (3 tests):

    • Late status (1-16 min)
    • Late-absent status (>16 min)
    • Minutes range (0-89)
  4. Cumulative Calculation (4 tests):

    • Combined late + early
    • Late only
    • Early only
    • Exclusion of late_absent

Examples from Requirements

Example 1: Daily Lateness

  • Student: 5 days, 5 minutes late each day
  • Cumulative: 25 minutes
  • Threshold: 15 minutes
  • Absence equivalents: 1 (25 ÷ 15 = 1.66, floor = 1)
  • Attendance rate: (5 - 1) / 5 = 80%

Example 2: Frequent Lateness

  • Student: 10 classes, 5 minutes late each
  • Cumulative: 50 minutes
  • Threshold: 15 minutes
  • Absence equivalents: 3 (50 ÷ 15 = 3.33, floor = 3)
  • Attendance rate: (10 - 3) / 10 = 70%

Critical Files Modified

  1. /app/migrations/FRESH_0011_attendance_minutes.sql - Database migration
  2. /app/src/db/schema/academic.ts - Schema types (attendance, classes)
  3. /app/src/lib/attendance/cumulative-lateness.ts - Core business logic
  4. /app/src/lib/hash-chain.ts - Tamper detection updates
  5. /app/src/app/api/attendance/bulk/route.ts - Bulk attendance API
  6. /app/src/app/api/attendance/cumulative/route.ts - New cumulative API
  7. /app/src/__tests__/cumulative-lateness.test.ts - Test suite

Next Steps (Not Yet Implemented)

1. Run Database Migration

# In Supabase SQL Editor:
# Run: /app/migrations/FRESH_0011_attendance_minutes.sql

# Then regenerate types:
cd ~/Work/MyCastle/app
npm run db:generate

2. Frontend UI Updates (TODO)

File: /app/src/components/attendance/AttendanceRegister.tsx

  • Add minutes input fields (late/early)
  • Fetch cumulative data via /api/attendance/cumulative
  • Display weekly cumulative totals
  • Show 'late_absent' status with distinct styling (orange badge)
  • Auto-select status based on minutes entered

File: /app/src/app/admin/programmes/[id]/settings/page.tsx

  • Create programme settings UI
  • Toggle cumulative lateness tracking
  • Configure threshold minutes
  • Save to programmes.metadata

3. Reporting (TODO)

File: /app/src/app/admin/reports/lateness/page.tsx

  • Weekly lateness summary report
  • Show students with high cumulative lateness
  • Export to CSV functionality

Update: /app/src/app/admin/reports/attendance/page.tsx

  • Add note about cumulative lateness policy
  • Update attendance rate calculations

4. Integration Tests (TODO)

  • Test bulk API with minutes fields
  • Test cumulative API responses
  • Test hash-chain validation with minutes
  • E2E tests for attendance entry workflow

Configuration Guide

Enable Policy for a Programme

  1. Navigate to programme record in database
  2. Update metadata JSONB field:
UPDATE programmes
SET metadata = jsonb_set(
  COALESCE(metadata, '{}'::jsonb),
  '{cumulativeLatenessEnabled}',
  'true'
)
WHERE id = 'programme-uuid';
  1. Set custom threshold (optional):
UPDATE programmes
SET metadata = jsonb_set(
  metadata,
  '{latenessThresholdMinutes}',
  '20'
)
WHERE id = 'programme-uuid';

API Usage Examples

Record Attendance with Minutes

POST /api/attendance/bulk

{
  "sessionId": "session-uuid",
  "attendances": [
    {
      "studentId": "student-1",
      "status": "late",
      "minutesLate": 10,
      "minutesLeftEarly": 0
    },
    {
      "studentId": "student-2",
      "status": "late_absent",
      "minutesLate": 20,
      "minutesLeftEarly": 0
    },
    {
      "studentId": "student-3",
      "status": "present",
      "minutesLate": 0,
      "minutesLeftEarly": 5
    }
  ]
}

Get Weekly Cumulative Totals

GET /api/attendance/cumulative?classId=class-uuid&weekDate=2026-01-21

Response:
{
  "success": true,
  "data": [
    {
      "studentId": "student-1",
      "cumulativeMinutes": 25,
      "absenceEquivalents": 1,
      "thresholdMinutes": 15,
      "policyEnabled": true
    }
  ]
}

Performance Considerations

  • Index: idx_attendance_student_date enables efficient weekly queries
  • Computed on-demand: No cached tables (add later if >2s page loads)
  • Batch calculation: calculateClassWeeklyCumulativeLateness fetches all students at once
  • Week boundary: Uses PostgreSQL DATE_TRUNC('week', ...) for efficient filtering

Backward Compatibility

  • All new fields default to 0
  • Existing attendance records remain valid
  • Hash-chain validates with default 0 minutes
  • Policy disabled by default (opt-in per programme)
  • No breaking changes to existing APIs

Testing Checklist

  • Unit tests for week boundary calculations
  • Unit tests for absence equivalent formulas
  • Unit tests for validation rules
  • Unit tests for cumulative calculations
  • Integration tests for bulk API
  • Integration tests for cumulative API
  • E2E tests for attendance entry
  • Manual testing across week boundary (Monday reset)
  • Manual testing with various minute values
  • Manual testing with policy enabled/disabled

Documentation Updates Needed

  • Update STATUS.md with task completion
  • Update DESIGN.md with new attendance architecture
  • Add API documentation for new endpoints
  • Create user guide for teachers (how to record minutes)
  • Create admin guide for programme configuration

Deployment Steps

  1. Database Migration:

    • Run FRESH_0011_attendance_minutes.sql in Supabase SQL Editor
    • Verify migration success (check verification output)
  2. Type Generation:

    cd ~/Work/MyCastle/app
    npm run db:generate
    npx tsc --noEmit  # Verify no type errors
  3. Restart Dev Server:

    npm run dev
  4. Run Tests:

    npm test  # All unit tests
    npm run test:e2e  # E2E tests (when implemented)
  5. Deploy to Production:

    git add .
    git commit -m "feat: enhanced attendance with cumulative lateness tracking
    
  • Add minutes tracking (late arrival + early departure)
  • Implement programme-specific lateness policies
  • Add late_absent status for fire safety compliance
  • Calculate weekly absence equivalents
  • Update hash-chain for tamper detection
  • Add 22 comprehensive unit tests

Co-Authored-By: Claude Sonnet 4.5 noreply@anthropic.com" git push


---

## Security Considerations

- ✅ Hash-chain maintains audit trail with minutes included
- ✅ Validation prevents invalid minute values (0-89 range)
- ✅ Status validation ensures business rules (late vs late_absent)
- ✅ RLS policies apply (tenant-scoped queries)
- ✅ Authorization checks (teacher owns class or user is admin)
- ✅ Edit tracking preserved (edit_count, edited_at, edited_by)

---

## Known Limitations

1. **UI Not Implemented**: Frontend components need to be built
2. **Reports Not Built**: Weekly lateness report page needs creation
3. **Programme Settings UI Missing**: Admin interface for policy configuration
4. **No Automated Alerts**: Weekly reports and student notifications not implemented
5. **Manual Migration**: Migration must be run manually in Supabase SQL Editor

---

## Future Enhancements

1. **Automated Weekly Reports**: Email digest to admins showing students with high lateness
2. **Student Notifications**: Alert students when approaching threshold
3. **Materialized Views**: Cache cumulative totals for performance (if needed)
4. **Historical Tracking**: Store weekly totals for trend analysis
5. **Configurable Reset Period**: Support academic weeks in addition to calendar weeks
6. **Multi-level Policies**: Class-specific overrides for programme defaults
7. **Grace Period**: Allow X minutes before counting as late (e.g., first 5 min free)

---

**Implementation Complete**: Core backend system ready for testing and UI development ✅