Attendance marking that works when the classroom has no signal
Schools in rural and semi-urban areas have classrooms where mobile data drops out for hours at a stretch. Teachers still need to mark attendance, and the ERP still needs accurate records at the end of the day. A purely online attendance module is useless in that situation — the teacher either marks on paper and someone re-enters it later, or the module gets quietly abandoned.
We solve this by making the attendance module work offline first. The teacher's device — usually a phone, sometimes a tablet — stores attendance locally and syncs when connectivity returns. The hard part is not the offline capture. The hard part is conflict resolution when the device comes back online and the server has its own version of the truth.
Here is the approach we take.
What the device stores
When a teacher opens the attendance screen, the app pulls the current roster for that class and section from local storage. If the roster is stale — a new student joined yesterday and the device has not synced — we flag it, but we do not block marking. The teacher marks present, absent, late, or absent-with-reason for each student. The entry is written to a local SQLite table with a status of `pending_sync`.
CREATE TABLE attendance_entries (
id INTEGER PRIMARY KEY AUTOINCREMENT,
class_id INTEGER NOT NULL,
section_id INTEGER NOT NULL,
student_id INTEGER NOT NULL,
date TEXT NOT NULL,
status TEXT NOT NULL CHECK(status IN ('present','absent','late','absent_with_reason')),
remark TEXT,
marked_by INTEGER NOT NULL,
marked_at TEXT NOT NULL,
sync_status TEXT DEFAULT 'pending_sync',
server_version INTEGER,
UNIQUE(class_id, section_id, student_id, date)
);The `UNIQUE` constraint matters. It means a single student on a single date has exactly one row. If the teacher re-opens the screen and changes an entry, we update the existing row rather than inserting a new one.
What happens on sync
When the device detects connectivity — we use a simple connectivity listener, not a background service that burns battery — it pushes all `pending_sync` rows to the server. The server does three things:
1. Checks if a row already exists for that student-date combination. 2. If not, inserts and returns a `server_version`. 3. If yes, compares timestamps and applies a conflict rule.
The conflict rule is straightforward: the entry with the later `marked_at` timestamp wins. We considered last-write-wins by server arrival time, but that penalises teachers whose devices were offline longer. Using `marked_at` — the moment the teacher actually made the decision — is more faithful to what happened in the classroom.
There is one exception. If an administrator has locked the attendance for a date — say, after generating a report for the trustees — the server rejects the sync and the device keeps the row as `pending_sync` with a `conflict_locked` flag. The teacher sees a message that attendance for that date is locked and contacts the admin.
Why not operational transform or CRDTs
We looked at operational transform and CRDT libraries. They are powerful for collaborative editing where multiple users are changing the same document in real time. Attendance is simpler. Each entry is a discrete fact — a student was present or absent on a date — and there is exactly one authoritative source per classroom per date: the teacher who was in the room. Operational transform adds complexity we do not need and would make the sync harder to debug.
The tradeoff we accept is that if two teachers mark the same class on the same date — one offline, one online — the later timestamp wins and the earlier entry is discarded. We log the discarded entry so an admin can review it. In practice this happens rarely, and when it does, the teacher who was actually in the classroom is usually the one whose device was offline.
What we do not do
We do not let the app run a persistent sync service in the background. It drains battery, and on lower-end Android devices it gets killed by the OS anyway. The sync runs when the app is open and connectivity is present, and once when the app launches. That covers the common case: teacher marks attendance, walks to the staff room where Wi-Fi is available, opens the app to check something, and the sync fires.
We also do not attempt to sync photos or signatures over mobile data. If a school requires biometric or photo-based attendance, we store those locally and sync only when the device is on Wi-Fi. The attendance status itself is a few hundred bytes per class; the media is megabytes. Treating them differently keeps the critical path fast.
The result is that a teacher in a signal-dead classroom marks attendance as if the ERP were fully online, and by the time the school office checks the dashboard an hour later, the entries are there.