From Django to Deep Learning: Building an AI-Proctored Exam System That Actually Works + Video

Listen to this Post

Featured Image

Introduction:

The shift to online education has exposed a critical vulnerability—academic integrity in remote assessments. Traditional proctoring solutions are either prohibitively expensive, invasive, or easily circumvented. Vision Exam System addresses this by combining computer vision with generative AI, creating a real-time proctoring platform that detects cheating behaviors while maintaining a frictionless user experience. This article breaks down the technical architecture, implementation strategies, and security hardening techniques behind building a production-grade AI-proctored examination system using Django, OpenCV, and Google’s Gemini API.

Learning Objectives:

  • Understand how to integrate OpenCV-based computer vision pipelines with Django for real-time video frame processing
  • Implement client-side hardening techniques including copy-paste prevention, DevTools blocking, and tab-switching detection
  • Master prompt engineering for Google Gemini API to generate structured quiz content dynamically
  • Configure SQLite for production-grade concurrent access in Django applications
  • Build a real-time teacher dashboard using WebSocket-based monitoring
  1. Real-Time Video Processing Pipeline with OpenCV and Django

The core of any AI-proctoring system is the ability to capture, process, and analyze video frames in real-time. Vision Exam System captures webcam snapshots every three seconds, processing each frame through an OpenCV pipeline that performs face detection, eye tracking, and object recognition.

How It Works:

The system uses Django’s request-response cycle to receive base64-encoded image frames from the client-side JavaScript. Each frame is decoded and passed through OpenCV’s `cv2.CascadeClassifier` for face detection using pre-trained Haar cascades. Eye tracking is implemented using facial landmark detection to determine gaze direction and eye visibility.

Implementation Steps:

1. Set up OpenCV in your Django environment:

pip install opencv-python opencv-contrib-python numpy

2. Create a frame processing utility:

import cv2
import numpy as np
import base64

def process_frame(base64_image):
 Decode base64 to image
img_data = base64.b64decode(base64_image.split(',')[bash])
nparr = np.frombuffer(img_data, np.uint8)
frame = cv2.imdecode(nparr, cv2.IMREAD_COLOR)

Face detection
face_cascade = cv2.CascadeClassifier(
cv2.data.haarcascades + 'haarcascade_frontalface_default.xml'
)
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
faces = face_cascade.detectMultiScale(gray, 1.1, 4)

Eye detection
eye_cascade = cv2.CascadeClassifier(
cv2.data.haarcascades + 'haarcascade_eye.xml'
)
eyes = eye_cascade.detectMultiScale(gray, 1.1, 4)

return {
'face_detected': len(faces) > 0,
'face_count': len(faces),
'eye_count': len(eyes),
'multiple_faces': len(faces) > 1
}

3. Integrate with Django views:

from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt

@csrf_exempt
def proctor_frame(request):
if request.method == 'POST':
image_data = request.POST.get('image')
result = process_frame(image_data)
 Store alert in database if violation detected
if result['multiple_faces']:
Alert.objects.create(
student=request.user,
type='multiple_faces',
timestamp=timezone.now()
)
return JsonResponse(result)

Linux Command for OpenCV Installation:

sudo apt-get update
sudo apt-get install python3-opencv
sudo apt-get install libopencv-dev

2. Mobile Phone Detection Using HSV Color Segmentation

Detecting electronic devices during exams is one of the most challenging aspects of remote proctoring. Vision Exam System implements a custom computer vision heuristic using HSV (Hue, Saturation, Value) color space thresholding combined with contour shape approximation.

How It Works:

The system converts each frame from BGR to HSV color space, applies color range masks for typical device colors (black, silver, metallic), and uses contour detection to identify shapes consistent with mobile phones. Contour approximation helps distinguish phones from other objects based on rectangular shape characteristics.

Implementation Steps:

1. Define HSV color ranges for device detection:

 Typical color ranges for mobile devices
