JaivinBarot commited on
Commit
b756f0e
·
verified ·
1 Parent(s): 5fdf56e

Upload 34 files

Browse files
Files changed (35) hide show
  1. .gitattributes +1 -0
  2. Dockerfile +16 -0
  3. ExamscheduleViewer/__init__.py +0 -0
  4. ExamscheduleViewer/__pycache__/__init__.cpython-312.pyc +0 -0
  5. ExamscheduleViewer/__pycache__/settings.cpython-312.pyc +0 -0
  6. ExamscheduleViewer/__pycache__/urls.cpython-312.pyc +0 -0
  7. ExamscheduleViewer/__pycache__/wsgi.cpython-312.pyc +0 -0
  8. ExamscheduleViewer/asgi.py +16 -0
  9. ExamscheduleViewer/settings.py +124 -0
  10. ExamscheduleViewer/urls.py +23 -0
  11. ExamscheduleViewer/wsgi.py +16 -0
  12. db.sqlite3 +3 -0
  13. genrateExamSchedule/__init__.py +0 -0
  14. genrateExamSchedule/__pycache__/__init__.cpython-312.pyc +0 -0
  15. genrateExamSchedule/__pycache__/admin.cpython-312.pyc +0 -0
  16. genrateExamSchedule/__pycache__/apps.cpython-312.pyc +0 -0
  17. genrateExamSchedule/__pycache__/models.cpython-312.pyc +0 -0
  18. genrateExamSchedule/__pycache__/urls.cpython-312.pyc +0 -0
  19. genrateExamSchedule/__pycache__/views.cpython-312.pyc +0 -0
  20. genrateExamSchedule/admin.py +26 -0
  21. genrateExamSchedule/apps.py +6 -0
  22. genrateExamSchedule/migrations/0001_initial.py +30 -0
  23. genrateExamSchedule/migrations/__init__.py +0 -0
  24. genrateExamSchedule/migrations/__pycache__/0001_initial.cpython-312.pyc +0 -0
  25. genrateExamSchedule/migrations/__pycache__/__init__.cpython-312.pyc +0 -0
  26. genrateExamSchedule/models.py +21 -0
  27. genrateExamSchedule/tests.py +3 -0
  28. genrateExamSchedule/urls.py +14 -0
  29. genrateExamSchedule/views.py +408 -0
  30. manage.py +22 -0
  31. requirements.txt +0 -0
  32. templates/index.html +80 -0
  33. templates/login.html +61 -0
  34. templates/upload_schedule.html +81 -0
  35. templates/view_schedule.html +124 -0
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ db.sqlite3 filter=lfs diff=lfs merge=lfs -text
Dockerfile ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Read the doc: https://huggingface.co/docs/hub/spaces-sdks-docker
2
+ # you will also find guides on how best to write your Dockerfile
3
+
4
+ FROM python:3.9
5
+
6
+ RUN useradd -m -u 1000 user
7
+ USER user
8
+ ENV PATH="/home/user/.local/bin:$PATH"
9
+
10
+ WORKDIR /app
11
+
12
+ COPY --chown=user ./requirements.txt requirements.txt
13
+ RUN pip install --no-cache-dir --upgrade -r requirements.txt
14
+
15
+ COPY --chown=user . /app
16
+ CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
ExamscheduleViewer/__init__.py ADDED
File without changes
ExamscheduleViewer/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (149 Bytes). View file
 
ExamscheduleViewer/__pycache__/settings.cpython-312.pyc ADDED
Binary file (2.6 kB). View file
 
ExamscheduleViewer/__pycache__/urls.cpython-312.pyc ADDED
Binary file (1.12 kB). View file
 
ExamscheduleViewer/__pycache__/wsgi.cpython-312.pyc ADDED
Binary file (659 Bytes). View file
 
