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
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”: “Issues · farridav/django-jazzmin · GitHub”, “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())
]’‘’