COLOR_RANGES = {
'black': ([0, 0, 0], [180, 255, 50]),
'silver': ([0, 0, 100], [180, 50, 200]),
'metallic': ([0, 0, 50], [180, 30, 150])
}

2. Create the phone detection function:

def detect_phone(frame):
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
detected_phones = []

for color_name, (lower, upper) in COLOR_RANGES.items():
lower = np.array(lower, dtype=np.uint8)
upper = np.array(upper, dtype=np.uint8)
mask = cv2.inRange(hsv, lower, upper)

Find contours
contours, _ = cv2.findContours(
mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE
)

for contour in contours:
area = cv2.contourArea(contour)
if area < 1000:  Filter out small noise
continue

Approximate contour shape
epsilon = 0.02  cv2.arcLength(contour, True)
approx = cv2.approxPolyDP(contour, epsilon, True)

Phones typically have rectangular shapes (4 vertices)
if len(approx) == 4:
x, y, w, h = cv2.boundingRect(contour)
aspect_ratio = w / h
 Phone aspect ratio typically between 1.5 and 2.5
if 1.5 < aspect_ratio < 2.5:
detected_phones.append((x, y, w, h))

return detected_phones

3. Fine-tune HSV values for your environment:

 Interactive HSV calibration tool
def calibrate_hsv():
cap = cv2.VideoCapture(0)
cv2.namedWindow('Trackbars')

def nothing(x):
pass

cv2.createTrackbar('LH', 'Trackbars', 0, 179, nothing)
cv2.createTrackbar('UH', 'Trackbars', 179, 179, nothing)
cv2.createTrackbar('LS', 'Trackbars', 0, 255, nothing)
cv2.createTrackbar('US', 'Trackbars', 255, 255, nothing)
cv2.createTrackbar('LV', 'Trackbars', 0, 255, nothing)
cv2.createTrackbar('UV', 'Trackbars', 255, 255, nothing)

while True:
_, frame = cap.read()
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)

lh = cv2.getTrackbarPos('LH', 'Trackbars')
uh = cv2.getTrackbarPos('UH', 'Trackbars')
ls = cv2.getTrackbarPos('LS', 'Trackbars')
us = cv2.getTrackbarPos('US', 'Trackbars')
lv = cv2.getTrackbarPos('LV', 'Trackbars')
uv = cv2.getTrackbarPos('UV', 'Trackbars')

lower = np.array([lh, ls, lv])
upper = np.array([uh, us, uv])
mask = cv2.inRange(hsv, lower, upper)

cv2.imshow('Mask', mask)
if cv2.waitKey(1) & 0xFF == ord('q'):
break

cap.release()
cv2.destroyAllWindows()

3. Client-Side Security Hardening

Client-side security is paramount in exam proctoring systems. Vision Exam System implements multiple layers of protection including copy-paste prevention, Developer Tools blocking, right-click restrictions, and tab-switching detection using the Page Visibility API.

How It Works:

The system uses JavaScript event listeners to intercept and prevent unauthorized actions. The Page Visibility API detects when a student switches tabs or minimizes the browser window. Combined with keyboard event blocking and context menu prevention, this creates a secure examination environment.

Implementation Steps:

1. Prevent copy-paste and right-click:

// Disable right-click
document.addEventListener('contextmenu', function(e) {
e.preventDefault();
return false;
});

// Disable copy-paste
document.addEventListener('copy', function(e) {
e.preventDefault();
return false;
});

document.addEventListener('paste', function(e) {
e.preventDefault();
return false;
});

// Disable keyboard shortcuts
document.addEventListener('keydown', function(e) {
// Ctrl+C, Ctrl+V, Ctrl+U, Ctrl+S, Ctrl+1
if (e.ctrlKey && ['c', 'v', 'u', 's', 'p'].includes(e.key.toLowerCase())) {
e.preventDefault();
return false;
}
// F12
if (e.key === 'F12') {
e.preventDefault();
return false;
}
});