ExamscheduleViewer/asgi.py ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ ASGI config for ExamscheduleViewer project.
3
+
4
+ It exposes the ASGI callable as a module-level variable named ``application``.
5
+
6
+ For more information on this file, see
7
+ https://docs.djangoproject.com/en/5.0/howto/deployment/asgi/
8
+ """
9
+
10
+ import os
11
+
12
+ from django.core.asgi import get_asgi_application
13
+
14
+ os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'ExamscheduleViewer.settings')
15
+
16
+ application = get_asgi_application()
ExamscheduleViewer/settings.py ADDED
@@ -0,0 +1,124 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Django settings for ExamscheduleViewer project.
3
+
4
+ Generated by 'django-admin startproject' using Django 5.0.4.
5
+
6
+ For more information on this file, see
7
+ https://docs.djangoproject.com/en/5.0/topics/settings/
8
+
9
+ For the full list of settings and their values, see
10
+ https://docs.djangoproject.com/en/5.0/ref/settings/
11
+ """
12
+
13
+ from pathlib import Path
14
+
15
+ # Build paths inside the project like this: BASE_DIR / 'subdir'.
16
+ BASE_DIR = Path(__file__).resolve().parent.parent
17
+
18
+
19
+ # Quick-start development settings - unsuitable for production
20
+ # See https://docs.djangoproject.com/en/5.0/howto/deployment/checklist/
21
+
22
+ # SECURITY WARNING: keep the secret key used in production secret!
23
+ SECRET_KEY = 'django-insecure-**@-tuvqyk^ildsh4(g7t^dqa5-9t$n)i6a3+k7wo$9as-r+@d'
24
+
25
+ # SECURITY WARNING: don't run with debug turned on in production!
26
+ DEBUG = True
27
+
28
+ ALLOWED_HOSTS = ["*"]
29
+
30
+
31
+ # Application definition
32
+
33
+ INSTALLED_APPS = [
34
+ 'django.contrib.admin',
35
+ 'django.contrib.auth',
36
+ 'django.contrib.contenttypes',
37
+ 'django.contrib.sessions',
38
+ 'django.contrib.messages',
39
+ 'django.contrib.staticfiles',
40
+ 'genrateExamSchedule',
41
+ ]
42
+
43
+ MIDDLEWARE = [
44
+ 'django.middleware.security.SecurityMiddleware',
45
+ 'django.contrib.sessions.middleware.SessionMiddleware',
46
+ 'django.middleware.common.CommonMiddleware',
47
+ 'django.middleware.csrf.CsrfViewMiddleware',
48
+ 'django.contrib.auth.middleware.AuthenticationMiddleware',
49
+ 'django.contrib.messages.middleware.MessageMiddleware',
50
+ 'django.middleware.clickjacking.XFrameOptionsMiddleware',
51
+ ]
52
+
53
+ ROOT_URLCONF = 'ExamscheduleViewer.urls'
54
+
55
+ TEMPLATES = [
56
+ {
57
+ 'BACKEND': 'django.template.backends.django.DjangoTemplates',
58
+ 'DIRS': ['templates'],
59
+ 'APP_DIRS': True,
60
+ 'OPTIONS': {
61
+ 'context_processors': [
62
+ 'django.template.context_processors.debug',
63
+ 'django.template.context_processors.request',
64
+ 'django.contrib.auth.context_processors.auth',
65
+ 'django.contrib.messages.context_processors.messages',
66
+ ],
67
+ },
68
+ },
69
+ ]
70
+
71
+ WSGI_APPLICATION = 'ExamscheduleViewer.wsgi.application'
72
+
73
+
74
+ # Database
75
+ # https://docs.djangoproject.com/en/5.0/ref/settings/#databases
76
+
77
+ DATABASES = {
78
+ 'default': {
79
+ 'ENGINE': 'django.db.backends.sqlite3',
80
+ 'NAME': BASE_DIR / 'db.sqlite3',
81
+ }
82
+ }
83
+
84
+
85
+ # Password validation
86
+ # https://docs.djangoproject.com/en/5.0/ref/settings/#auth-password-validators
87
+
88
+ AUTH_PASSWORD_VALIDATORS = [
89
+ {
90
+ 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
91
+ },
92
+ {
93
+ 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
94
+ },
95
+ {
96
+ 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
97
+ },
98
+ {
99
+ 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
100
+ },
101
+ ]
102
+
103
+
104
+ # Internationalization
105
+ # https://docs.djangoproject.com/en/5.0/topics/i18n/
106
+
107
+ LANGUAGE_CODE = 'en-us'
108
+
109
+ TIME_ZONE = 'UTC'
110
+
111
+ USE_I18N = True
112
+
113
+ USE_TZ = True
114
+
115
+
116
+ # Static files (CSS, JavaScript, Images)
117
+ # https://docs.djangoproject.com/en/5.0/howto/static-files/
118
+
119
+ STATIC_URL = 'static/'
120
+
121
+ # Default primary key field type
122
+ # https://docs.djangoproject.com/en/5.0/ref/settings/#default-auto-field
123
+
124
+ DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
ExamscheduleViewer/urls.py ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ URL configuration for ExamscheduleViewer project.
3
+
4
+ The `urlpatterns` list routes URLs to views. For more information please see:
5
+ https://docs.djangoproject.com/en/5.0/topics/http/urls/
6
+ Examples:
7
+ Function views
8
+ 1. Add an import: from my_app import views
9
+ 2. Add a URL to urlpatterns: path('', views.home, name='home')
10
+ Class-based views
11
+ 1. Add an import: from other_app.views import Home
12
+ 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
13
+ Including another URLconf
14
+ 1. Import the include() function: from django.urls import include, path
15
+ 2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
16
+ """
17
+ from django.contrib import admin
18
+ from django.urls import path, include
19
+
20
+ urlpatterns = [
21
+ path('admin/', admin.site.urls),
22
+ path('', include('genrateExamSchedule.urls')),
23
+ ]
ExamscheduleViewer/wsgi.py ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ WSGI config for ExamscheduleViewer project.
3
+
4
+ It exposes the WSGI callable as a module-level variable named ``application``.
5
+
6
+ For more information on this file, see
7
+ https://docs.djangoproject.com/en/5.0/howto/deployment/wsgi/
8
+ """
9
+
10
+ import os
11
+
12
+ from django.core.wsgi import get_wsgi_application
13
+
14
+ os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'ExamscheduleViewer.settings')
15
+
16
+ application = get_wsgi_application()
db.sqlite3 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e9c9c2909f8ec33b1ba647b80aab25ee6e74ddb8f7d38e4dc5a011a168084dd3
3
+ size 466944
genrateExamSchedule/__init__.py ADDED
File without changes
genrateExamSchedule/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (150 Bytes). View file
 
genrateExamSchedule/__pycache__/admin.cpython-312.pyc ADDED
Binary file (1.75 kB). View file
 
genrateExamSchedule/__pycache__/apps.cpython-312.pyc ADDED
Binary file (482 Bytes). View file
 
genrateExamSchedule/__pycache__/models.cpython-312.pyc ADDED
Binary file (1.56 kB). View file
 
genrateExamSchedule/__pycache__/urls.cpython-312.pyc ADDED
Binary file (862 Bytes). View file
 
genrateExamSchedule/__pycache__/views.cpython-312.pyc ADDED
Binary file (18.4 kB). View file
 
