Database Schema
The complete schema is in core/models.py.
Core Entities
| Model | Purpose |
|---|---|
| Person | Card holder profile and identity data (is_archived flag controls active/archived status) |
| Device | Physical scanner identity and area assignment |
| Record | Card scan event, with optional class or event context |
| DeviceLog | Device heartbeat and connectivity telemetry |
| Department | Organizational grouping |
| Program | Academic program linked to a department with code and title |
| Area | Physical deployment location |
| Class | Class attendance domain model |
| Event | Event attendance domain model |
| Semester | Academic period model |
| UserLoginEvent | Audit log of successful user logins (IP, user agent, source) |
| UserActivity | Event log of user page visits with debounce (full history) |
| IDCard | Issued physical card instance for a Person; lifecycle state (draft, active, lost, damaged, expired) |
| IDCardPrintLog | Append-only audit log of every card print event (who printed, when, why) |
| Dataset | Asynchronous dataset export job and reusable template for ML and data visualization with strict PII anonymization |
Dataset export & ML hygiene:
Datasetstores export configurations (config), status (pending,processing,ready,failed,expired), file artifacts, and manifest summaries. Generated archives are automatically cleaned up from storage after a 7-day TTL while preserving the dataset definition for one-click re-generation. Zero-trust sanitization ensures personal PII (names, personal contacts, government IDs, images) is never exported.
ID card lifecycle:
Person.active_card(FK, nullable) points at the currently activeIDCard. Each print issues a newIDCard(statusactive) and expires the previous one (expired), so historical card numbers still resolve scans to the same person.IDCardPrintLogrecords thereason(first_issue,lost_replacement,damaged_replacement,reprint_error,info_update,test_print) and theprinted_byuser for auditing and per-person print-count reporting. The legacyPerson.card_number/id_image/id_image_backfields are retained for backward compatibility but deprecated in favour ofIDCard.Record attribution:
Recordstores the raw scannedcard_number(not a FK), so the admin list,Record.__str__, and the MCP attendance reports resolve the person viacore.card_resolution.resolve_person_by_card_number— which consultsIDCardhistory (includingexpiredcards) before falling back to the currentPerson.card_number. This keeps historical scans attributing to the right person after a card is reissued with a new RFID.
Model-Level Permissions
Custom model permissions are declared in core/models.py for visibility control and action authorization:
| Model | Permission Codename | Purpose |
|---|---|---|
| Class | view_all_classes | Permit viewing all Class records in admin without instructor/co-instructor restriction |
| Event | view_all_events | Permit viewing all Event records in admin without facilitator restriction |
| Person | import_persons | Permit importing person records from external files |
| Person | scan_persons | Permit access to the Scan Persons dashboard (only grants page/view access) |
| Person | person_id_registration | Permit registering new IDs |
| Person | migrate_card_numbers | Permit migrating legacy card numbers |
| AIAgent | access_ai_agent | Permit access to the Onyx AI chat widget in the admin portal |
Relationship Snapshot
- Department links to Person, Class, Event, Program, and access mappings
- Program belongs to a Department
- Device belongs to Area and emits DeviceLog entries
- Record links card scans to Device, the User who performed the scan, and optional Class or Event
- Semester scopes Class and Event timing windows
- UserLoginEvent and UserActivity link to the Django auth User model for audit trail