2. Tab-switching detection with Page Visibility API:

document.addEventListener('visibilitychange', function() {
if (document.hidden) {
// Student switched tabs or minimized browser
fetch('/api/tab-switch/', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
event: 'tab_switch',
timestamp: Date.now()
})
});
}
});

3. Disable Developer Tools:

// Block common DevTools opening methods
setInterval(function() {
// Detect DevTools by checking element size
const devtools = /./;
devtools.toString = function() {
if (this.console && this.console.log) {
// DevTools is open
fetch('/api/devtools-detected/', {method: 'POST'});
}
};
}, 1000);

// Prevent inspect element
document.addEventListener('keydown', function(e) {
if (e.ctrlKey && e.shiftKey && ['I', 'J', 'C'].includes(e.key.toUpperCase())) {
e.preventDefault();
return false;
}
});

Windows Command for Testing:

 Test client-side security features in different browsers
start chrome --disable-web-security --user-data-dir="C:\temp\chrome_dev"
start firefox -private -url http://localhost:8000

4. AI Quiz Generation with Google Gemini API

Vision Exam System integrates Google’s Gemini API to allow teachers to auto-generate quizzes on any topic instantly. This feature leverages prompt engineering to produce structured, JSON-formatted quiz content that can be directly stored in the database.

How It Works:

Teachers submit a topic and difficulty level. The system constructs a carefully engineered prompt that instructs Gemini to generate a quiz with specific parameters—number of questions, question types, and output format. Gemini’s structured output capability ensures consistent JSON responses that the Django backend can parse and store.

Implementation Steps:

1. Install Google Generative AI SDK:

pip install google-generativeai

2. Configure Gemini API:

import google.generativeai as genai
from django.conf import settings

genai.configure(api_key=settings.GEMINI_API_KEY)
model = genai.GenerativeModel('gemini-pro')

3. Create prompt engineering function:

def generate_quiz(topic, num_questions=5, difficulty='medium'):
prompt = f"""
Generate a quiz on the topic: {topic}
Difficulty level: {difficulty}
Number of questions: {num_questions}

Return the quiz in the following JSON format:
{{
"title": "Quiz title",
"questions": [
{{
"id": 1,
"type": "multiple_choice",
"question": "Question text",
"options": ["A", "B", "C", "D"],
"correct_answer": 0,
"explanation": "Brief explanation"
}}
]
}}

Ensure the JSON is valid and properly formatted.
"""

response = model.generate_content(prompt)
return parse_quiz_response(response.text)

4. Parse and store quiz content:

import json
import re

def parse_quiz_response(response_text):
 Extract JSON from response (handle markdown code blocks)
json_match = re.search(r'<code>bash\n?(.?)\n?</code>', response_text, re.DOTALL)
if json_match:
response_text = json_match.group(1)

try:
quiz_data = json.loads(response_text)
return quiz_data
except json.JSONDecodeError:
 Fallback: try to find JSON-like structure
start = response_text.find('{')
end = response_text.rfind('}') + 1
if start != -1 and end != -1:
return json.loads(response_text[start:end])
return None

5. Django view for quiz generation:

from django.views.decorators.csrf import csrf_exempt
from django.http import JsonResponse

@csrf_exempt
def generate_quiz_api(request):
if request.method == 'POST':
topic = request.POST.get('topic')
num_questions = int(request.POST.get('num_questions', 5))
difficulty = request.POST.get('difficulty', 'medium')

quiz_data = generate_quiz(topic, num_questions, difficulty)

if quiz_data:
 Save to database
quiz = Quiz.objects.create(
title=quiz_data['title'],
teacher=request.user,
topic=topic,
difficulty=difficulty
)
for q_data in quiz_data['questions']:
Question.objects.create(
quiz=quiz,
question_text=q_data['question'],
options=q_data['options'],
correct_answer=q_data['correct_answer'],
explanation=q_data.get('explanation', '')
)
return JsonResponse({'status': 'success', 'quiz_id': quiz.id})
return JsonResponse({'status': 'error', 'message': 'Failed to generate quiz'})