genrateExamSchedule/admin.py ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from django.contrib import admin
2
+ from .models import ExamSchedule
3
+ from django.http import HttpResponse
4
+ # Register your models here.
5
+
6
+ class ExamScheduleAdmin(admin.ModelAdmin):
7
+ list_display = ('student_name', 'student_id', 'exam_date', 'room_number', 'subject_name', 'subject_code', 'seat_Number')
8
+ search_fields = ('student_name', 'student_id', 'subject_name')
9
+ list_filter = ('exam_date', 'room_number')
10
+ ordering = ('exam_date', 'student_name')
11
+ list_per_page = 500
12
+ actions = ['export_as_csv']
13
+
14
+ def export_as_csv(self, request, queryset):
15
+ import csv
16
+ response = HttpResponse(content_type='text/csv')
17
+ response['Content-Disposition'] = 'attachment; filename="exam_schedule.csv"'
18
+ writer = csv.writer(response)
19
+ writer.writerow(['student_name', 'student_id', 'exam_date', 'room_number', 'subject_name', 'subject_code', 'seat_Number'])
20
+ for schedule in queryset:
21
+ writer.writerow([schedule.student_name, schedule.student_id, schedule.exam_date, schedule.room_number, schedule.subject_name, schedule.subject_code, schedule.seat_Number])
22
+ return response
23
+
24
+
25
+ admin.site.register(ExamSchedule, ExamScheduleAdmin)
26
+
genrateExamSchedule/apps.py ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ from django.apps import AppConfig
2
+
3
+
4
+ class GenrateexamscheduleConfig(AppConfig):
5
+ default_auto_field = 'django.db.models.BigAutoField'
6
+ name = 'genrateExamSchedule'
genrateExamSchedule/migrations/0001_initial.py ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Generated by Django 5.0.4 on 2025-04-08 04:29
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ initial = True
9
+
10
+ dependencies = [
11
+ ]
12
+
13
+ operations = [
14
+ migrations.CreateModel(
15
+ name='ExamSchedule',
16
+ fields=[
17
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
18
+ ('student_name', models.CharField(max_length=100)),
19
+ ('student_id', models.CharField(max_length=20)),
20
+ ('exam_date', models.DateField()),
21
+ ('room_number', models.CharField(max_length=10)),
22
+ ('subject_name', models.CharField(max_length=100)),
23
+ ('subject_code', models.CharField(max_length=20)),
24
+ ('seat_Number', models.CharField(max_length=10)),
25
+ ],
26
+ options={
27
+ 'unique_together': {('exam_date', 'student_id', 'subject_name')},
28
+ },
29
+ ),
30
+ ]
genrateExamSchedule/migrations/__init__.py ADDED
File without changes
genrateExamSchedule/migrations/__pycache__/0001_initial.cpython-312.pyc ADDED
Binary file (1.41 kB). View file
 
genrateExamSchedule/migrations/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (161 Bytes). View file
 
