consumer.py
‘’'import json
import asyncio
from datetime import datetime, timedelta
from channels.generic.websocket import AsyncWebsocketConsumer
from django.db.models import ObjectDoesNotExist
class CountdownConsumer(AsyncWebsocketConsumer):
async def connect(self):
self.id = self.scope[‘url_route’][‘kwargs’][‘id’]
self.running = True # Flag to control the countdown loop
await self.channel_layer.group_add(self.id, self.channel_name)
await self.accept()
# Send initial countdown data
asyncio.create_task(self.send_countdown())
async def disconnect(self, close_code):
self.running = False
await self.channel_layer.group_discard(self.id, self.channel_name)
async def send_countdown(self):
from .models import Course # Import your model here
try:
# Fetch the timer's duration
timer = await asyncio.to_thread(Course.objects.get, id=self.id)
if not isinstance(timer.time_duration, timedelta):
raise ValueError("time_duration must be a timedelta object")
# Calculate end time
end_time = datetime.now() + timer.time_duration
# Loop to send countdown updates
while self.running:
remaining = (end_time - datetime.now()).total_seconds()
if remaining <= 0:
await self.send(json.dumps({'remaining': 0}))
break
await self.send(json.dumps({'remaining': int(remaining)}))
await asyncio.sleep(1) # Update every second
except ObjectDoesNotExist:
await self.send(json.dumps({'error': 'Timer not found'}))
except Exception as e:
await self.send(json.dumps({'error': str(e)}))
‘’’
start-exam.html
‘’’ {% extends ‘student/studentbase.html’ %} {% block content %} {%load static%}
Course: {{course.course_name}}
{% for q in questions%}{{ forloop.counter }}. {{q.question}}
[{{q.marks}} Marks]
<div class="form-check mx-4 d-flex align-items-center">
<input class="form-check-input" type="radio" name="{{ forloop.counter }}" id="{{q.option1}}" value="Option1">
<label class="form-check-label ml-3" for="option1">
{{q.option1}}
</label>
</div>
<div class="form-check mx-4 d-flex align-items-center">
<input class="form-check-input" type="radio" name="{{ forloop.counter }}" id="{{q.option2}}" value="Option2">
<label class="form-check-label ml-3" for="option2">
{{q.option2}}
</label>
</div>
<div class="form-check mx-4 d-flex align-items-center">
<input class="form-check-input" type="radio" name="{{ forloop.counter }}" id="{{q.option3}}" value="Option3">
<label class="form-check-label ml-3" for="option3">
{{q.option3}}
</label>
</div>
<div class="form-check mx-4 d-flex align-items-center">
<input class="form-check-input" type="radio" name="{{ forloop.counter }}" id="{{q.option4}}" value="Option4">
<label class="form-check-label ml-3" for="option4">
{{q.option4}}
</label>
</div>
{% endfor %}
<input class="btn btn-primary btn-lg" style="border-radius: 0%;" type="submit" value="Submit Answers"><br>
<button class="btn btn-danger">go back</button>
</form>
{% endblock content %}‘’’
asgi.py
‘’'“”"
ASGI config for ExamManagementSystem project.
It exposes the ASGI callable as a module-level variable named application.
For more information on this file, see
“”"
import os
from django.core.asgi import get_asgi_application
from channels.routing import ProtocolTypeRouter,URLRouter
from channels.auth import AuthMiddlewareStack
import exam.routing
os.environ.setdefault(‘DJANGO_SETTINGS_MODULE’, ‘ExamManagementSystem.settings’)
application = ProtocolTypeRouter({
“http”: get_asgi_application(),
“websocket”: AuthMiddlewareStack(
URLRouter(
exam.routing.websocket_urlpatterns
)
),
})
‘’’
setting.py
‘’'“”"
Django settings for ExamManagementSystem project.
Generated by ‘django-admin startproject’ using Django 5.1.3.
For more information on this file, see
https://docs.djangoproject.com/en/5.1/topics/settings/
For the full list of settings and their values, see
“”"
import os
from pathlib import Path
Build paths inside the project like this: BASE_DIR / ‘subdir’.
BASE_DIR = Path(file).resolve().parent.parent
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(file)))
TEMPLATE_DIR = os.path.join(BASE_DIR,‘templates’)
STATIC_DIR=os.path.join(BASE_DIR,‘static’)
MEDIA_ROOT=os.path.join(BASE_DIR,‘static’)
Quick-start development settings - unsuitable for production
See Deployment checklist | Django documentation | Django
SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = ‘django-insecure-50#egag5^2nj4s#hlx15(m+!xi6+cb05lr%l6ata46n(8w$u’
SECURITY WARNING: don’t run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS =
Application definition
INSTALLED_APPS = [
‘channels’,
‘daphne’,
‘jazzmin’,
‘django.contrib.admin’,
‘django.contrib.auth’,
‘django.contrib.contenttypes’,
‘django.contrib.sessions’,
‘django.contrib.messages’,
‘django.contrib.staticfiles’,
‘teacher’,
‘student’,
‘exam’,
‘widget_tweaks’,
‘crispy_forms’,
‘crispy_bootstrap4’,
]
ASGI_APPLICATION = ‘ExamManagementSystem.asgi.application’
CRISPY_TEMPLATE_PACK = ‘bootstrap4’
CHANNEL_LAYERS = {
“default”:{
“BACKEND”:“channels.layers.InMemoryChannelLayer”,
},
}
MIDDLEWARE = [
‘django.middleware.security.SecurityMiddleware’,
‘django.contrib.sessions.middleware.SessionMiddleware’,
‘django.middleware.common.CommonMiddleware’,
‘django.middleware.csrf.CsrfViewMiddleware’,
‘django.contrib.auth.middleware.AuthenticationMiddleware’,
‘django.contrib.messages.middleware.MessageMiddleware’,
‘django.middleware.clickjacking.XFrameOptionsMiddleware’,
]
JAZZMIN_SETTINGS = {
# title of the window (Will default to current_admin_site.site_title if absent or None)
“site_title”: “Admin Portal”,
# Title on the login screen (19 chars max) (defaults to current_admin_site.site_header if absent or None)
"site_header": "Admin Portal",
# Title on the brand (19 chars max) (defaults to current_admin_site.site_header if absent or None)
"site_brand": "Admin Dashboard",
# Logo to use for your site, must be present in static files, used for brand on top left
# Logo to use for your site, must be present in static files, used for login form logo (defaults to site_logo)
"login_logo": None,
# Logo to use for login form in dark themes (defaults to login_logo)
"login_logo_dark": None,
# CSS classes that are applied to the logo above
"site_logo_classes": "img-circle",
# Relative path to a favicon for your site, will default to site_logo if absent (ideally 32x32 px)
"site_icon": None,
# Welcome text on the login screen
"welcome_sign": "Welcome to the Exam management",
# Copyright on the footer
# List of model admins to search from the search bar, search bar omitted if excluded
# If you want to use a single search field you dont need to use a list, you can use a simple string
"search_model": ["auth.User", "auth.Group"],
# Field name on user model that contains avatar ImageField/URLField/Charfield or a callable that receives the user
"user_avatar": None,
############
# Top Menu #
############
# Links to put along the top menu
"topmenu_links": [
# Url that gets reversed (Permissions can be added)
{"name": "Home", "url": "admin:index", "permissions": ["auth.view_user"]},
# external url that opens in a new window (Permissions can be added)
{"name": "Support", "url": "https://github.com/farridav/django-jazzmin/issues", "new_window": True},
# model admin to link to (Permissions checked against model)
{"model": "auth.User"},
# App with dropdown menu to all its models pages (Permissions checked against models)
{"app": "student"},
],
#############
# User Menu #
#############
# Additional links to include in the user menu on the top right ("app" url type is not allowed)
“”" “usermenu_links”: [
{“name”: “Support”, “url”: “GitHub · Where software is built”, “new_window”: True},
{“model”: “auth.user”}
],
“”"
#############
# Side Menu #
#############
# Whether to display the side menu
"show_sidebar": True,
# Whether to aut expand the menu
"navigation_expanded": True,
# Hide these apps when generating side menu e.g (auth)
"hide_apps": [],
# Hide these models when generating side menu (e.g auth.user)
"hide_models": [],
# List of apps (and/or models) to base side menu ordering off of (does not need to contain all apps/models)
"order_with_respect_to": ["auth", "books", "books.author", "books.book"],
# Custom links to append to app groups, keyed on app name
"custom_links": {
"books": [{
"name": "Make Messages",
"url": "make_messages",
"icon": "fas fa-comments",
"permissions": ["books.view_book"]
}]
},
# Custom icons for side menu apps/models See https://fontawesome.com/icons?d=gallery&m=free&v=5.0.0,5.0.1,5.0.10,5.0.11,5.0.12,5.0.13,5.0.2,5.0.3,5.0.4,5.0.5,5.0.6,5.0.7,5.0.8,5.0.9,5.1.0,5.1.1,5.2.0,5.3.0,5.3.1,5.4.0,5.4.1,5.4.2,5.13.0,5.12.0,5.11.2,5.11.1,5.10.0,5.9.0,5.8.2,5.8.1,5.7.2,5.7.1,5.7.0,5.6.3,5.5.0,5.4.2
# for the full list of 5.13.0 free icon classes
"icons": {
"auth": "fas fa-users-cog",
"auth.user": "fas fa-user",
"auth.Group": "fas fa-users",
},
# Icons that are used when one is not manually specified
"default_icon_parents": "fas fa-chevron-circle-right",
"default_icon_children": "fas fa-circle",
#################
# Related Modal #
#################
# Use modals instead of popups
"related_modal_active": False,
#############
# UI Tweaks #
#############
# Relative paths to custom CSS/JS scripts (must be present in static files)
"custom_css": None,
"custom_js": None,
# Whether to link font from fonts.googleapis.com (use custom_css to supply font otherwise)
"use_google_fonts_cdn": True,
# Whether to show the UI customizer on the sidebar
"show_ui_builder": False,
###############
# Change view #
###############
# Render out the change view as a single form, or in tabs, current options are
# - single
# - horizontal_tabs (default)
# - vertical_tabs
# - collapsible
# - carousel
"changeform_format": "horizontal_tabs",
# override change forms on a per modeladmin basis
"changeform_format_overrides": {"auth.user": "collapsible", "auth.group": "vertical_tabs"},
# Add a language dropdown into the admin
# "language_chooser": True,
}
ROOT_URLCONF = ‘ExamManagementSystem.urls’
AUTH_USER_MODEL = ‘student.CustomUser’
TEMPLATES = [
{
‘BACKEND’: ‘django.template.backends.django.DjangoTemplates’,
‘DIRS’: [TEMPLATE_DIR],
‘APP_DIRS’: True,
‘OPTIONS’: {
‘context_processors’: [
‘django.template.context_processors.debug’,
‘django.template.context_processors.request’,
‘django.contrib.auth.context_processors.auth’,
‘django.contrib.messages.context_processors.messages’,
],
},
},
]
WSGI_APPLICATION = ‘ExamManagementSystem.wsgi.application’
Database
Settings | Django documentation | Django
DATABASES = {
‘default’: {
‘ENGINE’: ‘django.db.backends.sqlite3’,
‘NAME’: os.path.join(BASE_DIR, ‘db.sqlite3’),
}
}
Password validation
Settings | Django documentation | Django
AUTH_PASSWORD_VALIDATORS = [
{
‘NAME’: ‘django.contrib.auth.password_validation.UserAttributeSimilarityValidator’,
},
{
‘NAME’: ‘django.contrib.auth.password_validation.MinimumLengthValidator’,
‘OPTIONS’: {
‘min_length’: 5,
}
},
{
‘NAME’: ‘django.contrib.auth.password_validation.CommonPasswordValidator’,
},
{
‘NAME’: ‘django.contrib.auth.password_validation.NumericPasswordValidator’,
},
]
Internationalization
Internationalization and localization | Django documentation | Django
LANGUAGE_CODE = ‘en-us’
TIME_ZONE = ‘UTC’
USE_I18N = True
USE_TZ = True
Static files (CSS, JavaScript, Images)
How to manage static files (e.g. images, JavaScript, CSS) | Django documentation | Django
STATIC_URL = ‘/static/’
STATICFILES_DIRS=[
STATIC_DIR,
]
LOGIN_REDIRECT_URL=‘/’
Default primary key field type
Settings | Django documentation | Django
DEFAULT_AUTO_FIELD = ‘django.db.models.BigAutoField’
‘’’
routing.py
‘’'from django.urls import re_path
from . import consumers
websocket_urlpatterns = [
re_path(r’ws/student/start-exam/(?P\d+)/$‘, consumers.CountdownConsumer.as_asgi())
]’‘’