Building a Live Proctoring System With React and Dyte
@ravindra-dyte
γ»
Jan 30,2024
γ»
15 min read
γ»
1k views
γ»
Originally posted on dyte.io
By the end of this tutorial, we will have built a "Live Proctoring System" using Dyte APIs that allows the admin to monitor whether multiple people are peeking into a candidate's screen. ππ§βπ»
Introduction
Proctoring is a method of monitoring students during an examination to prevent them from dishonest conduct.
In the case of online exams, it is not possible to have a proctor for each student. π΅οΈ
This is where live automatic proctoring comes into the picture. It is a method of monitoring students during an online exam using a webcam and a microphone.
With the help of computer vision and machine learning, one can detect if a student is trying to cheat during an online exam.
β¨ In this tutorial, we will build a live proctoring system using Dyte APIs that allow an admin to monitor if there are multiple people in the video frame of a candidate in real-time and send them a warning message.
High-Level Design of the application
We want to notify the proctor if anyone other than the candidate is seen on the webcam.
The proctor would get the candidate's details and a photograph from their webcam as proof in the meeting sidebar. πΈβ¨
π§βπ» Before we start building our live proctoring system, we will need to set up a Dyte account.
We can create a free account by clicking the "Start Building" button on Dyte.io and signing up using Google or GitHub π.
Once signed up, we can access our Dyte API keys from the "API Keys" tab in the left sidebar. We will keep these keys secure as we will use them later.ππ€«
For our live proctoring system, we will use React for the frontend and FastAPI for building the Backend and APIs.
We will begin by creating a new directory for our project, called dyte-proctoring and navigating into it using the following commands:
mkdir dyte-proctoring
cd dyte-proctoring
Please note:
We will also require accounts on the following platforms:
Imgur: Create an account on Imgur and create an API key. Here is a step-by-step guide
ElephantSQL: Here is a step-by-step guide to creating a db on ElephantSQL.
Now back to the tutorial.
Step 1: Setting up the frontend
Let's start setting up our frontend project using React and Dyte! β¨
We will create a boilerplate React app using create-react-app. We can do this with the following command:
yarn create react-app frontend
This will initialize a new React app in the frontend directory. π
Then, we will go ahead and install the dyte react-web-core, dyte react-ui-kit and react-router packages in this project using the following command π
Let's create a new file named imgur.py and add the following code. This will help us upload our screenshots to Imgur. π
Here we are connecting the Imgur API using the CLIENT_ID that we can get from our Imgur API Dashboard and using it to upload the suspicious candidate's image and get back the link to it. ππ
imgur.py
import base64
from fastapi import FastAPI, UploadFile, HTTPException
from httpx import AsyncClient
from dotenv import load_dotenv
async with AsyncClient() as client:
response = await client.post("https://api.imgur.com/3/image", headers=headers, data=data)
if response.status_code != 200:
raise HTTPException(status_code=500, detail="Could not upload image.")
print(response.json())
return response.json()["data"]["link"]
Step 4: Setting up our backend application
Now, we will create a new file named app.py and add our π ElephantSQL PostgreSQL database connection and code for our APIs, including face detection logic.
In this file, we would need to create the following routes:
GET / - Root route
POST /is_admin/ - Check if the user is an admin
POST /multiple_faces_list/ - This route retrieves a list of participants with multiple faces detected in their images.
POST /detect_faces/ - Detect multiple faces in participant images
POST /meetings - Create a new meeting
POST /meetings/{meetingId}/participants - This route is responsible for adding a participant to a specific meeting identified by meetingId.
So, let's get started. π
app.py
import base64
import io
import logging
import random
import uvicorn
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
from imgur import upload_image
import face_recognition
import psycopg2
import os
import base64
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from dotenv import load_dotenv
from httpx import AsyncClient
import uuid
if name == "main":
uvicorn.run("app:app", host="localhost", port=8000, log_level="debug", reload=True)
This code defines a β‘οΈ FastAPI application with an endpoint /detect_faces which takes in a base64 encoded image and returns a boolean value indicating if there is more than one face in the picture.
It uses the face_recognition library to detect faces in the image received.
The code also makes use of the Dyte API for meeting-related operations. πΉ
We can start the backend server simply by using the following command π§βπ»:
python app.py
This Python server helps us create and join meetings, detect multiple faces, and get the list of suspicious candidates. π΅οΈ
When we hit the /detect_faces endpoint with an image file encoded as a base64 string, the multiple_detected key of the response would be set to True if there is more than one face in the image, else it will be set to False.
We can call this from our frontend with the participant's webcam feed to detect if there is more than one face in the frame.
With this sorted, let's return to our React application and create our UI. β¨
Step 5: Setting up the meeting UI
First, we'll add our CSS file. Create a new file frontend/src/App.css and paste the following code.
@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
Next, we will add the initial Dyte Meeting component to our app. We can do this by replacing the contents of frontend/src/App.jsx with the following code:
import { useEffect, useState } from "react";
import Meet from "./Meet";
import Home from "./Home";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import "./App.css";
function App() {
const [meetingId, setMeetingId] = useState();
This component will create a Dyte Meeting link and an adminId for the admin. We will store the adminId secretly in localstorage. The adminId will be used later for accessing any sensitive data.
Home component
The home component renders the / route. Create a file as frontend/src/Home.jsx.
import { Link } from "react-router-dom";
function Home({ meetingId }) {
return (
Delving into the Meet component that renders on route /meeting/:meetingId.
When the admin clicks on the link provided on the / route, he gets redirected to the meeting page, where we add the user to the meeting as a participant with group_call_host preset. π€
Since this user created the meeting and was redirected to the meet page, we will assign him an admin role. The link from the address bar can be shared with candidates.
When a candidate opens the shared link, they become a regular user. And for every regular user, the component emits screenshots of the users' videos directed to our Python server. π
/ eslint-disable /
import { useState, useEffect, useRef } from "react";
import { DyteMeeting, provideDyteDesignSystem } from "@dytesdk/react-ui-kit";
import { useDyteClient } from "@dytesdk/react-web-core";
import Proctor from "./Proctor";
import Heading from "./Heading";
import { SendImageToBackendMiddleware, joinMeeting } from "./utils";
// Constants
const SERVER_URL = process.env.SERVER_URL || "http://localhost:8000";
let LAST_BACKEND_PING_TIME = 0;
const DETECT_FACES_ENDPOINT = ${SERVER_URL}/detect_faces;
const TIME_BETWEEN_BACKEND_PINGS = 5000;
isAdmin talks to the Python server to identify whether the current client is an admin.
joinMeeting adds the current client to the meeting.
SendImageToBackendMiddleware sends screenshots of candidates' videos to the Python server.
Proctor component
The proctor component gets activated only for admins and, with the help of adminId, fetches the suspicious candidates' list and renders it in a chat-like format. Create a file frontend/src/Proctor.jsx
import { useEffect, useState } from "react";
import { getCandidateStatus } from "./utils";
To start the React app on the local server, we can run the following command:
yarn start
If we visit http://localhost:3000/, we should see the Dyte meeting in our browser.
Step 6: Adding the face detection logic to the frontend
Since now we have a nice backend server to detect faces and a great UI π, we can add the face detection logic to our frontend. For this, we will first add some constants to our previously edited frontend/src/App.jsx file:
We will be using the above constants in the SendImageToBackendMiddleware function, which we will add to our App component, just after the useDyteClient hook. πͺ
The SendImageToBackendMiddleware is a Dyte VideoMiddleware. Middlewares are add-ons that you can use to easily add effects and filters to your audio and video streams.
Here, we use the middleware functionality to get the canvas object of the participant's webcam feed, convert it to a base64 encoded image, and send it to our backend server. We also ensure that the backend is pinged only once every 30 seconds to avoid unnecessary load on the server.
We then use the sendNotification function to send a notification to the participant if the backend returns True for the multiple_detected key of the response.
That was all the code we needed to add basic live proctoring functionality to our Dyte meeting. π
The app sends a screenshot of the participant's webcam feed to the backend server every 30 seconds, and if the backend detects more than one face in the image, it sends a warning notification to the projector. β οΈ
The backend also logs the participant's ID and the time of the detection in the terminal. This can be used to keep track of the participants who may have cheated during the meeting for later review.
First, let us look at the candidate's view; the candidate can see that the proctor is in the meeting but cannot see the Proctoring Panel. π§βπ»
In the proctor's view, we can see the details (live proctoring information) with proof when two people are in the candidate's webcam view. π
π§βπ» You can try out the live proctoring system here. And here's the link to the repository for you to take a look at the whole codebase.
Conclusion
Celebrate! πβ¨ We've built a powerful live proctoring system with Dyte, ensuring integrity and fairness in online exams and interviews. But that's not all! We can now create our own customized online classroom or meeting platform.
We can now use this system to proctor our online exams and interviews. βοΈ
The possibilities are endless with Dyte, go ahead and try bringing your own ideas to life by visiting dyte.io! π
Only registered users can post comments. Please,
login or signup.
Start blogging about your favorite technologies, reach more readers and earn rewards!
Join other developers and claim your FAUN account now!
Only registered users can post comments. Please, login or signup.