genrateExamSchedule/models.py ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from django.db import models
2
+
3
+ # Create your models here.
4
+
5
+
6
+ class ExamSchedule(models.Model):
7
+ student_name = models.CharField(max_length=100)
8
+ student_id = models.CharField(max_length=20)
9
+ exam_date = models.DateField()
10
+ room_number = models.CharField(max_length=10)
11
+ subject_name = models.CharField(max_length=100)
12
+ subject_code = models.CharField(max_length=20)
13
+ seat_Number = models.CharField(max_length=10)
14
+
15
+
16
+ def __str__(self):
17
+ return f"{self.student_name} - {self.exam_date} - {self.room_number} - {self.subject_name} - {self.subject_code} - {self.seat_Number}"
18
+
19
+ class Meta:
20
+ unique_together = ('exam_date','student_id', 'subject_name')
21
+
genrateExamSchedule/tests.py ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ from django.test import TestCase
2
+
3
+ # Create your tests here.
genrateExamSchedule/urls.py ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ from django.contrib import admin
3
+ from django.urls import path, include
4
+ from . import views
5
+
6
+ urlpatterns = [
7
+ path('', views.index, name='index'),
8
+ path('login/', views.login_view, name='login_view'),
9
+ path('logout/', views.logout_view, name='logout_view'),
10
+ path('generate_schedule/', views.generate_schedule, name='generate_schedule'),
11
+ path('uplaod_schedule/', views.upload_schedule, name='upload_schedule'),
12
+ path('view_schedule/', views.view_schedule, name='view_schedule'),
13
+
14
+ ]
genrateExamSchedule/views.py ADDED
@@ -0,0 +1,408 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from django.shortcuts import render,redirect
2
+ from django.http import HttpResponse
3
+ import pandas as pd
4
+ import numpy as np
5
+ from collections import defaultdict
6
+ import random
7
+ import ast
8
+ import os
9
+ import io
10
+ from .models import ExamSchedule
11
+ import openpyxl
12
+ from datetime import datetime, timedelta
13
+ from django.contrib.auth.decorators import login_required
14
+ from django.contrib.auth import login, authenticate
15
+ from django.contrib.auth.models import User
16
+
17
+
18
+ def login_view(request):
19
+ if request.method == 'POST':
20
+ username = request.POST.get('username')
21
+ password = request.POST.get('password')
22
+ if username == 'admin' and password == 'admin':
23
+ request.session['is_login'] = True
24
+
25
+ return redirect('index')
26
+ else:
27
+ request.session['is_login'] = False
28
+ return render(request, 'login.html', {'error': 'Invalid username or password'})
29
+
30
+ return render(request,'login.html')
31
+
32
+ def logout_view(request):
33
+ request.session['is_login'] = False
34
+ return redirect('login_view')
35
+ class ExamSeatingPlanGenerator:
36
+ def __init__(self):
37
+ self.subject_data = None
38
+ self.room_data = None
39
+ self.student_data = None
40
+ self.seating_plan = []
41
+ self.unassigned_students = []
42
+
43
+ def load_data_from_excel(self, room_file, exam_schedule_file, student_file):
44
+ """Load input data from Excel files"""
45
+ # Load room data
46
+ self.room_data = pd.read_excel(room_file)
47
+ # Rename columns to match the expected column names in the algorithm
48
+ self.room_data = self.room_data.rename(columns={
49
+ 'Room Number': 'RoomNo',
50
+ 'Capacity': 'RoomCapacity'
51
+ })
52
+
53
+ # Load exam schedule data
54
+ self.subject_data = pd.read_excel(exam_schedule_file)
55
+
56
+ # Process the complex columns which contain lists
57
+ self._process_complex_exam_schedule()
58
+
59
+ # Load student data
60
+ self.student_data = pd.read_excel(student_file)
61
+ # Rename columns to match the expected column names in the algorithm
62
+ self.student_data = self.student_data.rename(columns={
63
+ 'studentId': 'student Id',
64
+ 'Name': 'student name',
65
+ 'Subject Name': 'subject name'
66
+ })
67
+
68
+ print(f"Loaded {len(self.subject_data)} subjects, {len(self.room_data)} rooms, {len(self.student_data)} students")
69
+
70
+ def _process_complex_exam_schedule(self):
71
+ """Process the complex exam schedule data where room numbers, max students allowed, and sides are lists"""
72
+ # Create expanded subject data
73
+ expanded_subjects = []
74
+
75
+ for _, row in self.subject_data.iterrows():
76
+ # Extract exam date (convert to string if it's a datetime)
77
+ exam_date = row.get('Date', None)
78
+ if pd.isna(exam_date):
79
+ exam_date = row.get('Exam Date', None)
80
+ if isinstance(exam_date, pd.Timestamp):
81
+ exam_date = exam_date.strftime('%Y-%m-%d')
82
+
83
+ # Extract subject code and name
84
+ subject_code = row.get('Subject Code', row.get('subject Code', None))
85
+ subject_name = row.get('Subject Name', row.get('subject Name', None))
86
+
87
+ # Get the list of room numbers
88
+ room_numbers_str = str(row.get('list of Room Numbers', row.get('Room Numbers', [])))
89
+ max_students_str = str(row.get('MaxStudents Allowed in rooms', row.get('MaxStudentsAllowed', [])))
90
+ assigned_sides_str = str(row.get('Assigned Sides', []))
91
+
92
+ # Parse the lists
93
+ try:
94
+ # Try to parse as a Python list literal
95
+ room_numbers = self._parse_list_from_string(room_numbers_str)
96
+ max_students = self._parse_list_from_string(max_students_str)
97
+ assigned_sides = self._parse_list_from_string(assigned_sides_str)
98
+
99
+ # Ensure each list has the same length or handle appropriately
100
+ num_rooms = len(room_numbers)
101
+ if len(max_students) != num_rooms:
102
+ print(f"Warning: Mismatch in list lengths for {subject_code}. Rooms: {num_rooms}, Max Students: {len(max_students)}")
103
+ max_students = max_students * num_rooms if len(max_students) == 1 else max_students[:num_rooms]
104
+
105
+ if len(assigned_sides) != num_rooms:
106
+ print(f"Warning: Mismatch in list lengths for {subject_code}. Rooms: {num_rooms}, Sides: {len(assigned_sides)}")
107
+ assigned_sides = assigned_sides * num_rooms if len(assigned_sides) == 1 else assigned_sides[:num_rooms]
108
+
109
+ # Create an entry for each room
110
+ for i in range(num_rooms):
111
+ expanded_subjects.append({
112
+ 'Exam Date': exam_date,
113
+ 'Subject Code': subject_code,
114
+ 'Subject Name': subject_name,
115
+ 'Room Number': str(room_numbers[i]),
116
+ 'MaxStudents Allowed': int(max_students[i]) if i < len(max_students) else 0,
117
+ 'Assigned Side': assigned_sides[i] if i < len(assigned_sides) else 'L'
118
+ })
119
+ except Exception as e:
120
+ print(f"Error processing row for {subject_code}: {e}")
121
+ # Add a default entry to avoid data loss
122
+ expanded_subjects.append({
123
+ 'Exam Date': exam_date,
124
+ 'Subject Code': subject_code,
125
+ 'Subject Name': subject_name,
126
+ 'Room Number': str(room_numbers_str).strip('[]'),
127
+ 'MaxStudents Allowed': 0,
128
+ 'Assigned Side': 'L'
129
+ })
130
+
131
+ # Replace the subject data with the expanded version
132
+ self.subject_data = pd.DataFrame(expanded_subjects)
133
+
134
+ def _parse_list_from_string(self, list_str):
135
+ """Parse a list from a string representation"""
136
+ # Remove any non-standard formatting
137
+ list_str = list_str.strip()
138
+
139
+ # Try different parsing methods
140
+ try:
141
+ # Try to use ast.literal_eval for safe parsing
142
+ return ast.literal_eval(list_str)
143
+ except:
144
+ # Try to handle comma-separated values in brackets
145
+ if list_str.startswith('[') and list_str.endswith(']'):
146
+ items = list_str[1:-1].split(',')
147
+ return [item.strip() for item in items]
148
+ # Try to handle comma-separated values without brackets
149
+ else:
150
+ return [item.strip() for item in list_str.split(',')]
151
+
152
+ def generate_seating_plan(self):
153
+ """Generate the full seating plan according to requirements"""
154
+ # Group students by subject
155
+ subject_students = defaultdict(list)
156
+ for _, student in self.student_data.iterrows():
157
+ subject_students[student['subject name']].append({
158
+ 'name': student['student name'],
159
+ 'id': student['student Id']
160
+ })
161
+
162
+ # Shuffle students within each subject for randomized assignment
163
+ for subject in subject_students:
164
+ random.shuffle(subject_students[subject])
165
+
166
+ # Track seat numbers for each side (L/R) in each room
167
+ seat_counters = {str(room): {'L': 1, 'R': 1} for room in self.room_data['RoomNo']}
168
+
169
+ # Group subject data by subject code, date
170
+ subject_groups = self.subject_data.groupby(['Exam Date', 'Subject Code', 'Subject Name'])
171
+
172
+ for (exam_date, subject_code, subject_name), group in subject_groups:
173
+ # Get students for this subject
174
+ students = subject_students.get(subject_name, [])
175
+ if not students:
176
+ print(f"Warning: No students found for subject {subject_name}")
177
+ continue
178
+
179
+ students_assigned = 0
180
+ student_idx = 0
181
+
182
+ # Process each room for this subject
183
+ for _, room_row in group.iterrows():
184
+ room_no = str(room_row['Room Number'])
185
+ max_students = int(room_row['MaxStudents Allowed'])
186
+ assigned_side = room_row['Assigned Side']
187
+
188
+ # Get room capacity
189
+ room_info = self.room_data[self.room_data['RoomNo'].astype(str) == room_no]
190
+ if len(room_info) == 0:
191
+ print(f"Warning: Room {room_no} not found in room data")
192
+ continue
193
+
194
+ bench_capacity = int(room_info.iloc[0]['RoomCapacity'])
195
+ total_seat_capacity = bench_capacity * 2
196
+
197
+ # Calculate how many students to assign to this room
198
+ students_to_assign = min(max_students, len(students) - student_idx)
199
+ if students_to_assign <= 0:
200
+ continue # Skip if no more students to assign
201
+
202
+ # Calculate number of benches needed on this side
203
+ benches_needed = (students_to_assign + 1) // 2 # Ceiling division
204
+
205
+ # Assign seats to students
206
+ for bench in range(benches_needed):
207
+ # First student in bench
208
+ if student_idx < len(students):
209
+ student = students[student_idx]
210
+ seat_number = f"{assigned_side}{seat_counters[room_no][assigned_side]}"
211
+
212
+ self.seating_plan.append({
213
+ 'Exam Date': exam_date,
214
+ 'Room Number': room_no,
215
+ 'Bench Number': bench + 1,
216
+ 'Seat Position': assigned_side,
217
+ 'Seat Number': seat_number,
218
+ 'Student ID': student['id'],
219
+ 'Student Name': student['name'],
220
+ 'Subject Code': subject_code,
221
+ 'Subject Name': subject_name
222
+ })
223
+
224
+ seat_counters[room_no][assigned_side] += 1
225
+ students_assigned += 1
226
+ student_idx += 1
227
+
228
+ # Second student in bench (if available and not the last odd student)
229
+ if student_idx < len(students) and (bench < benches_needed - 1 or students_to_assign % 2 == 0):
230
+ student = students[student_idx]
231
+ seat_number = f"{assigned_side}{seat_counters[room_no][assigned_side]}"
232
+
233
+ self.seating_plan.append({
234
+ 'Exam Date': exam_date,
235
+ 'Room Number': room_no,
236
+ 'Bench Number': bench + 1,
237
+ 'Seat Position': assigned_side,
238
+ 'Seat Number': seat_number,
239
+ 'Student ID': student['id'],
240
+ 'Student Name': student['name'],
241
+ 'Subject Code': subject_code,
242
+ 'Subject Name': subject_name
243
+ })
244
+
245
+ seat_counters[room_no][assigned_side] += 1
246
+ students_assigned += 1
247
+ student_idx += 1
248
+
249
+ # Record unassigned students
250
+ remaining_students = len(students) - students_assigned
251
+ if remaining_students > 0:
252
+ for i in range(students_assigned, len(students)):
253
+ student = students[i]
254
+ self.unassigned_students.append({
255
+ 'Exam Date': exam_date,
256
+ 'Student ID': student['id'],
257
+ 'Student Name': student['name'],
258
+ 'Subject Code': subject_code,
259
+ 'Subject Name': subject_name,
260
+ 'Reason': 'Insufficient room capacity'
261
+ })
262
+
263
+ print(f"Assigned {len(self.seating_plan)} students to seats")
264
+ print(f"Unable to assign {len(self.unassigned_students)} students")
265
+
266
+ def export_to_excel(self, output=None):
267
+ """Export seating plan and unassigned students to Excel
268
+
269
+ Args:
270
+ output: Can be a file path or an IO buffer. If None, returns a BytesIO object.
271
+
272
+ Returns:
273
+ BytesIO if output is None
274
+ """
275
+ # If no output is provided, create an in-memory buffer
276
+ if output is None:
277
+ output = io.BytesIO()
278
+
279
+ with pd.ExcelWriter(output, engine='openpyxl') as writer:
280
+ # Export seating plan
281
+ if self.seating_plan:
282
+ seating_df = pd.DataFrame(self.seating_plan)
283
+ seating_df.sort_values(['Exam Date', 'Room Number', 'Bench Number', 'Seat Position'], inplace=True)
284
+ seating_df.to_excel(writer, sheet_name='SeatingPlan', index=False)
285
+
286
+ # Export unassigned students
287
+ if self.unassigned_students:
288
+ unassigned_df = pd.DataFrame(self.unassigned_students)
289
+ unassigned_df.to_excel(writer, sheet_name='Unassigned Students', index=False)
290
+
291
+ # Export room usage summary
292
+ if self.seating_plan:
293
+ room_usage = pd.DataFrame(self.seating_plan).groupby(['Exam Date', 'Room Number']).size().reset_index()
294
+ room_usage.columns = ['Exam Date', 'Room Number', 'Students Assigned']
295
+ room_capacity = self.room_data.copy()
296
+ room_capacity['Student Capacity'] = room_capacity['RoomCapacity'] * 2
297
+ room_capacity['RoomNo'] = room_capacity['RoomNo'].astype(str)
298
+ room_usage = pd.merge(room_capacity, room_usage, left_on='RoomNo', right_on='Room Number', how='right')
299
+ room_usage['Students Assigned'] = room_usage['Students Assigned'].fillna(0).astype(int)
300
+ room_usage['Utilization %'] = (room_usage['Students Assigned'] / room_usage['Student Capacity'] * 100).round(1)
301
+ room_usage.to_excel(writer, sheet_name='RoomUtilization', index=False)
302
+
303
+ # Export subject summary
304
+ if self.seating_plan:
305
+ subject_summary = pd.DataFrame(self.seating_plan).groupby(['Exam Date', 'Subject Code', 'Subject Name']).size().reset_index()
306
+ subject_summary.columns = ['Exam Date', 'Subject Code', 'Subject Name', 'Students Assigned']
307
+
308
+ if self.unassigned_students:
309
+ subject_unassigned = pd.DataFrame(self.unassigned_students).groupby(['Exam Date', 'Subject Code', 'Subject Name']).size().reset_index()
310
+ subject_unassigned.columns = ['Exam Date', 'Subject Code', 'Subject Name', 'Students Unassigned']
311
+ subject_summary = pd.merge(subject_summary, subject_unassigned, on=['Exam Date', 'Subject Code', 'Subject Name'], how='left')
312
+ subject_summary['Students Unassigned'] = subject_summary['Students Unassigned'].fillna(0).astype(int)
313
+ else:
314
+ subject_summary['Students Unassigned'] = 0
315
+
316
+ subject_summary['Total Students'] = subject_summary['Students Assigned'] + subject_summary['Students Unassigned']
317
+ subject_summary.to_excel(writer, sheet_name='SubjectSummary', index=False)
318
+
319
+ # If using BytesIO, seek to beginning for reading
320
+ if isinstance(output, io.BytesIO):
321
+ output.seek(0)
322
+ return output
323
+
324
+ print(f"Seating plan exported")
325
+
326
+
327
+ def index(request):
328
+ if request.session['is_login'] == False:
329
+ return redirect('login_view')
330
+ else:
331
+ return render(request, 'index.html')
332
+ # @login_required('login')
333
+ def generate_schedule(request):
334
+ if request.session['is_login'] == False:
335
+ return redirect('login_view')
336
+ else:
337
+ if request.method == 'POST':
338
+ room_file = request.FILES['room_data']
339
+ exam_schedule_file = request.FILES['exam_schedule']
340
+ student_file = request.FILES['student_data']
341
+
342
+ generator = ExamSeatingPlanGenerator()
343
+ generator.load_data_from_excel(room_file, exam_schedule_file, student_file)
344
+ generator.generate_seating_plan()
345
+
346
+ # Generate Excel file in memory instead of saving to disk
347
+ output = generator.export_to_excel()
348
+
349
+ # Create response with the in-memory file
350
+ response = HttpResponse(
351
+ output.getvalue(),
352
+ content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
353
+ )
354
+ response['Content-Disposition'] = 'attachment; filename=Exam_Seating_Plan_Output.xlsx'
355
+ return response
356
+
357
+ return render(request, 'generate_schedule.html')
358
+
359
+ # @login_required('login')
360
+ def upload_schedule(request):
361
+ if request.session['is_login'] == False:
362
+ return redirect('login_view')
363
+ else:
364
+ if request.method == 'POST':
365
+ schedule_file = request.FILES['schedule_file']
366
+ # Load the workbook and select the Seating Plan sheet
367
+ workbook = openpyxl.load_workbook(schedule_file)
368
+ sheet = workbook['SeatingPlan']
369
+
370
+ # Iterate over the rows of the sheet and insert data into the database
371
+ for row in sheet.iter_rows(min_row=2, values_only=True): # Assuming first row is header
372
+ exam_date,room_number,benchNumber,seat_position,seat_number,student_id,student_name,subject_code,subject_name = row
373
+ ExamSchedule.objects.create(
374
+ student_name=student_name,
375
+ student_id=student_id,
376
+ exam_date=datetime.strptime(str(exam_date), '%Y-%m-%d').date(),
377
+ room_number=room_number,
378
+ subject_name=subject_name,
379
+ subject_code=subject_code,
380
+ seat_Number=seat_number
381
+ )
382
+
383
+
384
+ return render(request, 'upload_schedule.html',{'message': 'Schedule uploaded successfully!'})
385
+ return render(request, 'upload_schedule.html')
386
+
387
+
388
+ def view_schedule(request):
389
+
390
+ if request.method == 'POST':
391
+ student_id = request.POST.get('student_id')
392
+ exam_date = request.POST.get('exam_date',None)
393
+ today = datetime.today().date()
394
+ tomorrow = today + timedelta(days=1)
395
+ schedule = ExamSchedule.objects.filter(student_id=student_id, exam_date__in=[today,tomorrow])
396
+ print(schedule)
397
+
398
+ if len(schedule) > 0:
399
+ return render(request, 'view_schedule.html', {'schedules': schedule})
400
+ else:
401
+ return render(request, 'view_schedule.html', {'message': 'No schedule found for this student ID'})
402
+ else:
403
+ return render(request, 'view_schedule.html')
404
+
405
+
406
+
407
+
408
+
manage.py ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python
2
+ """Django's command-line utility for administrative tasks."""
3
+ import os
4
+ import sys
5
+
6
+
7
+ def main():
8
+ """Run administrative tasks."""
9
+ os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'ExamscheduleViewer.settings')
10
+ try:
11
+ from django.core.management import execute_from_command_line
12
+ except ImportError as exc:
13
+ raise ImportError(
14
+ "Couldn't import Django. Are you sure it's installed and "
15
+ "available on your PYTHONPATH environment variable? Did you "
16
+ "forget to activate a virtual environment?"
17
+ ) from exc
18
+ execute_from_command_line(sys.argv)
19
+
20
+
21
+ if __name__ == '__main__':
22
+ main()
requirements.txt ADDED
Binary file (532 Bytes). View file
 
