diff --git a/Dockerfile b/Dockerfile
index 5eb2890..0fbee5b 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -2,7 +2,7 @@
FROM python:3.8.16
# Install cron
-RUN apt-get update && apt-get install -y cron
+# RUN apt-get update && apt-get install -y cron
# Keeps Python from generating .pyc files in the container
ENV PYTHONDONTWRITEBYTECODE=1
@@ -17,14 +17,14 @@ RUN python -m pip install -r requirements.txt
WORKDIR /app
COPY . /app
-# Copy cron job file
-COPY cronjob /etc/cron.d/cronjob
+# # Copy cron job file
+# COPY cronjob /etc/cron.d/cronjob
-# Give execution rights on the cron job
-RUN chmod 0644 /etc/cron.d/cronjob
+# # Give execution rights on the cron job
+# RUN chmod 0644 /etc/cron.d/cronjob
-# Create the log file to be able to run tail
-RUN touch /var/log/cron.log
+# # Create the log file to be able to run tail
+# RUN touch /var/log/cron.log
# Creates a non-root user with an explicit UID and adds permission to access the /app folder
# For more info, please refer to https://aka.ms/vscode-docker-python-configure-containers
diff --git a/apps/global_settings/__init__.py b/apps/global_settings/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/apps/global_settings/admin.py b/apps/global_settings/admin.py
new file mode 100644
index 0000000..d976426
--- /dev/null
+++ b/apps/global_settings/admin.py
@@ -0,0 +1,5 @@
+from django.contrib import admin
+from .models import Settings
+
+# Register your models here.
+admin.site.register(Settings)
\ No newline at end of file
diff --git a/apps/global_settings/apps.py b/apps/global_settings/apps.py
new file mode 100644
index 0000000..033229a
--- /dev/null
+++ b/apps/global_settings/apps.py
@@ -0,0 +1,6 @@
+from django.apps import AppConfig
+
+
+class GlobalSettingsConfig(AppConfig):
+ default_auto_field = 'django.db.models.BigAutoField'
+ name = 'apps.global_settings'
diff --git a/apps/global_settings/migrations/0001_initial.py b/apps/global_settings/migrations/0001_initial.py
new file mode 100644
index 0000000..c532022
--- /dev/null
+++ b/apps/global_settings/migrations/0001_initial.py
@@ -0,0 +1,25 @@
+# Generated by Django 4.2.7 on 2023-12-11 22:17
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ initial = True
+
+ dependencies = [
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='Settings',
+ fields=[
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('enable_hostel_timers', models.BooleanField(default=False)),
+ ('frontend_checkin_timer', models.IntegerField(blank=True, default=0, null=True)),
+ ('backend_checkin_timer', models.IntegerField(blank=True, default=0, null=True)),
+ ('last_entry_without_hostel_checkout', models.TimeField(blank=True, null=True)),
+ ('valid_entry_without_hostel_checkout', models.TimeField(blank=True, null=True)),
+ ],
+ ),
+ ]
diff --git a/apps/global_settings/migrations/0002_settings_enable_gender_ratio_settings_female_ratio_and_more.py b/apps/global_settings/migrations/0002_settings_enable_gender_ratio_settings_female_ratio_and_more.py
new file mode 100644
index 0000000..0075e02
--- /dev/null
+++ b/apps/global_settings/migrations/0002_settings_enable_gender_ratio_settings_female_ratio_and_more.py
@@ -0,0 +1,28 @@
+# Generated by Django 4.2.7 on 2023-12-11 22:38
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('global_settings', '0001_initial'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='settings',
+ name='enable_gender_ratio',
+ field=models.BooleanField(default=False),
+ ),
+ migrations.AddField(
+ model_name='settings',
+ name='female_ratio',
+ field=models.FloatField(blank=True, default=0.5, null=True),
+ ),
+ migrations.AddField(
+ model_name='settings',
+ name='male_ratio',
+ field=models.FloatField(blank=True, default=0.5, null=True),
+ ),
+ ]
diff --git a/apps/global_settings/migrations/0003_alter_settings_options_settings_max_violation_count.py b/apps/global_settings/migrations/0003_alter_settings_options_settings_max_violation_count.py
new file mode 100644
index 0000000..779c64a
--- /dev/null
+++ b/apps/global_settings/migrations/0003_alter_settings_options_settings_max_violation_count.py
@@ -0,0 +1,22 @@
+# Generated by Django 4.2.7 on 2023-12-13 01:37
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('global_settings', '0002_settings_enable_gender_ratio_settings_female_ratio_and_more'),
+ ]
+
+ operations = [
+ migrations.AlterModelOptions(
+ name='settings',
+ options={'verbose_name_plural': 'Settings'},
+ ),
+ migrations.AddField(
+ model_name='settings',
+ name='max_violation_count',
+ field=models.IntegerField(blank=True, default=3, null=True),
+ ),
+ ]
diff --git a/apps/global_settings/migrations/__init__.py b/apps/global_settings/migrations/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/apps/global_settings/models.py b/apps/global_settings/models.py
new file mode 100644
index 0000000..83780ab
--- /dev/null
+++ b/apps/global_settings/models.py
@@ -0,0 +1,19 @@
+from django.db import models
+
+class Settings(models.Model):
+
+ enable_hostel_timers = models.BooleanField(default=False)
+ frontend_checkin_timer = models.IntegerField(blank=True, null=True, default=0)
+ backend_checkin_timer = models.IntegerField(blank=True, null=True, default=0)
+
+ last_entry_without_hostel_checkout = models.TimeField(blank=True, null=True)
+ valid_entry_without_hostel_checkout = models.TimeField(blank=True, null=True)
+
+ enable_gender_ratio = models.BooleanField(default=False)
+ male_ratio = models.FloatField(blank=True, null=True, default=0.5)
+ female_ratio = models.FloatField(blank=True, null=True, default=0.5)
+
+ max_violation_count = models.IntegerField(blank=True, null=True, default=3)
+
+ class Meta:
+ verbose_name_plural = 'Settings'
\ No newline at end of file
diff --git a/apps/global_settings/tests.py b/apps/global_settings/tests.py
new file mode 100644
index 0000000..7ce503c
--- /dev/null
+++ b/apps/global_settings/tests.py
@@ -0,0 +1,3 @@
+from django.test import TestCase
+
+# Create your tests here.
diff --git a/apps/global_settings/views.py b/apps/global_settings/views.py
new file mode 100644
index 0000000..91ea44a
--- /dev/null
+++ b/apps/global_settings/views.py
@@ -0,0 +1,3 @@
+from django.shortcuts import render
+
+# Create your views here.
diff --git a/apps/nightpass/migrations/0003_hostel_backend_checkin_timer_and_more.py b/apps/nightpass/migrations/0003_hostel_backend_checkin_timer_and_more.py
new file mode 100644
index 0000000..98d6cba
--- /dev/null
+++ b/apps/nightpass/migrations/0003_hostel_backend_checkin_timer_and_more.py
@@ -0,0 +1,23 @@
+# Generated by Django 4.2.7 on 2023-12-11 22:17
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('nightpass', '0002_alter_campusresource_slots_booked'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='hostel',
+ name='backend_checkin_timer',
+ field=models.IntegerField(blank=True, default=0, null=True),
+ ),
+ migrations.AddField(
+ model_name='hostel',
+ name='frontend_checkin_timer',
+ field=models.IntegerField(blank=True, default=0, null=True),
+ ),
+ ]
diff --git a/apps/nightpass/models.py b/apps/nightpass/models.py
index ed42976..09e091a 100644
--- a/apps/nightpass/models.py
+++ b/apps/nightpass/models.py
@@ -10,6 +10,9 @@ class Hostel(models.Model):
contact_number = models.CharField(max_length=15)
email = models.EmailField(max_length=100)
+ frontend_checkin_timer = models.IntegerField(blank=True, null=True, default=0)
+ backend_checkin_timer = models.IntegerField(blank=True, null=True, default=0)
+
def __str__(self):
return self.name
diff --git a/apps/nightpass/static/serviceworker.js b/apps/nightpass/static/serviceworker.js
new file mode 100644
index 0000000..e1b407d
--- /dev/null
+++ b/apps/nightpass/static/serviceworker.js
@@ -0,0 +1,26 @@
+var staticCacheName = 'thapar-nightpass';
+
+self.addEventListener('install', function(event) {
+event.waitUntil(
+ caches.open(staticCacheName).then(function(cache) {
+ return cache.addAll([
+ '',
+ ]);
+ })
+);
+});
+
+self.addEventListener('fetch', function(event) {
+var requestUrl = new URL(event.request.url);
+ if (requestUrl.origin === location.origin) {
+ if ((requestUrl.pathname === '/')) {
+ event.respondWith(caches.match(''));
+ return;
+ }
+ }
+ event.respondWith(
+ caches.match(event.request).then(function(response) {
+ return response || fetch(event.request);
+ })
+ );
+});
diff --git a/apps/nightpass/static/styles.css b/apps/nightpass/static/styles.css
index f2ea7b0..8de3a70 100644
--- a/apps/nightpass/static/styles.css
+++ b/apps/nightpass/static/styles.css
@@ -235,7 +235,61 @@ select {
color: #fff;
}
+footer {
+ background-color: #696b6d;
+ color: #fff;
+ text-align: center;
+ padding: 10px;
+ position: fixed;
+ bottom: 0;
+ width: 100%;
+}
+
+.custom-table {
+ width: 100%;
+ border-collapse: collapse;
+}
+.custom-table th,
+.custom-table td {
+ border: 1px solid #ddd;
+ padding: 8px;
+ text-align: left;
+}
+
+.custom-table th {
+ background-color: #eb1b23;
+ color: #fff;
+ font-weight: bold;
+}
+
+.red {
+ background-color: #d92550;
+}
+
+.green {
+ background-color: #3ac47d;
+}
+
+.yellow {
+ background-color: #f7b924;
+}
+
+.badge {
+ color: white;
+ padding: 5px 10px;
+ border-radius: 5px;
+}
+
+.table-container {
+ max-width: 800px; /* Adjust the maximum width as needed */
+ margin: 10px auto; /* Center the container horizontally */
+ padding: 20px; /* Add some padding for spacing */
+ overflow-x: auto; /* Add horizontal scrolling for small screens */
+ background-color: #fff;
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
+
+}
@media (max-width: 768px) {
.profile-card, .resource-card {
@@ -246,7 +300,7 @@ select {
flex-wrap: wrap;
justify-content: center;
align-items: center;
-}
+ }
.activity-table {
font-size: 14px;
diff --git a/apps/nightpass/static/thapar_logo.png b/apps/nightpass/static/thapar_logo.png
new file mode 100644
index 0000000..49b1550
Binary files /dev/null and b/apps/nightpass/static/thapar_logo.png differ
diff --git a/apps/nightpass/templates/base.html b/apps/nightpass/templates/base.html
new file mode 100644
index 0000000..9eda960
--- /dev/null
+++ b/apps/nightpass/templates/base.html
@@ -0,0 +1,55 @@
+
+
+
+
+
+ Thapar NightPass
+
+
+
+
+
+
+
+
+
+ {% block content %}
+ {% endblock %}
+
+
+
+
+
+
+{% if messages %}
+{% for message in messages %}
+
+{% endfor %}
+{% endif %}
+
+
diff --git a/apps/nightpass/templates/caretaker.html b/apps/nightpass/templates/caretaker.html
new file mode 100644
index 0000000..858439f
--- /dev/null
+++ b/apps/nightpass/templates/caretaker.html
@@ -0,0 +1,41 @@
+{% extends 'base.html' %}
+
+{% block content %}
+
+
+
+
+
+ Name |
+ Room No |
+ Status |
+ Action |
+
+
+
+ {% for pass in hostel_passes %}
+
+ {{ pass.user.student.name }} |
+ {{ pass.user.student.room_number }} |
+ {% if pass.user.student.is_checked_in %}
+ Checked In |
+ {% else %}
+ {% if pass %}
+ {% if pass.check_in and not pass.check_out %}
+ {{ pass.campus_resource }} |
+ {% else %}
+ Outside |
+ {% endif %}
+ {% else %}
+ Outside |
+ {% endif %}
+ {% endif %}
+ |
+
+ {% endfor %}
+
+
+
+
+
+{% endblock %}
diff --git a/apps/nightpass/templates/lmao.html b/apps/nightpass/templates/lmao.html
index f189660..608f9e9 100644
--- a/apps/nightpass/templates/lmao.html
+++ b/apps/nightpass/templates/lmao.html
@@ -1,21 +1,5 @@
-
-
-
-
-
- Thapar NightPass
-
-
-
-
-
-
-
-
+{% extends 'base.html' %}
+{% block content %}
@@ -48,7 +32,7 @@
Thapar NightPass
-
-{% if messages %}
-{% for message in messages %}
-
-{% endfor %}
-{% endif %}
-
-
+
+ {% endblock %}
\ No newline at end of file
diff --git a/apps/nightpass/urls.py b/apps/nightpass/urls.py
index b20f329..2ec9444 100644
--- a/apps/nightpass/urls.py
+++ b/apps/nightpass/urls.py
@@ -4,4 +4,6 @@
urlpatterns = [
path('', views.campus_resources_home),
path('book/
', views.generate_pass),
- path('cancel/', views.cancel_pass),]
+ path('cancel/', views.cancel_pass),
+ path('hostel/', views.hostel_home),
+ ]
diff --git a/apps/nightpass/views.py b/apps/nightpass/views.py
index 45d7b21..cfef5b7 100644
--- a/apps/nightpass/views.py
+++ b/apps/nightpass/views.py
@@ -5,6 +5,7 @@
from django.utils import timezone
from .models import *
from ..users.models import *
+from ..global_settings.models import Settings as settings
import random
import string
import json
@@ -16,14 +17,22 @@
+
@login_required
def campus_resources_home(request):
+ Settings = settings.objects.first()
campus_resources = CampusResource.objects.filter(is_display=True)
user = request.user
if user.user_type == 'student':
- user_pass = NightPass.objects.filter(user=user, check_out=False).first()
+ user_pass = NightPass.objects.filter(user=user, valid=True).first()
user_incidents = NightPass.objects.filter(user=user, defaulter=True)
- return render(request, 'lmao.html', {'user':user.student,'campus_resources':campus_resources, 'user_pass':user_pass, 'user_incidents':user_incidents})
+
+ if Settings.enable_hostel_timers:
+ checkin_timer = user.student.hostel.frontend_checkin_timer
+ else:
+ checkin_timer = Settings.frontend_checkin_timer
+
+ return render(request, 'lmao.html', {'user':user.student,'campus_resources':campus_resources, 'user_pass':user_pass, 'user_incidents':user_incidents, 'frontend_checkin_timer':checkin_timer})
elif user.user_type == 'security':
return redirect('/access')
elif user.user_type == 'admin':
@@ -38,17 +47,46 @@ def generate_pass(request, campus_resource):
if (campus_resource.is_display == False) or not campus_resource.is_booking:
data = {
'status':False,
- 'message':'Booking is currently not available for this resource'
+ 'message':'Booking is currently not available for this resource.'
}
return HttpResponse(json.dumps(data))
if campus_resource.booking_complete:
data = {
'status':False,
- 'message':'No slots available!'
+ 'message':'All slots are booked for today!'
}
return HttpResponse(json.dumps(data))
+ Settings = settings.objects.first()
+ if Settings.enable_gender_ratio:
+ if user.student.gender == 'male':
+ male_pass_count = NightPass.objects.filter(valid=True, campus_resource=campus_resource,
+ user__student__gender='male').count()
+ if (male_pass_count > Settings.male_ratio*(campus_resource.max_capacity)) or Settings.male_ratio==float(0):
+ data = {
+ 'status':False,
+ 'message':'All slots are booked for today!'
+ }
+ return HttpResponse(json.dumps(data))
+
+ elif user.student.gender == 'female':
+ female_pass_count = NightPass.objects.filter(valid=True, campus_resource=campus_resource,
+ user__student__gender='female').count()
+ if (female_pass_count > Settings.female_ratio*(campus_resource.max_capacity)) or Settings.female_ratio==float(0) :
+ data = {
+ 'status':False,
+ 'message':'All slots are booked for today!'
+ }
+ return HttpResponse(json.dumps(data))
+
+ if int(user.student.violation_flags) >= int(Settings.max_violation_count):
+ data = {
+ 'status':False,
+ 'message':'Nightpass facility has been temporarily suspended! Contact DOSA office for further details.'
+ }
+ return HttpResponse(json.dumps(data))
+
user_pass = NightPass.objects.filter(user=user, date=date.today()).first()
if not user_pass:
campus_resource.refresh_from_db()
@@ -119,18 +157,13 @@ def cancel_pass(request):
}
return HttpResponse(json.dumps(data))
else:
- if user_nightpass.check_out and user_nightpass.check_in:
+ if user_nightpass.check_out or user_nightpass.check_in or user_nightpass.hostel_checkout_time:
data={
'status':False,
'message':f"Cannot cancel pass after utilization."
}
return HttpResponse(json.dumps(data))
- elif user_nightpass.check_in:
- data={
- 'status':False,
- 'message':f"Cannot cancel pass once you enter {user_nightpass.campus_resource}."
- }
- return HttpResponse(json.dumps(data))
+
else:
user_nightpass.delete()
user_nightpass.campus_resource.slots_booked -= 1
@@ -142,3 +175,12 @@ def cancel_pass(request):
'message':f"Pass cancelled successfully!"
}
return HttpResponse(json.dumps(data))
+
+
+def hostel_home(request):
+ user = request.user
+ if request.user.is_staff and user.user_type == 'security':
+ hostel_passes = NightPass.objects.filter(valid=True, user__student__hostel=request.user.security.hostel) | NightPass.objects.filter(date=date.today(), user__student__hostel=request.user.security.hostel).order_by('check_out')
+ return render(request, 'caretaker.html', {'hostel_passes':hostel_passes})
+ else:
+ return redirect('/access')
\ No newline at end of file
diff --git a/apps/users/admin.py b/apps/users/admin.py
index a2da4bc..b0b378f 100644
--- a/apps/users/admin.py
+++ b/apps/users/admin.py
@@ -3,7 +3,7 @@
from django.http import HttpResponse
from django.utils import timezone
from django.utils.html import format_html
-from rangefilter.filter import DateRangeFilter
+from rangefilter.filters import DateRangeFilter
from tablib import Dataset
from .models import *
from import_export.admin import ImportExportModelAdmin
@@ -15,14 +15,14 @@
class NightPassAdmin(admin.ModelAdmin):
- list_display = ( 'user','hostel','date', 'campus_resource','hostel_check_out', 'check_in', 'check_out', 'hostel_check_in', 'defaulter')
+ list_display = ( 'name','user','hostel','date', 'campus_resource','hostel_check_out', 'check_in', 'check_out', 'hostel_check_in', 'defaulter')
search_fields = ('user__student__name','user__student__registration_number','user__student__email')
actions = ['export_as_xlsx']
- list_filter = (('date', DateRangeFilter),'campus_resource','user__student__hostel', 'defaulter', 'check_in', 'check_out')
+ list_filter = (('date', DateRangeFilter),'campus_resource','user__student__gender','user__student__hostel', 'defaulter', 'check_in', 'check_out')
autocomplete_fields = ('user', 'campus_resource')
readonly_fields = ('pass_id', 'check_in_time', 'check_out_time', 'hostel_checkout_time', 'hostel_checkin_time')
- def user(self, obj):
+ def name(self, obj):
return obj.user.student.name
def hostel(self, obj):
diff --git a/apps/users/management/commands/check_defaulters.py b/apps/users/management/commands/check_defaulters.py
index f2f4a97..557603c 100644
--- a/apps/users/management/commands/check_defaulters.py
+++ b/apps/users/management/commands/check_defaulters.py
@@ -1,10 +1,13 @@
from django.core.management.base import BaseCommand
-from ...models import NightPass, Student
+from ...models import NightPass
+from ....global_settings.models import Settings as settings
from datetime import date, timedelta, datetime, time
from django.utils import timezone
+
def check_defaulters():
+ Settings = settings.objects.first()
previous_day_nightpasses = NightPass.objects.filter(date=date.today()-timedelta(days=1))
previous_day_nightpasses.update(defaulter=False, defaulter_remarks='')
for nightpass in previous_day_nightpasses:
@@ -15,15 +18,23 @@ def check_defaulters():
defaulter = True
remarks+= f"Did not visit {nightpass.campus_resource.name}"
else:
- start_default_time = timezone.make_aware(datetime.combine(nightpass.check_in_time.date(), time(20,45)), timezone.get_current_timezone())
- end_default_time = timezone.make_aware(datetime.combine(nightpass.check_in_time.date(), time(21,00)), timezone.get_current_timezone())
+ # start_default_time = timezone.make_aware(datetime.combine(nightpass.check_in_time.date(), time(20,45)), timezone.get_current_timezone())
+ # end_default_time = timezone.make_aware(datetime.combine(nightpass.check_in_time.date(), time(21,00)), timezone.get_current_timezone())
+ start_default_time = Settings.valid_entry_without_hostel_checkout
+ end_default_time = Settings.last_entry_without_hostel_checkout
+
+ if Settings.enable_hostel_timers:
+ checkin_timer = nightpass.user.student.hostel.backend_checkin_timer
+ else:
+ checkin_timer = Settings.backend_checkin_timer
+
if nightpass.hostel_checkout_time:
- if (nightpass.check_in_time - nightpass.hostel_checkout_time) > timedelta(minutes=30):
- if (nightpass.check_in_time > start_default_time):
+ if (nightpass.check_in_time - nightpass.hostel_checkout_time) > timedelta(minutes=checkin_timer):
+ if (nightpass.check_in_time.time() > start_default_time):
defaulter = True
remarks+= f"Late check in at {nightpass.campus_resource.name}"
else:
- if (nightpass.check_in_time > end_default_time):
+ if (nightpass.check_in_time.time() > end_default_time):
defaulter = True
remarks+= f"Late check in at {nightpass.campus_resource.name}"
if not nightpass.check_out_time:
@@ -33,7 +44,7 @@ def check_defaulters():
if not nightpass.hostel_checkin_time:
defaulter = True
remarks+= "Late check in at hostel"
- elif (nightpass.hostel_checkin_time - nightpass.check_out_time) > timedelta(minutes=30):
+ elif (nightpass.hostel_checkin_time - nightpass.check_out_time) > timedelta(minutes=checkin_timer):
defaulter = True
remarks+= "Late check in at hostel"
if (nightpass.check_out_time-nightpass.check_in_time) < timedelta(minutes=10):
diff --git a/apps/users/migrations/0009_alter_nightpass_options_alter_security_options.py b/apps/users/migrations/0009_alter_nightpass_options_alter_security_options.py
new file mode 100644
index 0000000..cb145df
--- /dev/null
+++ b/apps/users/migrations/0009_alter_nightpass_options_alter_security_options.py
@@ -0,0 +1,21 @@
+# Generated by Django 4.2.7 on 2023-12-13 01:37
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('users', '0008_student_gender_alter_nightpass_valid'),
+ ]
+
+ operations = [
+ migrations.AlterModelOptions(
+ name='nightpass',
+ options={'verbose_name_plural': 'Night Passes'},
+ ),
+ migrations.AlterModelOptions(
+ name='security',
+ options={'verbose_name_plural': 'Security'},
+ ),
+ ]
diff --git a/apps/users/models.py b/apps/users/models.py
index 9841238..2da1360 100644
--- a/apps/users/models.py
+++ b/apps/users/models.py
@@ -123,6 +123,9 @@ class Security(models.Model):
def __str__(self):
return self.name
+ class Meta:
+ verbose_name_plural = 'Security'
+
class NightPass(models.Model):
user = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
@@ -142,4 +145,7 @@ class NightPass(models.Model):
defaulter_remarks = models.TextField(blank=True, null=True)
def __str__(self):
- return self.pass_id
\ No newline at end of file
+ return self.pass_id
+
+ class Meta:
+ verbose_name_plural = 'Night Passes'
\ No newline at end of file
diff --git a/apps/users/static/login.css b/apps/users/static/login.css
index d7a23f7..f2ea7b0 100644
--- a/apps/users/static/login.css
+++ b/apps/users/static/login.css
@@ -1,98 +1,258 @@
body {
- background-color: #940d0d;
font-family: Arial, sans-serif;
+ background-color: #f5f5f5;
margin: 0;
+ padding: 0;
+ line-height: 1.6;
+}
+
+.header {
display: flex;
- justify-content: center;
align-items: center;
- height: 100vh;
+ justify-content: space-between;
+ background-color: #eb1b23;
+ color: #fff;
+ text-align: center;
+ padding: 2px; /* Reduced header padding */
+}
+
+.header-logo {
+ width: 100px; /* Set the width of your college logo */
+ height: auto; /* Maintain aspect ratio */
+}
+
+.logout-button {
+ background-color: #fff;
+ color: #eb1b23;
+ border: none;
+ padding: 5px 10px;
+ border-radius: 5px;
+ cursor: pointer;
+ transition: background-color 0.2s;
+ margin-right: 20px;
+}
+
+.logout-button:hover {
+ background-color: #ccc;
}
.container {
- background-color: white;
- max-width: 300px;
- padding: 20px;
- width: 45%;
- border-radius: 8px;
- box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
+ display: flex;
+ flex-wrap: nowrap;
+ justify-content: center;
+ align-items: center;
}
-.logo {
+
+.profile-card, .resource-card {
+ background-color: #fff;
+ border-radius: 10px;
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
+ margin: 20px;
+ width: 300px;
text-align: center;
+ padding: 20px;
+ transition: transform 0.2s;
}
-.logo img {
- height: 100px;
+.profile-card:hover, .resource-card:hover {
+ transform: scale(1.05);
}
-.login-box {
- text-align: center;
+.profile-img {
+ width: 100px;
+ border-radius: 50%;
+ object-fit: cover;
+ margin-bottom: 20px;
}
-.login-box h2 {
+.qr-code {
+ width: 100px;
+ height: 100px;
margin-bottom: 20px;
}
-.container form {
- display: flex;
- flex-direction: column;
+.profile-details {
+ font-size: 18px;
+ margin-bottom: 20px;
+ text-align: left;
}
-.container input {
- padding: 10px;
+.profile-details p {
margin-bottom: 10px;
- border: 1px solid #ccc;
- border-radius: 4px;
}
-.container button {
- padding: 10px;
- background-color: #940d0d;
- color: white;
+.safe {
+ color: green;
+}
+
+.unsafe {
+ color: red;
+}
+
+.book-button {
+ background-color: #eb1b23;
+ color: #fff;
border: none;
- border-radius: 4px;
+ padding: 10px 20px;
+ border-radius: 10px;
cursor: pointer;
+ transition: background-color 0.2s;
+}
+
+.booked {
+ background-color: grey;
}
-.container button:hover {
- background-color: #ed0505;
+.book-button:hover {
+ background-color: #ff3336;
}
-/* "activate-account" link */
-.activate-account {
+.modal {
+ display: none;
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background-color: rgba(0, 0, 0, 0.5);
+}
+
+.modal-content {
+ background-color: #fff;
+ width: 80%;
+ max-width: 400px;
+ margin: 20% auto;
+ padding: 20px;
+ border-radius: 5px;
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
text-align: center;
- margin-top: 15px;
+ position: relative;
+ display: flex;
}
-.activate-account a {
- color: #7240ce;
- text-decoration: none;
+.modal-image {
+ flex: 1;
}
-.activate-account a:hover {
- text-decoration: underline;
+.modal-image img {
+ width: 100%;
+ height: auto;
+ object-fit: cover;
}
-/* Animation */
-.container {
- animation: fadeIn 1s ease;
+.modal-details {
+ flex: 2;
+ padding: 10px;
+ text-align: left;
}
-@keyframes fadeIn {
- from {
- opacity: 0;
- transform: translateY(-20px);
- }
- to {
- opacity: 1;
- transform: translateY(0);
- }
+/* Close button for the modal */
+.close {
+ position: absolute;
+ top: 10px;
+ right: 10px;
+ cursor: pointer;
}
-/* Responsive styles */
-@media screen and (max-width: 500px) {
+.modal-button {
+ background-color: #eb1b23;
+ color: #fff;
+ border: none;
+ padding: 10px 20px;
+ border-radius: 10px;
+ cursor: pointer;
+ transition: background-color 0.2s;
+}
+
+.modal-button:hover {
+ background-color: #ff3336;
+}
+
+select {
+ width: 100%;
+ padding: 10px;
+ margin: 10px 0;
+}
+.resource-card {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: space-between;
+}
+.resource-card img {
+ width: 100px;
+ height: 100px;
+ object-fit: cover;
+ border-radius: 5px;
+ margin-right: 5px;
+}
+.resource-details {
+ flex: 1;
+ padding: 10px;
+ text-align: left;
+}
+.resource-details h2 {
+ margin: 0;
+}
+.resource-details p {
+ margin: 0;
+}
+.location-cards
+{
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: center;
+ align-items: center;
+}
+
+.table-section {
+ margin-top: 10px;
+ overflow-x: auto;
+ /* Add horizontal scroll for small screens */
+}
+
+.activity-table {
+ width: 100%;
+ max-width: 100%; /* Set a maximum width for the table */
+ border-collapse: collapse;
+ margin-top: 20px;
+ white-space: nowrap; /* Prevent text wrapping */
+ overflow: hidden;
+}
+
+.activity-table th, .activity-table td {
+ border: 1px solid #ddd;
+ padding: 8px;
+ text-align: left;
+ overflow: hidden;
+ text-overflow: ellipsis; /* Display ellipsis for overflowed text */
+ white-space: wrap;
+}
+
+.activity-table th {
+ background-color: #eb1b23;
+ color: #fff;
+}
+
+
+
+@media (max-width: 768px) {
+ .profile-card, .resource-card {
+ width: 90%;
+ }
.container {
- max-width: 90%;
- width: 70%;
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: center;
+ align-items: center;
+}
+
+ .activity-table {
+ font-size: 14px;
+ }
+
+ .activity-table th, .activity-table td {
+ padding: 5px;
}
}
diff --git a/apps/users/templates/index.html b/apps/users/templates/index.html
new file mode 100644
index 0000000..50545fd
--- /dev/null
+++ b/apps/users/templates/index.html
@@ -0,0 +1,114 @@
+
+
+
+
+
+ Thapar NightPass
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Welcome to the Night Pass Management System
+ Book your night pass to access campus resources and facilities. Enjoy a seamless experience!
+
+
+
+
+
+
+{% if messages %}
+{% for message in messages %}
+
+{% endfor %}
+{% endif %}
+
+
diff --git a/apps/users/templates/login.html b/apps/users/templates/login.html
deleted file mode 100644
index 2b63741..0000000
--- a/apps/users/templates/login.html
+++ /dev/null
@@ -1,192 +0,0 @@
-
-
-
-
-
-
- Login | Thapar NightPass
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {% if messages %}
- {% for message in messages %}
-
- {% endfor %}
- {% endif %}
-
-
-
-
\ No newline at end of file
diff --git a/apps/users/views.py b/apps/users/views.py
index 614ca3e..f97b7cc 100644
--- a/apps/users/views.py
+++ b/apps/users/views.py
@@ -108,7 +108,7 @@ def login_user(request):
if request.method == 'POST':
return gauth(request)
else:
- return render(request=request, template_name='login.html')
+ return render(request=request, template_name='index.html')
else:
return redirect('/')
diff --git a/apps/validation/static/info.js b/apps/validation/static/info.js
index ea05514..7e75d2c 100644
--- a/apps/validation/static/info.js
+++ b/apps/validation/static/info.js
@@ -70,45 +70,62 @@ function resetPass() {
}
+var timeouts = [];
function updateProfile(data) {
+ // document.querySelector('.profile-card').innerHTML=`
+ //
+ //
+ //
+ //
Registration Number:
+ //
Hostel:
+ //
Room Number:
+ //
In Hostel:
+ //
Hostel Check-in Time:
+ //
Hostel Check-out Time:
+ //
Last Check-out Time:
+ //
Has Booked:
+ //
`;
+
document.querySelector('.profile-card').innerHTML=`
-
-
-
-
Registration Number:
-
Hostel:
-
Room Number:
-
In Hostel:
-
Hostel Check-in Time:
-
Hostel Check-out Time:
-
Last Check-out Time:
-
Has Booked:
-
`;
- const profileDetails = document.querySelector('.profile-details');
+
+ Roll Number:
+
+`;
+
+
+ const profileDetails = document.querySelector('.profile-card');
const nameElement = profileDetails.querySelector('h2');
- const regNumberElement = profileDetails.querySelector('p:nth-child(2)');
- const hostelElement = profileDetails.querySelector('p:nth-child(3)');
- const roomNumberElement = profileDetails.querySelector('p:nth-child(4)');
- const inHostelElement = profileDetails.querySelector('p:nth-child(5)');
- const hostelCheckinElement = profileDetails.querySelector('p:nth-child(6)');
- const hostelCheckoutElement = profileDetails.querySelector('p:nth-child(7)');
- const lastCheckoutElement = profileDetails.querySelector('p:nth-child(8)');
- const hasBookedElement = profileDetails.querySelector('p:nth-child(9)');
- const options = { hour: '2-digit', minute: '2-digit', hour12: true, month: 'long', day: '2-digit' };
- const checkinTime = new Date(data.hostel_checkin_time).toLocaleString('en-US', options);
- const checkoutTime = new Date(data.hostel_checkout_time).toLocaleString('en-US', options);
- const lastCheckoutTime = new Date(data.last_checkout_time).toLocaleString('en-US', options);
+ const regNumberElement = profileDetails.querySelector('p');
+
+
+ // const profileDetails = document.querySelector('.profile-details');
+ // const nameElement = profileDetails.querySelector('h2');
+ // const regNumberElement = profileDetails.querySelector('p:nth-child(2)');
+ // const hostelElement = profileDetails.querySelector('p:nth-child(3)');
+ // const roomNumberElement = profileDetails.querySelector('p:nth-child(4)');
+ // const inHostelElement = profileDetails.querySelector('p:nth-child(5)');
+ // const hostelCheckinElement = profileDetails.querySelector('p:nth-child(6)');
+ // const hostelCheckoutElement = profileDetails.querySelector('p:nth-child(7)');
+ // const lastCheckoutElement = profileDetails.querySelector('p:nth-child(8)');
+ // const hasBookedElement = profileDetails.querySelector('p:nth-child(9)');
+ // const options = { hour: '2-digit', minute: '2-digit', hour12: true, month: 'long', day: '2-digit' };
+ // const checkinTime = new Date(data.hostel_checkin_time).toLocaleString('en-US', options);
+ // const checkoutTime = new Date(data.hostel_checkout_time).toLocaleString('en-US', options);
+ // const lastCheckoutTime = new Date(data.last_checkout_time).toLocaleString('en-US', options);
document.getElementById('user_picture').src = data.picture;
nameElement.textContent = data.name;
regNumberElement.textContent = `Registration Number: ${data.registration_number}`;
- hostelElement.textContent = `Hostel: ${data.hostel}`;
- roomNumberElement.textContent = `Room Number: ${data.room_number}`;
- inHostelElement.textContent = `In Hostel: ${data.is_checked_in ? 'Yes' : 'No'}`;
- hostelCheckinElement.textContent = `Hostel Check-in Time: ${checkinTime}`;
- hostelCheckoutElement.textContent = `Hostel Check-out Time: ${checkoutTime}`;
- lastCheckoutElement.textContent = `Last Check-out Time: ${lastCheckoutTime}`;
- hasBookedElement.textContent = `Has Booked: ${data.has_booked ? 'Yes' : 'No'}`;
- setTimeout(function(){resetProfile();}, 10000);
+ // hostelElement.textContent = `Hostel: ${data.hostel}`;
+ // roomNumberElement.textContent = `Room Number: ${data.room_number}`;
+ // inHostelElement.textContent = `In Hostel: ${data.is_checked_in ? 'Yes' : 'No'}`;
+ // hostelCheckinElement.textContent = `Hostel Check-in Time: ${checkinTime}`;
+ // hostelCheckoutElement.textContent = `Hostel Check-out Time: ${checkoutTime}`;
+ // lastCheckoutElement.textContent = `Last Check-out Time: ${lastCheckoutTime}`;
+ // hasBookedElement.textContent = `Has Booked: ${data.has_booked ? 'Yes' : 'No'}`;
+ for (var i = 0; i < timeouts.length; i++) {
+ clearTimeout(timeouts[i]);
+ }
+ timeouts.push(setTimeout(function(){resetProfile();}, 7000));
}
// Function to update the user pass section with data
@@ -214,7 +231,10 @@ function checkIn(registration_number) {
if (res) {
if (response.student_stats) {updateStats(response.student_stats);}
toastr.success(response.message);
- setTimeout(function(){ resetProfile(); }, 5000);
+ for (var i = 0; i < timeouts.length; i++) {
+ clearTimeout(timeouts[i]);
+ }
+ timeouts.push(setTimeout(function(){ resetProfile(); }, 5000));
}
else {
@@ -241,7 +261,10 @@ function checkOut(registration_number) {
if (res) {
if (response.student_stats) {updateStats(response.student_stats);}
toastr.success(response.message);
- setTimeout(function(){ resetProfile(); }, 7000);
+ for (var i = 0; i < timeouts.length; i++) {
+ clearTimeout(timeouts[i]);
+ }
+ timeouts.push(setTimeout(function(){ resetProfile(); }, 7000));
}
else {
toastr.error(response.message);
@@ -272,4 +295,4 @@ function checkInput() {
setInterval(function () {
checkInput()
-}, 1000);
\ No newline at end of file
+}, 300);
\ No newline at end of file
diff --git a/apps/validation/templates/info.html b/apps/validation/templates/info.html
index 838fecc..5c01593 100644
--- a/apps/validation/templates/info.html
+++ b/apps/validation/templates/info.html
@@ -97,7 +97,7 @@
Booking: {{ total_count }}
var audio = new Audio('../static/beep.mp3');
audio.play();
if (code.length == 9) {fetch_data({'registration_number':code})}
- lastScannedBarcode.concat(code);
+ lastScannedBarcode.push(code);
}
});
@@ -109,7 +109,7 @@
Booking: {{ total_count }}
setInterval(function () {
lastScannedBarcode.pop();
- }, 10000);
+ }, 5000);