Prompt Engineering Best Practices:

  • Be explicit about output format—use structured output features when available
  • Provide examples of desired output to guide the model
  • Set constraints on question types and difficulty levels
  • Validate and sanitize all generated content before storage

5. Production-Grade SQLite Configuration for Django

SQLite is often dismissed for production use due to concurrency concerns, but with proper configuration, it can handle moderate workloads effectively. Vision Exam System uses SQLite with optimized settings for concurrent access.

How It Works:

The default SQLite settings in Django are suitable for development but will encounter “database is locked” errors under concurrent access. By enabling WAL (Write-Ahead Logging) mode and adjusting transaction settings, the system can handle multiple simultaneous connections.

Implementation Steps:

1. Configure Django settings for production SQLite:

 settings.py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
'OPTIONS': {
'timeout': 20,  Increased timeout for concurrent access
'transaction_mode': 'IMMEDIATE',  Django 5.1+ feature
'init_command': '''
PRAGMA journal_mode=WAL;
PRAGMA synchronous=NORMAL;
PRAGMA cache_size=-2000000;
PRAGMA temp_store=MEMORY;
'''
}
}
}

2. Apply migrations with WAL mode enabled:

python manage.py migrate

3. Monitor database performance:

 Utility to check WAL status
from django.db import connection

def check_wal_status():
with connection.cursor() as cursor:
cursor.execute("PRAGMA journal_mode;")
result = cursor.fetchone()
print(f"Journal mode: {result[bash]}")

cursor.execute("PRAGMA synchronous;")
result = cursor.fetchone()
print(f"Synchronous mode: {result[bash]}")

SQLite Production Checklist:

  • Enable WAL mode for better concurrent read/write performance
  • Set `synchronous=NORMAL` for balance between safety and performance
  • Increase timeout to prevent lock errors
  • Use `transaction_mode=’IMMEDIATE’` in Django 5.1+
  • Consider `dj-lite` package for automated production configuration

6. Real-Time Teacher Dashboard with Django Channels

Vision Exam System provides a live dashboard for teachers to monitor student snapshots, alert counts, and status in real-time. This is implemented using Django Channels for WebSocket-based communication.

How It Works:

Django Channels extends Django to handle WebSocket connections, allowing real-time bidirectional communication between the server and connected clients. Each student’s proctoring status is broadcast to all connected teacher clients.

Implementation Steps:

1. Install Django Channels:

pip install channels channels-redis

2. Configure ASGI application:

 asgi.py
import os
from django.core.asgi import get_asgi_application
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.auth import AuthMiddlewareStack
import proctor.routing

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'exam_system.settings')

application = ProtocolTypeRouter({
'http': get_asgi_application(),
'websocket': AuthMiddlewareStack(
URLRouter(proctor.routing.websocket_urlpatterns)
),
})

3. Create WebSocket consumer:

 consumers.py
import json
from channels.generic.websocket import AsyncWebsocketConsumer
from channels.db import database_sync_to_async
from .models import Student, Alert

class ProctorConsumer(AsyncWebsocketConsumer):
async def connect(self):
self.room_group_name = 'proctor_dashboard'
await self.channel_layer.group_add(
self.room_group_name,
self.channel_name
)
await self.accept()

async def disconnect(self, close_code):
await self.channel_layer.group_discard(
self.room_group_name,
self.channel_name
)

async def receive(self, text_data):
data = json.loads(text_data)
 Handle incoming messages (e.g., update status)
if data['type'] == 'student_update':
await self.update_student_status(data)

async def update_student_status(self, data):
 Broadcast to all connected teachers
await self.channel_layer.group_send(
self.room_group_name,
{
'type': 'proctor_update',
'student_id': data['student_id'],
'status': data['status'],
'alert_count': data['alert_count']
}
)