templates/index.html ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Generate Schedule</title>
7
+ <style>
8
+ form {
9
+ width: 400px;
10
+ margin: 0 auto;
11
+ padding: 20px;
12
+ background-color: #f2f2f2;
13
+ border: 1px solid #ccc;
14
+ border-radius: 5px;
15
+ box-shadow: 0 0 10px rgba(0,0,0,0.2);
16
+ margin-top: 10%;
17
+ }
18
+
19
+ label {
20
+ display: block;
21
+ margin-bottom: 10px;
22
+ }
23
+
24
+ input[type="file"] {
25
+ display: block;
26
+ margin-bottom: 10px;
27
+ padding: 10px;
28
+ border: 1px solid #ccc;
29
+ }
30
+
31
+ input[type="submit"] {
32
+ width: 100%;
33
+ background-color: #4CAF50;
34
+ color: white;
35
+ padding: 14px 20px;
36
+ margin: 8px 0;
37
+ border: none;
38
+ border-radius: 4px;
39
+ cursor: pointer;
40
+ }
41
+ nav {
42
+ text-align: center;
43
+ margin-bottom: 20px;
44
+ }
45
+ nav a {
46
+ margin: 0 15px;
47
+ text-decoration: none;
48
+ color: #333;
49
+ }
50
+ nav a:hover {
51
+ text-decoration: underline;
52
+ }
53
+ body {
54
+ font-family: Arial, sans-serif;
55
+ background-color: #f2f2f2;
56
+ margin: 0;
57
+ padding: 20px;
58
+ }
59
+ </style>
60
+ </head>
61
+ <body>
62
+ <nav>
63
+ <a href="{% url 'index' %}">Home</a> |
64
+ <a href="{% url 'view_schedule' %}">View Schedule</a> |
65
+ <a href="{% url 'logout_view' %}">Logout</a>
66
+ </nav>
67
+ <form action="{% url 'generate_schedule' %}" method="post" enctype="multipart/form-data">
68
+ {% csrf_token %}
69
+ <label for="room_data">Room Data:</label>
70
+ <input type="file" name="room_data" id="room_data" accept=".xlsx"><br>
71
+ <label for="exam_schedule">Exam Schedule:</label>
72
+ <input type="file" name="exam_schedule" id="exam_schedule" accept=".xlsx"><br>
73
+ <label for="student_data">Student Data:</label>
74
+ <input type="file" name="student_data" id="student_data" accept=".xlsx"><br>
75
+ <input type="submit" value="Generate Schedule">
76
+ </form>
77
+
78
+ </body>
79
+
80
+ </html>
templates/login.html ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Login</title>
7
+ <style>
8
+ body {
9
+ font-family: Arial, sans-serif;
10
+ background-color: #f2f2f2;
11
+ margin: 0;
12
+ padding: 20px;
13
+ }
14
+
15
+ h1 {
16
+ text-align: center;
17
+ }
18
+
19
+ form {
20
+ text-align: center;
21
+ margin-bottom: 20px;
22
+ }
23
+
24
+ label {
25
+ display: block;
26
+ margin-bottom: 5px;
27
+ font-weight: bold;
28
+ }
29
+
30
+ input[type="text"],
31
+ input[type="date"] {
32
+ padding:6px;
33
+ border: 1px solid #ccc;
34
+ border-radius: 4px;
35
+ }
36
+
37
+ input[type="submit"] {
38
+ padding: 8px 16px;
39
+ background-color: #4CAF50;
40
+ color: white;
41
+ border: none;
42
+ border-radius: 4px;
43
+ cursor: pointer;
44
+ }
45
+ </style>
46
+ </head>
47
+ <body>
48
+
49
+ <h1>Login</h1>
50
+ <form action="" method="post">
51
+ {% csrf_token %}
52
+ <label for="username">Username:</label><br>
53
+ <input type="text" id="username" name="username"><br><br>
54
+ <label for="password">Password:</label><br>
55
+ <input type="password" id="password" name="password" style="margin-bottom: 10px;"><br>
56
+
57
+ <input type="submit" value="Submit">
58
+ </form>
59
+ <p>{{ message }}</p>
60
+ </body>
61
+ </html>
templates/upload_schedule.html ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Upload Schedule</title>
7
+ <style>
8
+ form {
9
+ width: 400px;
10
+ margin: 0 auto;
11
+ padding: 20px;
12
+ background-color: #f2f2f2;
13
+ border: 1px solid #ccc;
14
+ border-radius: 5px;
15
+ box-shadow: 0 0 10px rgba(0,0,0,0.2);
16
+ margin-top: 10%;
17
+ }
18
+
19
+ label {
20
+ display: block;
21
+ margin-bottom: 10px;
22
+ }
23
+
24
+ input[type="file"] {
25
+ display: block;
26
+ margin-bottom: 10px;
27
+ padding: 10px;
28
+ border: 1px solid #ccc;
29
+ }
30
+
31
+ input[type="submit"] {
32
+ width: 100%;
33
+ background-color: #4CAF50;
34
+ color: white;
35
+ padding: 14px 20px;
36
+ margin: 8px 0;
37
+ border: none;
38
+ border-radius: 4px;
39
+ cursor: pointer;
40
+ }
41
+ nav {
42
+ text-align: center;
43
+ margin-bottom: 20px;
44
+ }
45
+ nav a {
46
+ margin: 0 15px;
47
+ text-decoration: none;
48
+ color: #333;
49
+ }
50
+ nav a:hover {
51
+ text-decoration: underline;
52
+ }
53
+ body {
54
+ font-family: Arial, sans-serif;
55
+ background-color: #f2f2f2;
56
+ margin: 0;
57
+ padding: 20px;
58
+ }
59
+ </style>
60
+ </head>
61
+ <body>
62
+ <nav>
63
+ <a href="{% url 'index' %}">Home</a> |
64
+ <a href="{% url 'upload_schedule' %}">Upload Seating Plan</a> |
65
+ <a href="{% url 'view_schedule' %}">View Schedule</a> |
66
+ <a href="{% url 'logout_view' %}">Logout</a>
67
+ </nav>
68
+ {% if messages %}
69
+ {% for message in messages %}
70
+ <p>{{ message }}</p>
71
+ {% endfor %}
72
+ {% endif %}
73
+ <form action="{% url 'upload_schedule' %}" method="post" enctype="multipart/form-data">
74
+ {% csrf_token %}
75
+ <label for="schedule_file">Schedule File:</label>
76
+ <input type="file" name="schedule_file" id="schedule_file" accept=".xlsx"><br>
77
+ <input type="submit" value="Upload Seating Plan">
78
+ </form>
79
+
80
+ </body>
81
+ </html>
templates/view_schedule.html ADDED
@@ -0,0 +1,124 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>View Schedule</title>
7
+ <style>
8
+ body {
9
+ font-family: Arial, sans-serif;
10
+ background-color: #f2f2f2;
11
+ margin: 0;
12
+ padding: 20px;
13
+ }
14
+
15
+ h1 {
16
+ text-align: center;
17
+ }
18
+
19
+ form {
20
+ text-align: center;
21
+ margin-bottom: 20px;
22
+ }
23
+
24
+ label {
25
+ display: block;
26
+ margin-bottom: 5px;
27
+ font-weight: bold;
28
+ }
29
+
30
+ input[type="text"],
31
+ input[type="date"] {
32
+ padding: 8px;
33
+ border: 1px solid #ccc;
34
+ border-radius: 4px;
35
+ }
36
+
37
+ input[type="submit"] {
38
+ padding: 8px 16px;
39
+ background-color: #4CAF50;
40
+ color: white;
41
+ border: none;
42
+ border-radius: 4px;
43
+ cursor: pointer;
44
+ }
45
+ table {
46
+ border-collapse: collapse;
47
+ width: 100%;
48
+ }
49
+
50
+ th, td {
51
+ border: 1px solid #ddd;
52
+ padding: 8px;
53
+ text-align: left;
54
+ }
55
+
56
+ th {
57
+ background-color: #f2f2f2;
58
+ }
59
+ nav {
60
+ text-align: center;
61
+ margin-bottom: 20px;
62
+ }
63
+ nav a {
64
+ margin: 0 15px;
65
+ text-decoration: none;
66
+ color: #333;
67
+ }
68
+ nav a:hover {
69
+ text-decoration: underline;
70
+ }
71
+ body {
72
+ font-family: Arial, sans-serif;
73
+ background-color: #f2f2f2;
74
+ margin: 0;
75
+ padding: 20px;
76
+ }
77
+ </style>
78
+ </head>
79
+ <body>
80
+
81
+
82
+ <form action="{% url 'view_schedule' %}" method="post">
83
+ {% csrf_token %}
84
+ <label for="student_id">Student ID:</label>
85
+ <input type="text" name="student_id" id="student_id" required><br><br>
86
+ <label for="exam_date">Exam Date:</label>
87
+ <input type="date" name="exam_date" id="exam_date" required><br><br>
88
+ <input type="submit" value="Search">
89
+ </form>
90
+ {% if schedules %}
91
+ <table>
92
+ <tr>
93
+ <th>Student Name</th>
94
+ <th>Student ID</th>
95
+ <th>Exam Date</th>
96
+ <th>Room Number</th>
97
+ <th>Subject Name</th>
98
+ <th>Subject Code</th>
99
+ <th>Seat Number</th>
100
+ </tr>
101
+ {% for schedule in schedules %}
102
+ <tr>
103
+ <td>{{schedule.student_name}}</td>
104
+ <td>{{schedule.student_id}}</td>
105
+ <td>{{schedule.exam_date}}</td>
106
+ <td>{{schedule.room_number}}</td>
107
+ <td>{{schedule.subject_name}}</td>
108
+ <td>{{schedule.subject_code}}</td>
109
+ <td>{{schedule.seat_Number}}</td>
110
+ </tr>
111
+ {% endfor %}
112
+ </table>
113
+
114
+ {% endif %}
115
+
116
+ {% if message %}
117
+ <p>{{ message }}</p>
118
+ {% endif %}
119
+
120
+
121
+
122
+
123
+ </body>
124
+ </html>