A teacher opens a camera, students are recognised and marked present, absentees' guardians get an email. Nobody else's data is ever reachable.
Roll calls burn class time, and paper registers are easy to lose and easy to fake. EduGuard AI lets a teacher register a class once, then take attendance by pointing a camera at the room, with a manual override for the cases a camera gets wrong.
The interesting engineering problem was not the face matching, it was making it safe to share one app between many teachers. Every class, student, attendance session, export and notification endpoint verifies that the logged-in teacher owns the resource before touching it, and that behaviour is covered by automated tests.
- Teacher accounts: register / login / logout with hashed passwords (Werkzeug), username, email and password-length validation.
- Class and student management with per-class uniqueness on student ID (
UNIQUE(student_id, class_id)), so two classes can reuse roll numbers. - Face registration from uploaded student photos (
.jpg,.jpeg,.png,.webponly); a face encoding is computed and stored with the student. - Camera attendance: a separate OpenCV process compares live faces against the class's stored encodings, tracks faces across frames, and writes the marked students to a results file the web app polls.
- Manual override: toggle any student present/absent inline through
/api/mark_attendance. - Absence notifications by email (SMTP) only for absent students who have not been notified yet; failures stay pending for retry.
- Exports: attendance sheets and class rosters as PDF (ReportLab) or Excel (openpyxl).
- Ownership checks on protected resources, with JSON
403on API endpoints and flash-and-redirect on pages. - Self-healing database setup: single canonical
instance/eduguard.db, automatic archive of a legacy root DB, and an automatic migration from a global unique student ID to the composite constraint.
flowchart LR
T([Teacher browser]) -->|login_required| F[Flask app<br/>routes.py]
F --> O{Ownership check<br/>resource.teacher_id == current_user.id}
O -- no --> D[403 / redirect with message]
O -- yes --> DB[(SQLite<br/>instance/eduguard.db)]
F -->|start_face_detection| P[[Separate process<br/>helpers.py: OpenCV + face_recognition]]
P -->|reads student encodings| DB
P -->|marked_students JSON in temp dir| R[/results file/]
F -->|polls check_face_detection_results| R
F -->|absent + not yet notified| M[SMTP email]
F --> X[PDF / Excel export]
Data model: Teacher 1-N Class 1-N Student; Attendance (one per class and date) 1-N AttendanceRecord (student, present flag, notification_sent).
| Decision | Why | Trade-off |
|---|---|---|
| Ownership check on every resource route (class, student, attendance, exports, notifications, detection) | A teacher must never read or change another teacher's data, even by guessing IDs | Repeated per route; a shared decorator or query scoping would reduce repetition |
| Camera loop runs in a separate process, results via a JSON file | Keeps the Flask request thread free and isolates the OpenCV window and heavy model work | Local-machine design (opens a desktop window), not a cloud-hosted camera pipeline |
hog detector, frame skipping, 0.25 downscale for detection (helpers.py defaults) |
Real-time performance on ordinary laptops, with a cnn option available |
Lower accuracy on small or angled faces than a GPU CNN |
| Distance threshold 0.6 for matching against registered encodings, best match wins | Standard face_recognition tolerance; picks the closest registered student |
Not a security control; the manual toggle exists for misses |
| Email only for absent and unsent records; failed sends stay pending | Prevents duplicate mails and allows safe retry | Requires SMTP credentials; skipped safely when unset |
Composite (student_id, class_id) uniqueness with startup migration |
Real schools reuse roll numbers across classes | SQLite table-rebuild migration is hand-written rather than using Alembic |
Tests stub out face_recognition (tests/conftest.py) |
The suite runs without dlib or a camera, so it works in CI | Face matching itself is not covered by tests |
| Layer | Technology |
|---|---|
| Backend | Flask, Flask-Login, Flask-SQLAlchemy |
| Database | SQLite |
| Vision | OpenCV, face_recognition (dlib), NumPy |
| Reports | ReportLab (PDF), openpyxl (Excel) |
| Frontend | Jinja templates, Bootstrap 5, vanilla JavaScript |
| Testing | pytest |
Prerequisite: face-recognition needs dlib, which needs CMake and a C++ toolchain to build on most machines.
git clone https://github.com/tchxm/EduGuard-AI.git
cd EduGuard-AI
python -m venv .venv && source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txt
cp .env.example .env
python run.pyApp URL: http://127.0.0.1:5000
Set in .env (see .env.example):
| Variable | Purpose |
|---|---|
SECRET_KEY |
Flask session signing key. Change it; a development default is used if unset. |
EMAIL_HOST / EMAIL_PORT |
SMTP server (defaults smtp.gmail.com / 587) |
EMAIL_USER / EMAIL_PASSWORD |
SMTP login (for Gmail, use an app password) |
EMAIL_FROM |
Sender address |
If email variables are missing, notification attempts are skipped safely.
- Single source of truth:
instance/eduguard.db. - A legacy root
eduGuard.dbis migrated or archived automatically on startup when present.
The suite in tests/ has 8 tests in 3 files:
| File | Covers |
|---|---|
test_auth.py |
Login redirect for anonymous users, invalid credentials, successful login |
test_permissions.py |
403 for a non-owner marking attendance, owner can update a record, cross-teacher blocks on class view, add / edit / delete student |
test_notifications.py |
Emails only for absent and unsent students; failed sends stay pending |
pytest -qAll 8 tests pass. face_recognition is stubbed in tests/conftest.py, so no camera or dlib install is needed to run them (face matching itself is not covered).
- TODO: Login / register page
- TODO: Teacher dashboard with classes
- TODO: Live camera window recognising students
- TODO: Attendance sheet with manual toggles and notification status
- TODO: Exported PDF / Excel sample (with dummy data)
app.py app factory, DB path + legacy migration, face-detection process launcher
routes.py all HTTP routes, ownership checks, email, PDF/Excel export
models.py Teacher, Class, Student, Attendance, AttendanceRecord
helpers.py OpenCV + face_recognition attendance process (run as a script)
run.py entry point (0.0.0.0:5000)
templates/ Jinja pages static/ CSS and (git-ignored) uploaded dataset
tests/ pytest suite
- Factor the repeated ownership checks into a decorator or scoped queries
- Added
scipytorequirements.txtand dropped the unusedtwiliopin - Test coverage for exports and the face-matching path
- SMS notifications
- Liveness / anti-spoofing (a
detect_livenessstub exists but is disabled by default) - Dockerfile and CI workflow running
pytest
Mohammed Afnan, AI/ML student at REVA University. GitHub · Portfolio · LinkedIn
Licensed under MIT.