AI-powered exam proctoring with webcam monitoring, anti-cheat enforcement, and a full admin panel. Built with Flask + SQLite.
# 1. Install dependencies
pip install -r requirements.txt
# 2. Run the app (DB + demo data created automatically on first run)
python app.py
# 3. Open in browser
# Student portal : http://localhost:5000
# Admin panel : http://localhost:5000/admin/login| Role | Username / ID | Password |
|---|---|---|
| Admin | admin | admin123 |
| Student | STU001 | alice123 |
| Student | STU002 | bob123 |
| Student | STU003 | carol123 |
proctoring/
├── app.py # App factory + seeding
├── extensions.py # db, login_manager
├── models.py # ORM: Student, Admin, Question, ExamResult, ViolationLog
├── requirements.txt
├── routes/
│ ├── auth.py # /login /admin/login /logout
│ ├── exam.py # /exam/ /exam/start /exam/submit
│ ├── proctor.py # /proctor/violation /proctor/status
│ └── admin.py # /admin/ /admin/violations /admin/student/<id>
├── static/
│ ├── css/main.css # All styles (dark industrial theme)
│ └── js/exam.js # Timer, anti-cheat, camera, submit
└── templates/
├── base.html
├── login.html # Student login
├── dashboard.html # Pre-exam / result view
├── exam.html # Exam interface
├── admin_login.html
├── admin_dashboard.html # Results table
├── admin_violations.html
└── admin_student.html # Per-student detail
- Student login with ID + password (Werkzeug password hashing)
- Admin login with separate credentials
- Session-based auth via Flask-Login
- Duplicate login prevention —
is_onlineflag blocks a second login
- 15 Python MCQ questions (seeded automatically)
- Configurable duration + violation limit (
ExamConfigtable) - Countdown timer with SVG ring animation (urgent/warn colour states)
- Question navigator sidebar with answered/current indicators
- One-attempt restriction — re-submission redirected
| Mechanism | Implementation |
|---|---|
| Tab switch | Page Visibility API — visibilitychange event |
| Back/fwd nav | history.pushState + popstate trap |
| Copy / paste / cut | Event listeners + preventDefault() |
| Keyboard shortcuts | Ctrl+C/V/X/A/U/S/P, F12, PrintScreen blocked |
| Right-click | contextmenu disabled |
| Auto-submit on N violations | Server returns auto_submit: true |
| Auto-submit on timer | timeLeft <= 0 → submitExam(true) |
- WebRTC
getUserMedia— no plugin needed - Every 3 seconds, a 80×60 frame is sampled from the video feed
- Skin-tone pixel heuristic (Kovac et al.) estimates face presence:
skinRatio < 4%→ "No face" violation loggedskinRatio > 55%→ "Multiple faces" violation loggeddarkRatio > 85%→ "Too dark / camera covered" violation logged
- For production, swap
analyseFacePresence()with face-api.js TinyFaceDetector or a server-side OpenCV endpoint receiving periodic JPEG snapshots
Every suspicious event → ViolationLog row:
tab_switch, no_face, multiple_faces, copy_attempt, back_nav,
camera_denied
- Results table with pass/fail badges, percentage bar, auto-submit flag
- Full violation log with timestamps
- Per-student detail: score breakdown + complete answer sheet
- Replace SQLite with PostgreSQL (
SQLALCHEMY_DATABASE_URI = 'postgresql://...') - Use a real ML face detector — face-api.js or MediaPipe in the browser
- Set a real
SECRET_KEYfrom an environment variable - Add HTTPS — session security requires TLS in production
- Run with Gunicorn:
gunicorn -w 4 "app:create_app()"