async def proctor_update(self, event):
 Send to WebSocket
await self.send(text_data=json.dumps({
'type': 'proctor_update',
'student_id': event['student_id'],
'status': event['status'],
'alert_count': event['alert_count']
}))

4. Client-side WebSocket connection:

const socket = new WebSocket('ws://localhost:8000/ws/proctor/');

socket.onmessage = function(e) {
const data = JSON.parse(e.data);
if (data.type === 'proctor_update') {
updateDashboard(data.student_id, data.status, data.alert_count);
}
};

function updateDashboard(studentId, status, alertCount) {
const row = document.querySelector(<code>[data-student="${studentId}"]</code>);
if (row) {
row.querySelector('.status').textContent = status;
row.querySelector('.alerts').textContent = alertCount;
}
}

What Undercode Say:

  • AI proctoring is not a silver bullet—while computer vision and generative AI significantly enhance exam integrity, they must be complemented with robust client-side security and server-side monitoring to be effective. The combination of OpenCV’s real-time processing and Gemini’s content generation creates a comprehensive solution.

  • Security is multi-layered—client-side hardening (copy-paste prevention, DevTools blocking, tab-switching detection) is just as important as server-side AI processing. Attackers will target the weakest link, which is often the browser environment.

  • SQLite can be production-ready—with proper configuration (WAL mode, increased timeout, IMMEDIATE transactions), SQLite can handle moderate concurrent workloads. This reduces infrastructure complexity and costs for educational platforms.

  • Prompt engineering is critical—generating structured output from LLMs requires careful prompt design and robust parsing logic. Gemini’s structured output feature should be leveraged for production-grade applications.

  • Real-time monitoring creates trust—WebSocket-based dashboards provide transparency for educators, allowing them to intervene when necessary while building confidence in the proctoring system.

Prediction:

  • +1 The integration of generative AI with computer vision will create more adaptive proctoring systems that can detect emerging cheating methods in real-time, significantly reducing the need for human invigilators.

  • +1 Edge computing will reduce latency in AI proctoring, enabling real-time analysis on student devices rather than relying on cloud processing, improving privacy and responsiveness.

  • -1 Privacy concerns will escalate as AI proctoring becomes more sophisticated, potentially leading to regulatory pushback and requiring transparent data handling policies.

  • -1 The cat-and-mouse game between proctoring systems and cheating methods will intensify, with students developing increasingly sophisticated evasion techniques that challenge current detection capabilities.

  • +1 Open-source proctoring solutions like Vision Exam System will democratize access to secure online assessments, making quality education more accessible to institutions with limited budgets.

  • -1 Over-reliance on automated proctoring may lead to false positives, unfairly penalizing students with nervous tics or technical issues, necessitating human oversight and appeals processes.

  • +1 The combination of LLM-generated quizzes and AI proctoring will enable fully automated, end-to-end assessment workflows, reducing administrative overhead for educational institutions.

▶️ Related Video (82% Match):

https://www.youtube.com/watch?v=0SDK09c9o3Q

🎯Let’s Practice For Free:

🎓 Live Courses & Certifications:

Join Undercode Academy for Verified Certifications

🚀 Request a Custom Project:

Secure, high-velocity infrastructure and disruptive technological engineering. Contact our engineering team for high-tier development and proprietary systems:
[email protected]
💎 Smart Architecture | 🛡️ Secure by Design | ⭐ Trusted by Thousands

IT/Security Reporter URL:

Reported By: Muhammad Ateeq – Hackers Feeds
Extra Hub: Undercode MoN
Basic Verification: Pass ✅

🔐JOIN OUR CYBER WORLD [ CVE News • HackMonitor • UndercodeNews ]

💬 Whatsapp | 💬 Telegram

📢 Follow UndercodeTesting & Stay Tuned:

𝕏 formerly Twitter 🐦 | @ Threads | 🔗 Linkedin | 🦋BlueSky