feat(TRA-236,TRA-238): M2 course domain model and dwell-time tracking
TRA-236 — Course domain model:
- courses/models.py: Course → Module → Lesson → Page hierarchy with UUID
PKs, ordering fields, required_seconds navigation gate, version tracking,
and UniqueConstraint per (parent, order) pair
- courses/migrations/0001_initial.py: initial migration (applies cleanly on
a fresh DB)
- tests/test_course_domain.py: migration smoke, relation integrity, cascade
delete, ordering, and uniqueness-constraint tests
TRA-238 — Dwell-time tracking:
- tracking/models.py: Enrollment, PageProgress (can_advance property), and
DwellEvent models appended alongside existing AuditEvent
- tracking/services.py: record_dwell_event, compute_eligible_seconds (pure),
check_navigation_gate, _recompute_accumulated — reconnect merging within
RECONNECT_TOLERANCE_SECONDS and anti-idle cap at MAX_VALID_EVENT_SECONDS
- tracking/migrations/0001_initial.py: updated to include all four models
(AuditEvent, Enrollment, PageProgress, DwellEvent) with FK dependencies
on courses.Course and courses.Page
- tests/test_dwell_tracking.py: event replay, reconnect tolerance, anti-idle
cap, gate pass/block, and can_advance DB integration tests
Co-Authored-By: Paperclip <noreply@paperclip.ing>