diff --git a/FusionIIIT/Fusion/context_processors.py b/FusionIIIT/Fusion/context_processors.py deleted file mode 100644 index 54566d56e..000000000 --- a/FusionIIIT/Fusion/context_processors.py +++ /dev/null @@ -1,5 +0,0 @@ -def global_vars(request): - return { - 'global_var': request.session.get('currentDesignationSelected', 'default_value'), - 'global_var2': request.session.get('allDesignations', 'default_value2'), - } \ No newline at end of file diff --git a/FusionIIIT/Fusion/middleware/custom_middleware.py b/FusionIIIT/Fusion/middleware/custom_middleware.py deleted file mode 100644 index 74f7d7d72..000000000 --- a/FusionIIIT/Fusion/middleware/custom_middleware.py +++ /dev/null @@ -1,62 +0,0 @@ -# custom_middleware.py -from django.contrib.auth.signals import user_logged_in -from django.dispatch import receiver -from applications.globals.models import (ExtraInfo, Feedback, HoldsDesignation, - Issue, IssueImage, DepartmentInfo,ModuleAccess) -from django.shortcuts import get_object_or_404, redirect, render - -def user_logged_in_middleware(get_response): - @receiver(user_logged_in) - def user_logged_in_handler(sender, user, request, **kwargs): - if 'function_executed' not in request.session: - # Run the function only if the flag is not set - # Assuming user is a model with the desired data field, retrieve the data - # For example, if your User model has a field named 'custom_field', you can access it like: - if user.is_authenticated: - desig = list(HoldsDesignation.objects.select_related('user','working','designation').all().filter(working = request.user).values_list('designation')) - print(desig) - b = [i for sub in desig for i in sub] - design = HoldsDesignation.objects.select_related('user','designation').filter(working=request.user) - - designation=[] - if str(user.extrainfo.user_type) == "student": - designation.append(str(user.extrainfo.user_type)) - - - for i in design: - if str(i.designation) != str(user.extrainfo.user_type): - print('-------') - print(i.designation) - print(user.extrainfo.user_type) - print('') - designation.append(str(i.designation)) - - for i in designation: - print(i) - - request.session['currentDesignationSelected'] = designation[0] - request.session['allDesignations'] = designation - first_designation = designation[0] - module_access = ModuleAccess.objects.filter(designation=first_designation).first() - - if module_access: - access_rights = {} - - field_names = [field.name for field in ModuleAccess._meta.get_fields() if field.name not in ['id', 'designation']] - - for field_name in field_names: - access_rights[field_name] = getattr(module_access, field_name) - - request.session['moduleAccessRights'] = access_rights - print("logged iN") - - # Set the flag in the session to indicate that the function has bee+n executed - request.session['function_executed'] = True - - def middleware(request): - if request.user.is_authenticated: - user_logged_in_handler(request.user, request.user, request) - response = get_response(request) - return response - - return middleware \ No newline at end of file diff --git a/FusionIIIT/Fusion/settings/common.py b/FusionIIIT/Fusion/settings/common.py index bc97f1548..b98ea6960 100644 --- a/FusionIIIT/Fusion/settings/common.py +++ b/FusionIIIT/Fusion/settings/common.py @@ -50,7 +50,7 @@ # email of sender -EMAIL_HOST_USER = 'fusion@iiitdmj.ac.in' +EMAIL_HOST_USER = 'fusionmailservice@iiitdmj.ac.in' EMAIL_PORT = 587 ACCOUNT_EMAIL_REQUIRED = True @@ -68,7 +68,7 @@ ACCOUNT_EMAIL_SUBJECT_PREFIX = 'Fusion: ' -DEFAULT_FROM_EMAIL = 'Fusion IIIT ' +DEFAULT_FROM_EMAIL = 'Fusion IIIT ' SERVER_EMAIL = 'fusionmailservice@iiitdmj.ac.in' @@ -104,14 +104,13 @@ 'django.contrib.staticfiles', 'django.contrib.sites', 'django.contrib.humanize', - 'django_crontab', + 'corsheaders', 'applications.eis', 'notification', 'notifications', 'applications.academic_procedures', - 'applications.examination', 'applications.academic_information', 'applications.leave', 'applications.library', @@ -128,7 +127,6 @@ 'applications.ps1', 'applications.programme_curriculum', 'applications.placement_cell', - 'applications.otheracademic', 'applications.recruitment', 'applications.scholarships', 'applications.visitor_hostel', @@ -165,7 +163,6 @@ 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', - 'Fusion.middleware.custom_middleware.user_logged_in_middleware', ] ROOT_URLCONF = 'Fusion.urls' @@ -173,7 +170,7 @@ TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [os.path.join(BASE_DIR, '..', 'templates'),], + 'DIRS': [os.path.join(BASE_DIR, '..', 'templates/'),], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ @@ -181,7 +178,6 @@ 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', - 'Fusion.context_processors.global_vars', ], }, }, @@ -258,9 +254,9 @@ # os.path.join(BASE_DIR, 'static/') STATIC_URL = '/static/' -STATIC_ROOT = os.path.join(BASE_DIR, '..', 'static') +STATIC_ROOT = os.path.join(BASE_DIR, '..', 'static/') STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage' -MEDIA_ROOT = os.path.join(BASE_DIR, '..', 'media') +MEDIA_ROOT = os.path.join(BASE_DIR, '..', 'media/') MEDIA_URL = '/media/' ACCOUNT_USERNAME_REQUIRED = False @@ -281,11 +277,5 @@ YOUTUBE_DATA_API_KEY = 'api_key' - CORS_ORIGIN_ALLOW_ALL = True ALLOW_PASS_RESET = True - -# session settings -SESSION_COOKIE_AGE = 15 * 60 -SESSION_EXPIRE_AT_BROWSER_CLOSE = True -SESSION_SAVE_EVERY_REQUEST = True \ No newline at end of file diff --git a/FusionIIIT/Fusion/settings/development.py b/FusionIIIT/Fusion/settings/development.py index 63587a11f..6acc214c1 100644 --- a/FusionIIIT/Fusion/settings/development.py +++ b/FusionIIIT/Fusion/settings/development.py @@ -1,7 +1,7 @@ from Fusion.settings.common import * DEBUG = True -TEMPLATE_DEBUG = True + SECRET_KEY = '=&w9due426k@l^ju1=s1)fj1rnpf0ok8xvjwx+62_nc-f12-8(' ALLOWED_HOSTS = ['*'] @@ -16,11 +16,9 @@ } } - REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework.authentication.TokenAuthentication', - 'rest_framework.authentication.SessionAuthentication', ), 'DEFAULT_PERMISSION_CLASSES': ( 'rest_framework.permissions.IsAuthenticated', @@ -55,13 +53,3 @@ DEBUG_TOOLBAR_CONFIG = { 'INTERCEPT_REDIRECTS': False, } - -CRONJOBS = [ - # the below job will update the bill at every minute can be used for testing - # ('* * * * *', 'applications.central_mess.tasks.generate_bill'), - - #the below job which we need to add in production server, to update the mess bill of student everyday at 10 pm in night - ('0 22 * * *', 'applications.central_mess.tasks.generate_bill'), -] - -CRONTAB_DJANGO_MANAGE_PATH = '/home/owlman/Desktop/Fuse/Fusion/FusionIIIT/manage.py' \ No newline at end of file diff --git a/FusionIIIT/Fusion/settings/production.py b/FusionIIIT/Fusion/settings/production.py index 5c0351a03..b2bbf01b5 100644 --- a/FusionIIIT/Fusion/settings/production.py +++ b/FusionIIIT/Fusion/settings/production.py @@ -28,4 +28,4 @@ 'DEFAULT_PERMISSION_CLASSES': ( 'rest_framework.permissions.IsAuthenticated', ) -} \ No newline at end of file +} diff --git a/FusionIIIT/Fusion/settings/test.py b/FusionIIIT/Fusion/settings/test.py new file mode 100644 index 000000000..d42e68263 --- /dev/null +++ b/FusionIIIT/Fusion/settings/test.py @@ -0,0 +1,50 @@ +from .common import * +import json + +from django.db.backends.signals import connection_created + + +SECRET_KEY = "test-secret-key" + +DATABASES = { + "default": { + "ENGINE": "django.db.backends.sqlite3", + "NAME": os.path.join(BASE_DIR, "test_db.sqlite3"), + "TEST": { + "SERIALIZE": False, + }, + } +} + +PASSWORD_HASHERS = [ + "django.contrib.auth.hashers.MD5PasswordHasher", +] + +EMAIL_BACKEND = "django.core.mail.backends.locmem.EmailBackend" +SILENCED_SYSTEM_CHECKS = [ + "fields.E180", + "fields.W122", + "fields.W342", +] + + +def _sqlite_json_valid(value): + if value is None: + return 0 + try: + json.loads(value) + return 1 + except (TypeError, ValueError): + return 0 + + +def _register_sqlite_json_functions(sender, connection, **kwargs): + if connection.vendor != "sqlite": + return + connection.connection.create_function("JSON_VALID", 1, _sqlite_json_valid) + + +connection_created.connect( + _register_sqlite_json_functions, + dispatch_uid="fusion_test_sqlite_json_valid", +) diff --git a/FusionIIIT/Fusion/urls.py b/FusionIIIT/Fusion/urls.py index e3b3f6792..cb1bdbf92 100755 --- a/FusionIIIT/Fusion/urls.py +++ b/FusionIIIT/Fusion/urls.py @@ -21,9 +21,6 @@ from django.conf.urls.static import static from django.contrib import admin from django.contrib.auth import views as auth_views -from django.urls import path - -from applications.globals.views import RateLimitedPasswordResetView urlpatterns = [ @@ -47,7 +44,7 @@ url(r'^complaint/', include('applications.complaint_system.urls')), url(r'^healthcenter/', include('applications.health_center.urls')), url(r'^leave/', include('applications.leave.urls')), - url(r'^placement/', include('applications.placement_cell.urls')), + url(r'^placement/', include('applications.placement_cell.api.urls')), url(r'^filetracking/', include('applications.filetracking.urls')), url(r'^spacs/', include('applications.scholarships.urls')), url(r'^visitorhostel/', include('applications.visitor_hostel.urls')), @@ -63,35 +60,4 @@ url(r'^income-expenditure/', include('applications.income_expenditure.urls')), url(r'^hr2/', include('applications.hr2.urls')), url(r'^recruitment/', include('applications.recruitment.urls')), - url(r'^examination/', include('applications.examination.urls')), - url(r'^otheracademic/', include('applications.otheracademic.urls')), - - path( - 'password-reset/', - RateLimitedPasswordResetView.as_view( - template_name='registration/password_reset_form.html', - ), - name='reset_password', - ), - path( - 'password-reset/done/', - auth_views.PasswordResetDoneView.as_view( - template_name='registration/password_reset_done.html' - ), - name='password_reset_done', - ), - path( - 'reset///', - auth_views.PasswordResetConfirmView.as_view( - template_name='registration/password_reset_confirm.html', - ), - name='password_reset_confirm', - ), - path( - 'reset/done/', - auth_views.PasswordResetCompleteView.as_view( - template_name='registration/password_reset_complete.html' - ), - name='password_reset_complete', - ), ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) diff --git a/FusionIIIT/applications/__init__.py b/FusionIIIT/applications/__init__.py new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/FusionIIIT/applications/__init__.py @@ -0,0 +1 @@ + diff --git a/FusionIIIT/applications/academic_information/admin.py b/FusionIIIT/applications/academic_information/admin.py index 522815707..94f60274d 100755 --- a/FusionIIIT/applications/academic_information/admin.py +++ b/FusionIIIT/applications/academic_information/admin.py @@ -32,3 +32,4 @@ class Curriculum_InstructorAdmin(admin.ModelAdmin): admin.site.register(Holiday) admin.site.register(Curriculum,CurriculumAdmin) +#Hello! diff --git a/FusionIIIT/applications/academic_information/api/serializers.py b/FusionIIIT/applications/academic_information/api/serializers.py index fd4fb59b7..06d6154c5 100644 --- a/FusionIIIT/applications/academic_information/api/serializers.py +++ b/FusionIIIT/applications/academic_information/api/serializers.py @@ -18,6 +18,7 @@ class Meta: class CurriculumSerializer(serializers.ModelSerializer): course_id = CourseSerializer() + class Meta: model = Curriculum fields = ('curriculum_id','course_code','course_id','credits','course_type', diff --git a/FusionIIIT/applications/academic_information/api/urls.py b/FusionIIIT/applications/academic_information/api/urls.py index 99b7f39bd..e7fe37be9 100644 --- a/FusionIIIT/applications/academic_information/api/urls.py +++ b/FusionIIIT/applications/academic_information/api/urls.py @@ -14,23 +14,7 @@ # url(r'^meeting',views.meeting_api,name='meeting-get-api'), - # url(r'^calendar',views.ListCalendarView.as_view(),name='calendar-get-api'), - # url(r'^update-calendar',views.update_calendar,name='calendar-update-api'), - # url(r'^add-calendar',views.add_calendar,name='calendar-add-api'), - # url(r'^delete-calendar',views.delete_calendar,name='calendar-delete-api'), - url(r'^check-allocation$', views.check_allocation_api, name='check-allocation-api'), - url(r'^start-allocation$', views.start_allocation_api, name='start-allocation-api'), - url(r'^generatexlsheet$', views.generate_xlsheet_api, name='generate-xlsheet-api'), - url(r'^available-courses/$', views.available_courses, name='available_courses'), - url(r'^generate_preregistration_report',views.generate_preregistration_report, name = "generate_preregistration_report"), - url(r'^calendar/$', views.list_calendar, name='list_calendar'), - url(r'^calendar/add/$', views.add_calendar, name='add_calendar'), - url(r'^calendar/update/$', views.update_calendar, name='update_calendar'), - url(r'^calendar/delete/$', views.delete_calendar, name='delete_calendar'), - url(r'^calendar/clear/$', views.clear_calendar, name='clear_calendar'), - url(r'^calendar/export/$', views.export_calendar, name='export_calendar'), - url(r'^calendar/import/$', views.import_calendar, name='import_calendar'), - + # url(r'^calendar',views.calendar_api,name='calendar-get-api'), # url(r'^holiday',views.holiday_api,name='holiday-get-api'), @@ -43,4 +27,4 @@ # url(r'^grades',views.grades_api,name='grades-get-api'), # url(r'^spi',views.spi_api,name='spi-get-api') -] +] \ No newline at end of file diff --git a/FusionIIIT/applications/academic_information/api/views.py b/FusionIIIT/applications/academic_information/api/views.py index bb470c70d..192785374 100644 --- a/FusionIIIT/applications/academic_information/api/views.py +++ b/FusionIIIT/applications/academic_information/api/views.py @@ -1,95 +1,72 @@ -import datetime -import json -import io -import logging -import time -import pandas as pd -from io import BytesIO -from xlsxwriter.workbook import Workbook -from django.contrib.auth import get_user_model -from django.contrib.auth.decorators import login_required -import xlsxwriter -from applications.academic_procedures.models import course_registration -from applications.academic_information.utils import allocate, check_for_registration_complete -from applications.globals.models import (HoldsDesignation,Designation) -from django.shortcuts import get_object_or_404 -from django.forms.models import model_to_dict -from rest_framework.permissions import IsAuthenticated -from rest_framework.authentication import TokenAuthentication -from rest_framework import status -from rest_framework.decorators import api_view, permission_classes,authentication_classes -from rest_framework.permissions import AllowAny -from rest_framework.response import Response -from applications.globals.models import User,ExtraInfo -from applications.academic_information.models import Student, Course, Curriculum, Curriculum_Instructor, Student_attendance, Meeting, Calendar, Holiday, Grades, Spi, Timetable, Exam_timetable -from applications.programme_curriculum.models import Course as Courses, CourseSlot, Batch, Semester, CourseInstructor -from applications.academic_procedures.models import InitialRegistration, Assignment, StipendRequest -from . import serializers -from rest_framework.generics import ListCreateAPIView -from django.views.decorators.csrf import csrf_exempt -from django.http import HttpResponse, JsonResponse -from django.db.models import Q -from applications.academic_procedures.api.views import role_required -from django.core.cache import cache -from django.db import connection - -logger = logging.getLogger(__name__) - -@api_view(['GET']) -@permission_classes([IsAuthenticated]) -@authentication_classes([TokenAuthentication]) -def student_api(request): - - if request.method == 'GET': - student = Student.objects.all() - student_serialized = serializers.StudentSerializers(student,many=True).data - resp = { - 'students' : student_serialized, - } - return Response(data=resp,status=status.HTTP_200_OK) - - -@api_view(['GET']) +# from django.contrib.auth import get_user_model +# from django.contrib.auth.decorators import login_required +# from applications.globals.models import (HoldsDesignation,Designation) +# from django.shortcuts import get_object_or_404 +# from django.forms.models import model_to_dict +# from rest_framework.permissions import IsAuthenticated +# from rest_framework.authentication import TokenAuthentication +# from rest_framework import status +# from rest_framework.decorators import api_view, permission_classes,authentication_classes +# from rest_framework.permissions import AllowAny +# from rest_framework.response import Response +# from applications.globals.models import User,ExtraInfo +# from applications.academic_information.models import Student, Course, Curriculum, Curriculum_Instructor, Student_attendance, Meeting, Calendar, Holiday, Grades, Spi, Timetable, Exam_timetable +# from . import serializers + +# @api_view(['GET']) +# @permission_classes([IsAuthenticated]) +# @authentication_classes([TokenAuthentication]) +# def student_api(request): + +# if request.method == 'GET': +# student = Student.objects.all() +# student_serialized = serializers.StudentSerializers(student,many=True).data +# resp = { +# 'students' : student_serialized, +# } +# return Response(data=resp,status=status.HTTP_200_OK) + + +# @api_view(['GET']) +# @permission_classes([IsAuthenticated]) +# @authentication_classes([TokenAuthentication]) +# def course_api(request): + +# if request.method == 'GET': +# course = Course.objects.all() +# course_serialized = serializers.CourseSerializer(course,many=True).data +# resp = { +# 'courses' : course_serialized, +# } +# return Response(data=resp,status=status.HTTP_200_OK) + + +# @api_view(['GET']) +# @permission_classes([IsAuthenticated]) +# @authentication_classes([TokenAuthentication]) +# def curriculum_api(request): + +# if request.method == 'GET': +# curriculum = Curriculum.objects.all() +# curriculum_serialized = serializers.CurriculumSerializer(curriculum,many=True).data +# resp = { +# 'curriculum' : curriculum_serialized, +# } +# return Response(data=resp,status=status.HTTP_200_OK) + + +# @api_view(['GET']) # @permission_classes([IsAuthenticated]) # @authentication_classes([TokenAuthentication]) -def course_api(request): - - if request.method == 'GET': - course = Course.objects.all() - course_serialized = serializers.CourseSerializer(course,many=True).data - resp = { - 'courses' : course_serialized, - } - return Response(data=resp,status=status.HTTP_200_OK) - - - -@api_view(['GET']) -@permission_classes([IsAuthenticated]) -@authentication_classes([TokenAuthentication]) -def curriculum_api(request): - - if request.method == 'GET': - curriculum = Curriculum.objects.all() - curriculum_serialized = serializers.CurriculumSerializer(curriculum,many=True).data - resp = { - 'curriculum' : curriculum_serialized, - } - return Response(data=resp,status=status.HTTP_200_OK) - - -@api_view(['GET']) -@permission_classes([IsAuthenticated]) -@authentication_classes([TokenAuthentication]) -def meeting_api(request): - - if request.method == 'GET': - meeting = Meeting.objects.all() - meeting_serialized = serializers.MeetingSerializers(meeting,many=True).data - resp = { - 'meeting' : meeting_serialized, - } - return Response(data=resp,status=status.HTTP_200_OK) +# def meeting_api(request): + +# if request.method == 'GET': +# meeting = Meeting.objects.all() +# meeting_serialized = serializers.MeetingSerializers(meeting,many=True).data +# resp = { +# 'meeting' : meeting_serialized, +# } +# return Response(data=resp,status=status.HTTP_200_OK) # @api_view(['GET']) @@ -104,888 +81,101 @@ def meeting_api(request): # 'calendar' :calendar_serialized, # } # return Response(data=resp,status=status.HTTP_200_OK) - -# class ListCalendarView(ListCreateAPIView): -# permission_classes = [IsAuthenticated] -# authentication_classes=[TokenAuthentication] -# serializer_class = serializers.CalendarSerializers -# queryset = Calendar.objects.all() - -# @api_view(['PUT']) + + +# @api_view(['GET']) +# @permission_classes([IsAuthenticated]) +# @authentication_classes([TokenAuthentication]) +# def holiday_api(request): + +# if request.method == 'GET': +# holiday = Holiday.objects.all() +# holiday_serialized = serializers.HolidaySerializers(holiday,many=True).data +# resp = { +# 'holiday' : holiday_serialized, +# } +# return Response(data=resp,status=status.HTTP_200_OK) + + +# @api_view(['GET']) +# @permission_classes([IsAuthenticated]) +# @authentication_classes([TokenAuthentication]) +# def timetable_api(request): + +# if request.method == 'GET': +# timetable = Timetable.objects.all() +# timetable_serialized = serializers.TimetableSerializers(timetable,many=True).data +# resp = { +# 'timetable' : timetable_serialized, +# } +# return Response(data=resp,status=status.HTTP_200_OK) + + +# @api_view(['GET']) +# @permission_classes([IsAuthenticated]) +# @authentication_classes([TokenAuthentication]) +# def exam_timetable_api(request): + +# if request.method == 'GET': +# exam_timetable = Exam_timetable.objects.all() +# exam_timetable_serialized = serializers.Exam_timetableSerializers(exam_timetable,many=True).data +# resp = { +# 'exam_timetable' : exam_timetable_serialized, +# } +# return Response(data=resp,status=status.HTTP_200_OK) + + +# @api_view(['GET']) +# @permission_classes([IsAuthenticated]) +# @authentication_classes([TokenAuthentication]) +# def curriculum_instructor_api(request): + +# if request.method == 'GET': +# curriculum_instructor = Curriculum_Instructor.objects.all() +# curriculum_instructor_serialized = serializers.CurriculumInstructorSerializer(curriculum_instructor,many=True).data +# resp = { +# 'curriculum_instructor' : curriculum_instructor_serialized, +# } +# return Response(data=resp,status=status.HTTP_200_OK) + + +# @api_view(['GET']) # @permission_classes([IsAuthenticated]) # @authentication_classes([TokenAuthentication]) -# def update_calendar(request): -# if request.method == "PUT": -# id = request.data.get("id") -# instance = Calendar.objects.get(pk = id) -# instance.from_date = request.data.get("from_date") -# instance.to_date = request.data.get("to_date") -# instance.description = request.data.get("description") -# instance.save() - -# return Response({"message": "Updated successfully!"}) - -# @api_view(['POST']) +# def student_attendance_api(request): + +# if request.method == 'GET': +# student_attendance = Student_attendance.objects.all() +# student_attendance_serialized = serializers.Student_attendanceSerializers(student_attendance,many=True).data +# resp = { +# 'student_attendance' : student_attendance_serialized, +# } +# return Response(data=resp,status=status.HTTP_200_OK) + + +# @api_view(['GET']) # @permission_classes([IsAuthenticated]) # @authentication_classes([TokenAuthentication]) -# def add_calendar(request): -# if request.method == "POST": -# from_date = request.data.get("from_date") -# to_date = request.data.get("to_date") -# description = request.data.get("description") -# Calendar.objects.create(from_date=from_date, to_date=to_date, description=description) - -# return Response({"message": "Created successfully!"}) - -# @api_view(['DELETE']) +# def grades_api(request): + +# if request.method == 'GET': +# grades = Grades.objects.all() +# grades_serialized = serializers.GradesSerializers(grades,many=True).data +# resp = { +# 'grades' : grades_serialized, +# } +# return Response(data=resp,status=status.HTTP_200_OK) + + +# @api_view(['GET']) # @permission_classes([IsAuthenticated]) # @authentication_classes([TokenAuthentication]) -# def delete_calendar(request): -# id = request.data.get("id") # Get the ID from request body - -# try: -# instance = Calendar.objects.get(pk=id) -# instance.delete() -# return Response({"message": "Deleted successfully!"}, status=200) -# except Calendar.DoesNotExist: -# return Response({"error": "Calendar entry not found"}, status=404) - -@api_view(['GET']) -@permission_classes([IsAuthenticated]) -@authentication_classes([TokenAuthentication]) -def holiday_api(request): - - if request.method == 'GET': - holiday = Holiday.objects.all() - holiday_serialized = serializers.HolidaySerializers(holiday,many=True).data - resp = { - 'holiday' : holiday_serialized, - } - return Response(data=resp,status=status.HTTP_200_OK) - - -@api_view(['GET']) -@permission_classes([IsAuthenticated]) -@authentication_classes([TokenAuthentication]) -def timetable_api(request): - - if request.method == 'GET': - timetable = Timetable.objects.all() - timetable_serialized = serializers.TimetableSerializers(timetable,many=True).data - resp = { - 'timetable' : timetable_serialized, - } - return Response(data=resp,status=status.HTTP_200_OK) - - -@api_view(['GET']) -@permission_classes([IsAuthenticated]) -@authentication_classes([TokenAuthentication]) -def exam_timetable_api(request): - - if request.method == 'GET': - exam_timetable = Exam_timetable.objects.all() - exam_timetable_serialized = serializers.Exam_timetableSerializers(exam_timetable,many=True).data - resp = { - 'exam_timetable' : exam_timetable_serialized, - } - return Response(data=resp,status=status.HTTP_200_OK) - - -@api_view(['GET']) -@permission_classes([IsAuthenticated]) -@authentication_classes([TokenAuthentication]) -def curriculum_instructor_api(request): - - if request.method == 'GET': - curriculum_instructor = Curriculum_Instructor.objects.all() - curriculum_instructor_serialized = serializers.CurriculumInstructorSerializer(curriculum_instructor,many=True).data - resp = { - 'curriculum_instructor' : curriculum_instructor_serialized, - } - return Response(data=resp,status=status.HTTP_200_OK) - - -@api_view(['GET']) -@permission_classes([IsAuthenticated]) -@authentication_classes([TokenAuthentication]) -def student_attendance_api(request): - - if request.method == 'GET': - student_attendance = Student_attendance.objects.all() - student_attendance_serialized = serializers.Student_attendanceSerializers(student_attendance,many=True).data - resp = { - 'student_attendance' : student_attendance_serialized, - } - return Response(data=resp,status=status.HTTP_200_OK) - - -@api_view(['GET']) -@permission_classes([IsAuthenticated]) -@authentication_classes([TokenAuthentication]) -def grades_api(request): - - if request.method == 'GET': - grades = Grades.objects.all() - grades_serialized = serializers.GradesSerializers(grades,many=True).data - resp = { - 'grades' : grades_serialized, - } - return Response(data=resp,status=status.HTTP_200_OK) - - -@api_view(['GET']) -@permission_classes([IsAuthenticated]) -@authentication_classes([TokenAuthentication]) -def spi_api(request): - - if request.method == 'GET': - spi = Spi.objects.all() - spi_serialized = serializers.SpiSerializers(spi,many=True).data - resp = { - 'spi' : spi_serialized, - } - return Response(data=resp,status=status.HTTP_200_OK) - -@api_view(['POST']) -@permission_classes([IsAuthenticated]) -@authentication_classes([TokenAuthentication]) -@role_required(['acadadmin']) -def check_allocation_api(request): - """ - API to check the allocation status for a given batch, semester, year, and programme_type. - Uses the utility function to avoid code repetition. - """ - try: - batch = request.data.get('batch') - sem = request.data.get('sem') - year = request.data.get('year') - programme_type = request.data.get('programme_type') - - if not batch or not sem or not year: - return Response( - {"status": -3, "message": "Batch, semester, and year are required"}, - status=status.HTTP_400_BAD_REQUEST - ) - - try: - batch = int(batch) - sem = int(sem) - except ValueError: - return Response( - {"status": -3, "message": "Invalid batch or semester value"}, - status=status.HTTP_400_BAD_REQUEST - ) - - result = check_for_registration_complete(batch, sem, year, programme_type) - - # Map status values to appropriate HTTP codes - status_code_map = { - -3: status.HTTP_404_NOT_FOUND, - -2: status.HTTP_403_FORBIDDEN, - -1: status.HTTP_200_OK, - 1: status.HTTP_200_OK, - 2: status.HTTP_200_OK, - } - - return Response(result, status=status_code_map.get(result["status"], status.HTTP_500_INTERNAL_SERVER_ERROR)) - - except Exception as e: - return Response( - {"status": -3, "message": f"Internal Server Error: {str(e)}"}, - status=status.HTTP_500_INTERNAL_SERVER_ERROR - ) - -@api_view(['POST']) -@permission_classes([IsAuthenticated]) -@authentication_classes([TokenAuthentication]) -@role_required(['acadadmin']) -def start_allocation_api(request): - if request.method != "POST": - return JsonResponse({"error": "Invalid request method"}, status=400) - - try: - data = json.loads(request.body.decode('utf-8')) - batch = data.get("batch") - semester = data.get("semester") - year = data.get("year") - programme_type = data.get("programme_type") - - if not batch or not semester or not year: - return JsonResponse({"error": "Missing required fields"}, status=400) - - batch = int(batch) - semester = int(semester) - - mock_request = type('MockRequest', (), {})() - mock_request.POST = {'batch': batch, 'sem': semester, 'year': year, 'programme_type': programme_type} - - return allocate(mock_request) - - except json.JSONDecodeError: - return JsonResponse({"error": "Invalid JSON format"}, status=400) - - except Exception as e: - return JsonResponse({"error": str(e)}, status=500) - - -def parse_academic_year(academic_year, semester_type): - """ - Parse academic_year string (e.g., "2024-25") and determine the working_year based on semester type. - For Odd Semester, working_year = first part (e.g., 2024). - For Even Semester, working_year = second part prefixed by '20' (e.g., 2025 if academic_year is "2024-25"). - The session is set to the academic_year string. - """ - parts = academic_year.split("-") - if len(parts) != 2: - raise ValueError("Invalid academic year format. Expected format like '2024-25'.") - first_year = parts[0].strip() - second_year = parts[1].strip() - if semester_type == "Odd Semester": - working_year = int(first_year) - elif semester_type == "Even Semester": - working_year = int("20" + second_year) - else: - # For any other semester type (e.g., Summer Semester) use the first year by default. - working_year = int("20"+second_year) - return working_year - - -@api_view(['POST']) -@authentication_classes([TokenAuthentication]) -@permission_classes([IsAuthenticated]) -@role_required(['acadadmin', "Associate Professor", "Professor", "Assistant Professor", "Dean Academic"]) -def generate_xlsheet_api(request): - try: - start_time = time.time() - - # Get parameters - course_id = int(request.data.get('course')) - academic_year = request.data.get('academic_year') - semester_type = request.data.get('semester_type') - list_type = request.data.get('list_type', '').strip() - programme_type = request.data.get('programme_type', '').strip() - preview_only = request.data.get('preview_only', False) - - if not list_type: - list_type = None - if not programme_type: - programme_type = None - - if not all([course_id, academic_year, semester_type]): - return Response({ - 'error': 'Missing required parameters: course, academic_year, semester_type' - }, status=status.HTTP_400_BAD_REQUEST) - cache_key = f"student_list_{course_id}_{academic_year.replace('-', '_')}_{semester_type.replace(' ', '_')}_{(list_type or 'all').replace(' ', '_')}_{(programme_type or 'all').replace(' ', '_')}" - - cached_data = cache.get(cache_key) - if cached_data and not preview_only: - students, course_info = cached_data - else: - sql = """ - SELECT DISTINCT - u.username as roll_no, - u.first_name, - u.last_name, - CONCAT(u.first_name, ' ', u.last_name) as full_name, - COALESCE(d.acronym, 'General') as discipline, - u.email, - cr.registration_type, - c.code as course_code, - c.name as course_name, - s.programme - FROM course_registration cr - INNER JOIN globals_extrainfo ei ON cr.student_id_id = ei.id - INNER JOIN auth_user u ON ei.user_id = u.id - LEFT JOIN academic_information_student s ON ei.id = s.id_id - LEFT JOIN programme_curriculum_batch b ON s.batch_id_id = b.id - LEFT JOIN programme_curriculum_discipline d ON b.discipline_id = d.id - INNER JOIN programme_curriculum_course c ON cr.course_id_id = c.id - WHERE cr.session = %s - AND cr.semester_type = %s - AND cr.course_id_id = %s - """ - - params = [academic_year, semester_type, course_id] - - # Add list_type filter if specified - if list_type: - if list_type.lower() == 'backlog_improvement': - sql += " AND cr.registration_type IN ('Backlog', 'Improvement')" - else: - sql += " AND cr.registration_type = %s" - params.append(list_type) - - # Add programme_type filter if specified - if programme_type: - if programme_type.upper() == 'UG': - sql += " AND s.programme IN ('B.Tech', 'B.Des')" - elif programme_type.upper() == 'PG': - sql += " AND s.programme IN ('M.Tech', 'M.Des', 'PhD')" - else: - sql += " AND s.programme = %s" - params.append(programme_type) - - sql += " ORDER BY u.username" - try: - with connection.cursor() as cursor: - start_sql = time.time() - - cursor.execute(sql, params) - columns = [col[0] for col in cursor.description] - raw_results = cursor.fetchall() - students = [] - for row in raw_results: - data = dict(zip(columns, row)) - students.append({ - 'roll_no': data['roll_no'], - 'first_name': data['first_name'], - 'last_name': data['last_name'], - 'full_name': data['full_name'], - 'discipline': data['discipline'], - 'email': data['email'], - 'registration_type': data['registration_type'], - 'programme': data.get('programme', '') - }) - - sql_time = time.time() - start_sql - - reg_types = set(student['registration_type'] for student in students) - - if raw_results: - course_info = { - 'code': raw_results[0][7], # course_code - 'name': raw_results[0][8] # course_name - } - else: - cursor.execute("SELECT code, name FROM programme_curriculum_course WHERE id = %s", [course_id]) - result = cursor.fetchone() - course_info = {'code': result[0], 'name': result[1]} if result else {'code': 'N/A', 'name': 'Unknown'} - except Exception as e: - # Debug: logger.error(f"SQL Error in generate_xlsheet_api: {str(e)}") - return Response({ - 'error': f'Database query failed: {str(e)}', - 'sql_debug': sql, - 'params': params - }, status=500) - - cache.set(cache_key, (students, course_info), 300) - - if not list_type: - list_type_display = "All Enrolled Students" - elif list_type.lower() == 'backlog_improvement': - list_type_display = "Backlog & Improvement Students" - else: - list_type_display = f"{list_type} Students" - - # Add programme type to display name - if programme_type: - if programme_type.upper() == 'UG': - list_type_display += " (UG Only)" - elif programme_type.upper() == 'PG': - list_type_display += " (PG Only)" - else: - list_type_display += f" ({programme_type} Only)" - - processing_time = time.time() - start_time - if preview_only: - preview_students = students[:350] if len(students) > 350 else students - - return Response({ - 'students': preview_students, - 'course_info': { - 'code': course_info['code'], - 'name': course_info['name'], - 'academic_year': academic_year, - 'semester_type': semester_type, - 'list_type': list_type_display, - 'total_count': len(students), - 'preview_count': len(preview_students), - 'processing_time_ms': round(processing_time * 1000, 2) - } - }, status=status.HTTP_200_OK) - - # OPTIMIZATION 7: Fast Excel generation with minimal formatting - from openpyxl import Workbook - from openpyxl.styles import Font, PatternFill, Alignment - - wb = Workbook() - ws = wb.active - ws.title = "Student List" - - column_widths = [8, 15, 30, 15, 35, 18, 15] - for i, width in enumerate(column_widths, 1): - ws.column_dimensions[chr(64 + i)].width = width - - # Minimal header formatting (single operation) - header_font = Font(bold=True, color="FFFFFF") - header_fill = PatternFill(start_color="366092", end_color="366092", fill_type="solid") - - # Add title rows efficiently - ws.merge_cells('A1:G1') - ws['A1'] = "PDPM INDIAN INSTITUTE OF INFORMATION TECHNOLOGY, DESIGN AND MANUFACTURING JABALPUR" - ws['A1'].font = Font(bold=True, size=9) - ws['A1'].alignment = Alignment(horizontal="center") - - ws.merge_cells('A2:G2') - ws['A2'] = f"{semester_type.upper()}, {academic_year}" - ws['A2'].font = Font(bold=True, size=12) - ws['A2'].alignment = Alignment(horizontal="center") - - # Get instructor information - year_parts = academic_year.split('-') - if semester_type == "Even Semester": - year_int = int(year_parts[0]) + 1 - else: - year_int = int(year_parts[0]) - - instructor_name = "TBA" - course_instructor = CourseInstructor.objects.filter(course_id=course_id, year=year_int, semester_type=semester_type).first() - if course_instructor: - instructor_name = f"{course_instructor.instructor_id.id.user.first_name} {course_instructor.instructor_id.id.user.last_name}".strip() - - # Course details - ws['A3'] = "Course No:" - ws['B3'] = course_info['code'] - ws['A4'] = "Course Title:" - ws.merge_cells('B4:G4') - ws['B4'] = course_info['name'] - ws['A5'] = "Instructor:" - ws.merge_cells('B5:G5') - ws['B5'] = instructor_name - ws['A6'] = "List Type:" - ws.merge_cells('B6:G6') - ws['B6'] = list_type_display - - headers = ['Sl. No', 'Roll No', 'Name', 'Discipline', 'Email', 'Reg. Type', 'Signature'] - for col, header in enumerate(headers, 1): - cell = ws.cell(row=8, column=col, value=header) - cell.font = header_font - cell.fill = header_fill - cell.alignment = Alignment(horizontal="center") - - for idx, student in enumerate(students, 1): - row_data = [ - idx, - student['roll_no'], - student['full_name'], - student['discipline'], - student['email'], - student['registration_type'], - '' # Signature - ] - ws.append(row_data) - - from io import BytesIO - output = BytesIO() - wb.save(output) - output.seek(0) - - response = HttpResponse( - output.getvalue(), - content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' - ) - - if not list_type: - filename_suffix = "All_Enrolled_Students" - elif list_type.lower() == 'backlog_improvement': - filename_suffix = "Backlog_Improvement_Students" - else: - filename_suffix = f"{list_type.replace(' ', '_')}_Students" - - if programme_type: - if programme_type.upper() == 'UG': - filename_suffix += "_UG" - elif programme_type.upper() == 'PG': - filename_suffix += "_PG" - else: - filename_suffix += f"_{programme_type.replace(' ', '_')}" - - filename = f"{course_info['code']}_{filename_suffix}_CourseList.xlsx" - response['Content-Disposition'] = f'attachment; filename="{filename}"' - - total_time = time.time() - start_time - response['X-Processing-Time'] = f"{round(total_time * 1000, 2)}ms" - response['X-Student-Count'] = str(len(students)) - - return response - - except Exception as e: - try: - error_time = time.time() - start_time - except: - error_time = 0 - - import traceback - traceback.print_exc() - - return Response({ - 'error': f'Error: {str(e)}', - 'error_type': str(type(e)), - 'processing_time_ms': round(error_time * 1000, 2) - }, status=status.HTTP_500_INTERNAL_SERVER_ERROR) - -@api_view(['POST']) -@permission_classes([IsAuthenticated]) -@authentication_classes([TokenAuthentication]) -@role_required(['acadadmin']) -def generate_preregistration_report(request): - """ - to generate preresgistration report after pre-registration - - @param: - request - contains metadata about the requested page - - @variables: - sem - get current semester from current time - now - get current time - year - getcurrent year - batch - gets the batch from form - sem - stores the next semester - obj - All the registration details appended into one - data - Formated data for context - m - counter for Sl. No (in formated data) - z - temporary array to add data to variable data - k -temporary array to add data to formatted array/variable - output - io Bytes object to write to xlsx file - book - workbook of xlsx file - title - formatting variable of title the workbook - subtitle - formatting variable of subtitle the workbook - normaltext - formatting variable for normal text - sheet - xlsx sheet to be rendered - titletext - formatting variable of title text - dep - temporary variables - z - temporary variables for final output - b - temporary variables for final output - c - temporary variables for final output - st - temporary variables for final output - - """ - - if request.method == "POST": - sem = request.data.get('semester_no') - batch_id=request.data.get('batch_branch') - batch = Batch.objects.filter(id = batch_id).first() - obj = InitialRegistration.objects.filter(student_id__batch_id=batch_id, semester_id__semester_no=sem) - - - - registered_students = set() - unregistered_students = set() - - # registered students contains objects of type InitialRegistration - for stu in obj: - registered_students.add(stu.student_id) - - students = Student.objects.filter(batch_id = batch_id) - - for stu in students: - if stu not in registered_students: - unregistered_students.add(stu) - - - # for stu in obj: - # registered_students.add(stu.student_id) - # students = Student.objects.filter(batch_id = batch_id) - # for stu in students: - # if stu not in registered_students: - # unregistered_students.add(stu) - - - - data = [] - m = 1 - for i in unregistered_students: - # z is a row in excel - z = [] - z.append(m) - m += 1 - z.append(i.id.user.username) - z.append(str(i.id.user.first_name)+" "+str(i.id.user.last_name)) - z.append(i.id.department.name) - z.append('Not Registered') - data.append(z) - - sem_id = Semester.objects.get(curriculum = batch.curriculum, semester_no = sem) - course_slots = CourseSlot.objects.all().filter(semester = sem_id) - max_width = 1 - for student in registered_students: - #z = [] - # z.append(m) - # m += 1 - # z.append(i.id.user.username) - # z.append(str(i.id.user.first_name)+" "+str(i.id.user.last_name)) - # z.append(i.id.department.name) - # z.append('Registered') - # data.append(z) - current_student_registered_courses = InitialRegistration.objects.filter(student_id=student, semester_id__semester_no=sem).all() - timestamp = current_student_registered_courses.first().timestamp - #print("current student is ",student.id.user.username) - #print("timstamp value ",timestamp) - for slot in course_slots: - #print("current slot belongs to ",slot) - z = [] - z.append(m) - z.append(student.id.user.username) - z.append(str(student.id.user.first_name)+" "+str(student.id.user.last_name)) - z.append(student.id.department.name) - z.append('Registered') - z.append(str(timestamp)) - z.append(str(slot.name)) - - choices_of_current_student = InitialRegistration.objects.filter(student_id=student, semester_id__semester_no=sem,course_slot_id = slot).all() - max_width = max(max_width,len(choices_of_current_student)) - - for choice in range(1,len(choices_of_current_student)+1): - try: - current_choice = InitialRegistration.objects.get(student_id=student, semester_id__semester_no=sem, course_slot_id=slot, priority=choice) - z.append(str(current_choice.course_id.code) + "-" + str(current_choice.course_id.name)) - except : - z.append("No registration found") - # current_choice = InitialRegistration.objects.get(student_id=student, semester_id__semester_no=sem,course_slot_id = slot,priority = choice) - # # #print("current choice is ",current_choice) - # z.append(str(current_choice.course_id.code)+"-"+str(current_choice.course_id.name)) - - data.append(z) - m+=1 - output = BytesIO() - - book = xlsxwriter.Workbook(output,{'in_memory':True}) - title = book.add_format({'bold': True, - 'font_size': 22, - 'align': 'center', - 'valign': 'vcenter'}) - subtitle = book.add_format({'bold': True, - 'font_size': 15, - 'align': 'center', - 'valign': 'vcenter'}) - normaltext = book.add_format({'bold': False, - 'font_size': 15, - 'align': 'center', - 'valign': 'vcenter'}) - sheet = book.add_worksheet() - - # add semester too in title text - title_text = ("Pre-registeration : "+ batch.name + str(" ") + batch.discipline.acronym + str(" ") + str(batch.year) + " Semester : "+str(sem)) - # ?? - sheet.set_default_row(25) - characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - # text, formatting - sheet.merge_range('A2:E2', title_text, title) - sheet.write_string('A3',"Sl. No",subtitle) - sheet.write_string('B3',"Roll No",subtitle) - sheet.write_string('C3',"Name",subtitle) - sheet.write_string('D3',"Discipline",subtitle) - sheet.write_string('E3','Status',subtitle) - sheet.write_string('F3','TimeStamp',subtitle) - sheet.write_string('G3','Course Slot ID',subtitle) - for choice_num in range(7,7+max_width): - sheet.write_string(characters[choice_num]+'3','Choice '+str(choice_num-6),subtitle) - - - # Width of column - sheet.set_column('A:A',20) - sheet.set_column('B:B',20) - sheet.set_column('C:C',50) - sheet.set_column('D:D',15) - sheet.set_column('E:E',20) - sheet.set_column('F:F',40) - sheet.set_column('G:G',30) - sheet.set_column('H:H',70) - sheet.set_column('I:I',70) - sheet.set_column('J:J',70) - sheet.set_column('K:K',70) - sheet.set_column('L:L',70) - sheet.set_column('M:M',70) - #rows numbers - k = 4 - # SERIAL numbers S.no 1,2,3... - num = 1 - for i in data: - sheet.write_number('A'+str(k),num,normaltext) - num+=1 - z,b,c = str(i[0]),i[1],i[2] - if(len(i) > 5): - a,b,c,d,e,f,g = str(i[0]),str(i[1]),str(i[2]),str(i[3]),str(i[4]),str(i[5]),str(i[6]) - temp = str(i[3]).split() - sheet.write_string('B'+str(k),b,normaltext) - sheet.write_string('C'+str(k),c,normaltext) - sheet.write_string('D'+str(k),d,normaltext) - sheet.write_string('E'+str(k),e,normaltext) - sheet.write_string('F'+str(k),f,normaltext) - sheet.write_string('G'+str(k),g,normaltext) - characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - # for character in characters - for temp_num in range(7,len(i)): - sheet.write_string(characters[temp_num]+str(k),str(i[temp_num]),normaltext) - else: - a,b,c,d,e= str(i[0]),str(i[1]),str(i[2]),str(i[3]),str(i[4]) - temp = str(i[3]).split() - sheet.write_string('B'+str(k),b,normaltext) - sheet.write_string('C'+str(k),c,normaltext) - sheet.write_string('D'+str(k),d,normaltext) - sheet.write_string('E'+str(k),e,normaltext) - - k+=1 - book.close() - # ?? - output.seek(0) - response = HttpResponse(output.read(),content_type = 'application/vnd.ms-excel') - st = 'attachment; filename = ' + batch.name + batch.discipline.acronym + str(batch.year) + '-preresgistration.xlsx' - response['Content-Disposition'] = st - return response - - -@api_view(['GET']) -@permission_classes([IsAuthenticated]) -@authentication_classes([TokenAuthentication]) - -def list_calendar(request): - events = Calendar.objects.all().values('id', 'description', 'from_date', 'to_date') - return Response(list(events)) - -@api_view(['POST']) -@permission_classes([IsAuthenticated]) -@authentication_classes([TokenAuthentication]) -@role_required(['acadadmin']) -def add_calendar(request): - Calendar.objects.create( - description=request.data.get('description'), - from_date=request.data.get('from_date'), - to_date=request.data.get('to_date'), - ) - return Response({'message': 'Created successfully!'}) - -@api_view(['PUT']) -@permission_classes([IsAuthenticated]) -@authentication_classes([TokenAuthentication]) -@role_required(['acadadmin']) -def update_calendar(request): - try: - cal = Calendar.objects.get(pk=request.data.get('id')) - except Calendar.DoesNotExist: - return Response({'error': 'Not found'}, status=404) - cal.description = request.data.get('description') - cal.from_date = request.data.get('from_date') - cal.to_date = request.data.get('to_date') - cal.save() - return Response({'message': 'Updated successfully!'}) - -@api_view(['DELETE']) -@permission_classes([IsAuthenticated]) -@authentication_classes([TokenAuthentication]) -@role_required(['acadadmin']) -def delete_calendar(request): - try: - cal = Calendar.objects.get(pk=request.data.get('id')) - cal.delete() - return Response({'message': 'Deleted successfully!'}) - except Calendar.DoesNotExist: - return Response({'error': 'Not found'}, status=404) - -@api_view(['DELETE']) -@permission_classes([IsAuthenticated]) -@authentication_classes([TokenAuthentication]) -@role_required(['acadadmin']) -def clear_calendar(request): - Calendar.objects.all().delete() - return Response({'message': 'All events deleted!'}) - -@api_view(['GET']) -@permission_classes([IsAuthenticated]) -@authentication_classes([TokenAuthentication]) -@role_required(['acadadmin']) -def export_calendar(request): - qs = Calendar.objects.all().values('description', 'from_date', 'to_date') - df = pd.DataFrame(list(qs)) - buf = io.BytesIO() - with pd.ExcelWriter(buf, engine='xlsxwriter') as writer: - df.to_excel(writer, index=False) - buf.seek(0) - resp = HttpResponse( - buf.read(), - content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' - ) - resp['Content-Disposition'] = 'attachment; filename="calendar.xlsx"' - return resp - -@api_view(['POST']) -@permission_classes([IsAuthenticated]) -@authentication_classes([TokenAuthentication]) -@role_required(['acadadmin']) -def import_calendar(request): - file = request.FILES.get('file') - if not file: - return Response({'error': 'Excel file required'}, status=400) - df = pd.read_excel(file) - errors = [] - for _, row in df.iterrows(): - try: - Calendar.objects.create( - description=row['description'], - from_date=row['from_date'], - to_date=row['to_date'], - ) - except Exception as e: - errors.append(str(e)) - if errors: - return Response({'errors': errors}, status=400) - return Response({'message': 'Imported successfully!'}) - - -@api_view(['GET']) -@permission_classes([IsAuthenticated]) -@role_required(['acadadmin']) -def available_courses(request): - """ - GET /api/available-courses/?academic_year=2024-25&semester_type=Odd+Semester&programme_type=UG - Returns unique courses for which the student has registrations. - """ - year = request.query_params.get('academic_year') - sem = request.query_params.get('semester_type') - programme_type = request.query_params.get('programme_type') # Get programme_type filter - if not year or not sem: - return Response({"detail": "academic_year and semester_type required"}, status=400) - - regs = course_registration.objects.filter(session=year, semester_type=sem) - - if programme_type: - programme_mapping = { - 'UG': ['B.Tech', 'B.Des'], - 'PG': ['M.Tech', 'M.Des', 'PhD'] - } - - if programme_type in programme_mapping: - programmes = programme_mapping[programme_type] - from applications.academic_information.models import Student - student_ids_with_programme = Student.objects.filter( - programme__in=programmes - ).values_list('id', flat=True) - - regs = regs.filter(student_id__in=student_ids_with_programme) - - course_ids = regs.values_list('course_id', flat=True).distinct() - courses = Courses.objects.filter(id__in=course_ids) - - data = [] - # Calculate correct year based on semester type - year_parts = year.split('-') - if sem == "Even Semester": - year_int = int(year_parts[0]) + 1 # Even semester is in second year (e.g., 2026 for 2025-26) - else: - year_int = int(year_parts[0]) # Odd/Summer semester is in first year (e.g., 2025 for 2025-26) - - for c in courses: - instructor_name = "TBA" - course_instructor = CourseInstructor.objects.filter(course_id=c, year=year_int, semester_type=sem).first() - if course_instructor: - instructor_name = f"{course_instructor.instructor_id.id.user.first_name} {course_instructor.instructor_id.id.user.last_name}".strip() - - data.append({ - "id": c.id, - "code": c.code, - "name": c.name, - "instructor": instructor_name - }) - - return Response(data) \ No newline at end of file +# def spi_api(request): + +# if request.method == 'GET': +# spi = Spi.objects.all() +# spi_serialized = serializers.SpiSerializers(spi,many=True).data +# resp = { +# 'spi' : spi_serialized, +# } +# return Response(data=resp,status=status.HTTP_200_OK) \ No newline at end of file diff --git a/FusionIIIT/applications/academic_information/forms.py b/FusionIIIT/applications/academic_information/forms.py index 8fde1e589..ddf7ddcdc 100755 --- a/FusionIIIT/applications/academic_information/forms.py +++ b/FusionIIIT/applications/academic_information/forms.py @@ -1,6 +1,6 @@ from django import forms -from .models import Exam_timetable, Meeting, Timetable, Student +from .models import Exam_timetable, Meeting, Timetable class MinuteForm(forms.ModelForm): @@ -49,15 +49,3 @@ class ExamTimetableForm(forms.ModelForm): class Meta: model = Exam_timetable fields = ('programme', 'exam_time_table',) - - -class PreRegistrationSearchForm(forms.Form): - roll_no = forms.CharField( - max_length=20, - widget=forms.TextInput(attrs={'class': 'ui input', 'placeholder': 'Enter Roll Number'}), - label="Roll Number" - ) - semester_no = forms.IntegerField( - widget=forms.NumberInput(attrs={'class': 'ui input', 'placeholder': 'Enter Semester No'}), - label="Semester No" - ) \ No newline at end of file diff --git a/FusionIIIT/applications/academic_information/migrations/0001_initial.py b/FusionIIIT/applications/academic_information/migrations/0001_initial.py index 22fd38161..f75d3119c 100644 --- a/FusionIIIT/applications/academic_information/migrations/0001_initial.py +++ b/FusionIIIT/applications/academic_information/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 3.1.5 on 2024-07-16 15:44 +# Generated by Django 3.1.5 on 2023-03-15 18:53 from django.db import migrations, models import django.db.models.deletion @@ -117,8 +117,8 @@ class Migration(migrations.Migration): ('batch', models.IntegerField(default=2016)), ('cpi', models.FloatField(default=0)), ('category', models.CharField(choices=[('GEN', 'General'), ('SC', 'Scheduled Castes'), ('ST', 'Scheduled Tribes'), ('OBC', 'Other Backward Classes')], max_length=10)), - ('father_name', models.CharField(default='', max_length=40, null=True)), - ('mother_name', models.CharField(default='', max_length=40, null=True)), + ('father_name', models.CharField(default='', max_length=40)), + ('mother_name', models.CharField(default='', max_length=40)), ('hall_no', models.IntegerField(default=0)), ('room_no', models.CharField(blank=True, max_length=10, null=True)), ('specialization', models.CharField(choices=[('Power and Control', 'Power and Control'), ('Microwave and Communication Engineering', 'Microwave and Communication Engineering'), ('Micro-nano Electronics', 'Micro-nano Electronics'), ('CAD/CAM', 'CAD/CAM'), ('Design', 'Design'), ('Manufacturing', 'Manufacturing'), ('CSE', 'CSE'), ('Mechatronics', 'Mechatronics'), ('MDes', 'MDes'), ('None', 'None')], default='', max_length=40, null=True)), diff --git a/FusionIIIT/applications/academic_information/models.py b/FusionIIIT/applications/academic_information/models.py index fe51b81b0..ea1178878 100755 --- a/FusionIIIT/applications/academic_information/models.py +++ b/FusionIIIT/applications/academic_information/models.py @@ -1,6 +1,6 @@ from django.db import models -from applications.globals.models import ExtraInfo, Faculty +from applications.globals.models import ExtraInfo from applications.programme_curriculum.models import Batch @@ -41,22 +41,15 @@ class Constants: MTechSpecialization = ( ('Power and Control', 'Power and Control'), - ('Power & Control', 'Power & Control'), # Alternative format ('Microwave and Communication Engineering', 'Microwave and Communication Engineering'), - ('Communication and Signal Processing', 'Communication and Signal Processing'), ('Micro-nano Electronics', 'Micro-nano Electronics'), - ('Nanoelectronics and VLSI Design', 'Nanoelectronics and VLSI Design'), ('CAD/CAM', 'CAD/CAM'), ('Design', 'Design'), ('Manufacturing', 'Manufacturing'), - ('Manufacturing and Automation', 'Manufacturing and Automation'), ('CSE', 'CSE'), - ('AI & ML', 'AI & ML'), - ('Data Science', 'Data Science'), ('Mechatronics', 'Mechatronics'), ('MDes', 'MDes'), - ('None', 'None'), - ('', 'No Specialization') + ('None', 'None') ) COURSE_TYPE = ( @@ -76,7 +69,7 @@ class Student(models.Model): ''' Current Purpose : To store information pertinent to a user who is also a student - + ATTRIBUTES : @@ -100,12 +93,15 @@ class Student(models.Model): batch_id = models.ForeignKey(Batch, null=True, blank=True, on_delete=models.CASCADE) cpi = models.FloatField(default=0) category = models.CharField(max_length=10, choices=Constants.CATEGORY, null=False) - father_name = models.CharField(max_length=40, default='',null=True) - mother_name = models.CharField(max_length=40, default='',null=True) + father_name = models.CharField(max_length=40, default='') + mother_name = models.CharField(max_length=40, default='') hall_no = models.IntegerField(default=0) room_no = models.CharField(max_length=10, blank=True, null=True) specialization = models.CharField(max_length=40,choices=Constants.MTechSpecialization, null=True, default='') curr_semester_no = models.IntegerField(default=1) +# pwd_status = models.BooleanField(null=True, blank=True,default=False) +# father_mobile_no = models.CharField(max_length=10, null=True, blank=True, default='9999999999') +# mother_mobile_no = models.CharField(max_length=10, null=True, blank=True, default='9999999999') def __str__(self): username = str(self.id.user.username) @@ -114,7 +110,17 @@ def __str__(self): class Course(models.Model): - + ''' + Current Purpose : To store information of a course + + + + ATTRIBUTES : + + course_name(char) - to store the name of the course + course_details(TextField) - to store the course details + + ''' course_name = models.CharField(max_length=600) course_details = models.TextField(max_length=500) @@ -126,7 +132,25 @@ def __str__(self): class Curriculum(models.Model): - + ''' + Current Purpose : Currently this table stores mapping of a course to a particular semester and relevant details + + ! - the table does not follow the conventional definition of the term Curriculum + ATTRIBUTES : + + curriculum_id(AutoField) - to store the automatically generated course id which will be the primary key + course_code(CharField) - stores the course code (can be null) + course_id(academic_information.Course) - foreign key to the course + credits(integer) - stores credits assigned for the course + course_type(char) - to select the type of the course(eg : Professional Core/Project) + programme(char) - to store the programme for which the course is being offered + branch(char) - to store the branch/discipline for which the course is being offered(eg CSE) + batch(integer) - to store the batch for which the course is available(eg:2019) + sem(Integer) - the semester in which the course is offered for a batch + optional(Boolean) - denotes whether the course is optional or not + floated(Boolean) - denotes whether a course is floated for a particular semester or not + + ''' curriculum_id = models.AutoField(primary_key=True) course_code = models.CharField(max_length = 20) course_id = models.ForeignKey(Course,on_delete= models.CASCADE) @@ -148,7 +172,23 @@ def __str__(self): class Curriculum_Instructor(models.Model): - + ''' + Current Purpose : Currently this table stores mapping of a course in a semester to the faculty who will teach the course + + ! - the table does not follow the conventional definition of the term Curriculum + + + + + + ATTRIBUTES : + + curriculum_id(academic_information.Curriculum) - reference to the course details + instructor_id(globals.ExtraInfo) - reference to the faculty who will teach the course + chief_inst(Bool) - denotes whether the faculty is the chief instructor of the course or not + + + ''' curriculum_id = models.ForeignKey(Curriculum, on_delete=models.CASCADE) instructor_id = models.ForeignKey(ExtraInfo, on_delete=models.CASCADE) chief_inst = models.BooleanField(default=False) @@ -165,11 +205,27 @@ def __str__(self): class Student_attendance(models.Model): - - student_id = models.ForeignKey(Student,on_delete=models.CASCADE) + ''' + Current Purpose : Currently this table stores mapping of a student's attendance for a particular course in a semester - instructor_id = models.ForeignKey(Curriculum_Instructor, on_delete=models.CASCADE) + + + + + ATTRIBUTES : + + student_id(academic_information.Student) - referene to the student for whom the attendance is being recorded + instructor_id(academic_information.Curriculum_Instructor) - reference to the faculty and course + date(DateField) - the date for which the attendance will be recorded + present(Boolean) - to denote whether the student was present or not + + ''' + student_id = models.ForeignKey(Student,on_delete=models.CASCADE) +# course_id = models.ForeignKey(Course) +# attend = models.CharField(max_length=6, choices=Constants.ATTEND_CHOICES) + instructor_id = models.ForeignKey(Curriculum_Instructor, on_delete=models.CASCADE) +# curriculum_id = models.ForeignKey(Curriculum, on_delete=models.CASCADE) date = models.DateField() present = models.BooleanField(default=False) @@ -181,7 +237,18 @@ def __self__(self): class Meeting(models.Model): - + ''' + Current Purpose : stores the information regarding a meeting which was conducted by the academic department + + ATTRIBUTES: + + venue(char) - venue where the meeting will be held(eg:L-103) + date(DateField) - the date on which the meeting was held + time(char) - time at the meeting was held + agenga(text) - the points of discussion for the meeting + minutes_file(char) - the summary of the meeting + + ''' venue = models.CharField(max_length=50) date = models.DateField() time = models.CharField(max_length=20) @@ -196,7 +263,15 @@ def __str__(self): class Calendar(models.Model): - + ''' + Current Purpose : stores the information regarding an academic event(eg : course registrations etc) + + ATTRIBUTES: + + from_date(DateField) - start date of the event + to_date(DateField) - end date of the event + description(Char) - description about the event + ''' from_date = models.DateField() to_date = models.DateField() description = models.CharField(max_length=40) @@ -209,7 +284,14 @@ def __str__(self): class Holiday(models.Model): - + ''' + Current Purpose : stores the information regarding a holiday + + ATTRIBUTES + holiday_date(DateField) - the date of the holidar + holiday_name(char) - to denote the name of the holiday(eg : Republic Day) + holiday_type - to store whether the holiday is restricted or closed + ''' holiday_date = models.DateField() holiday_name = models.CharField(max_length=40) holiday_type = models.CharField(default='restricted', max_length=30, @@ -223,7 +305,14 @@ def __str__(self): class Grades(models.Model): - + ''' + Current Purpose : stores the information regarding the gradees of a student for a course in a semester + + ATTRIBUTES + student_id(academic_information.Student) - Reference to the student for whom the grades is about to be stores + curriculum_id(academic_information.Curriculum) - Reference to the course for which the graded are to be given + grade(Char) - denotes whether the grade given to the student is verified or not + ''' student_id = models.ForeignKey(Student,on_delete=models.CASCADE) curriculum_id = models.ForeignKey(Curriculum,on_delete=models.CASCADE) grade = models.CharField(max_length=4) @@ -234,7 +323,14 @@ class Meta: class Spi(models.Model): - + ''' + Current Purpose : stores the information regarding the SPI of a student for a semester + + ATTRIBUTES + sem(Integer) - the semester to which the spi refers to + student_id(academic_information.Student) - the student for whom the spi is stored + spi(float) - the spi value achieved + ''' sem = models.IntegerField() student_id = models.ForeignKey(Student, on_delete=models.CASCADE) spi = models.FloatField(default=0) @@ -248,7 +344,18 @@ def __self__(self): class Timetable(models.Model): - + ''' + Current Purpose : stores the class-schedule timetable for a batch (eg CSE -2019-Btech) + + ATTRIBUTES + + upload_date(DateTime) - stores the upload date and time + time_table(File) - the file which contains the timetable + batch(Integer) - stores the batch for which the timetable is valid + programme(Char) - stores the programme for which the time table is valid + branch(char) - the branch for which the time table is valid + ''' + upload_date = models.DateTimeField(auto_now_add=True) time_table = models.FileField(upload_to='Administrator/academic_information/') batch = models.IntegerField(default="2016") @@ -259,7 +366,17 @@ class Meta: class Exam_timetable(models.Model): - + ''' + Current Purpose : stores the exam-schedule timetable for a batch (2019-Btech) + + ATTRIBUTES + + upload_date(DateTime) - stores the upload date and time + exam_time_table(File) - the file which contains the timetable + batch(Integer) - stores the batch for which the timetable is valid + programme(Char) - stores the programme for which the time table is valid + + ''' upload_date = models.DateField(auto_now_add=True) exam_time_table = models.FileField(upload_to='Administrator/academic_information/') batch = models.IntegerField(default="2016") diff --git a/FusionIIIT/applications/academic_information/urls.py b/FusionIIIT/applications/academic_information/urls.py index 3e8c2eecb..ea984242e 100755 --- a/FusionIIIT/applications/academic_information/urls.py +++ b/FusionIIIT/applications/academic_information/urls.py @@ -43,5 +43,4 @@ url(r'^api/',include('applications.academic_information.api.urls')), url(r'^view_all_student_data', views.view_all_student_data, name='view_all_student_data'), url(r'^generateStudentSheet$',views.generatestudentxlsheet, name = 'generatestudentxlsheet'), - url(r'^course_allocated_students$',views.get_excel, name = 'course_allocated_students'), ] diff --git a/FusionIIIT/applications/academic_information/utils.py b/FusionIIIT/applications/academic_information/utils.py deleted file mode 100644 index 729c4342b..000000000 --- a/FusionIIIT/applications/academic_information/utils.py +++ /dev/null @@ -1,185 +0,0 @@ -from applications.academic_information.models import (Calendar, Student,Curriculum_Instructor, Curriculum, - Student_attendance) -from ..academic_procedures.models import (BranchChange, CoursesMtech, FinalRegistrations, InitialRegistration, StudentRegistrationChecks, - Register, Thesis, FinalRegistration, ThesisTopicProcess, - Constants, FeePayments, TeachingCreditRegistration, SemesterMarks, - MarkSubmissionCheck, Dues,AssistantshipClaim, MTechGraduateSeminarReport, - PhDProgressExamination,CourseRequested, course_registration, MessDue, Assistantship_status , backlog_course,) - -from applications.programme_curriculum.models import(Course,CourseSlot,Batch,Semester) -from django.http import HttpResponse, JsonResponse -from django.utils import timezone -from django.core import serializers -from django.db.models import Q -import datetime -import random -from django.db import transaction -time = timezone.now() -def check_for_registration_complete(batch, sem, year, programme_type): - date = datetime.date.today() - try: - pre_registration_date = Calendar.objects.filter(description=f"Pre Registration {sem} {year}").first() - if not pre_registration_date: - return {"status": -3, "message": "No such registration found"} - - prd_start_date = pre_registration_date.from_date - prd_end_date = pre_registration_date.to_date - - if date < prd_start_date: - return {"status": -2, "message": "Registration didn't start"} - - if prd_start_date <= date <= prd_end_date: - return {"status": -1, "message": "Registration is under process"} - - if FinalRegistration.objects.filter(Q(semester_id__semester_no = sem) & Q(student_id__batch = batch) & Q(student_id__batch_id__curriculum__programme__category=programme_type)).exists() : - return {"status": 2, "message": "Courses already allocated"} - - return {"status": 1, "message": "Courses not yet allocated"} - - except Exception as e: - return {"status": -3, "message": f"Internal Server Error: {str(e)}"} - -@transaction.atomic -def random_algo(batch,sem,year,course_slot, programme_type) : - unique_course = InitialRegistration.objects.filter(Q(semester_id__semester_no = sem) & Q( course_slot_id__name = course_slot ) & Q(student_id__batch = batch) & Q(student_id__batch_id__curriculum__programme__category=programme_type)).values_list('course_id',flat=True).distinct() - max_seats={} - seats_alloted = {} - present_priority = {} - next_priority = {} - total_seats = 0 - for course in unique_course : - max_seats[course] = Course.objects.get(id=course).max_seats - total_seats+=max_seats[course] - seats_alloted[course] = 0 - present_priority[course] = [] - next_priority[course] = [] - - priority_1 = InitialRegistration.objects.filter(Q(semester_id__semester_no = sem) & Q( course_slot_id__name = course_slot ) & Q(student_id__batch = batch) & Q(priority=1) & Q(student_id__batch_id__curriculum__programme__category=programme_type)) - rem=len(priority_1) - if rem > total_seats : - return -1 - - for p in priority_1 : - present_priority[p.course_id.id].append([p.student_id.id.id,p.course_slot_id.id]) - with transaction.atomic() : - p_priority = 1 - while rem > 0 : - for course in present_priority : - while(len(present_priority[course])) : - random_student_selected = random.choice(present_priority[course]) - - present_priority[course].remove(random_student_selected) - - if seats_alloted[course] < max_seats[course] : - stud = Student.objects.get(id__id = random_student_selected[0]) - curriculum_object = Student.objects.get(id__id = random_student_selected[0]).batch_id.curriculum - course_object = Course.objects.get(id=course) - course_slot_object = CourseSlot.objects.get(id = random_student_selected[1]) - semester_object = Semester.objects.get(Q(semester_no = sem) & Q(curriculum = curriculum_object)) - FinalRegistration.objects.create( - student_id = stud, - verified=False, - semester_id = semester_object, - course_id = course_object, - course_slot_id = course_slot_object - ) - seats_alloted[course] += 1 - rem-=1 - else : - next = InitialRegistration.objects.get(Q(student_id__id__id = random_student_selected[0]) & Q( course_slot_id__name = course_slot ) & Q(semester_id__semester_no = sem) & Q(student_id__batch = batch) & Q(priority=p_priority+1) & Q(student_id__batch_id__curriculum__programme__category=programme_type)) - next_priority[next.course_id.id].append([next.student_id.id.id,next.course_slot_id.id]) - p_priority+=1 - present_priority = next_priority - next_priority = {course : [] for course in unique_course} - - return 1 - -@transaction.atomic -def allocate(request): - batch = request.POST.get('batch') - sem = request.POST.get('sem') - year = request.POST.get('year') - programme_type = request.POST.get('programme_type') - - unique_course_slot = InitialRegistration.objects.filter( - Q(semester_id__semester_no=sem) & Q(student_id__batch=batch) & Q(student_id__batch_id__curriculum__programme__category=programme_type) - ).values('course_slot_id').distinct() - - unique_course_name = [] - - try: - with transaction.atomic(): - for entry in unique_course_slot: - course_slot_object = CourseSlot.objects.get(id=entry['course_slot_id']) - - if course_slot_object.type != "Open Elective": - students = InitialRegistration.objects.filter( - Q(semester_id__semester_no=sem) & - Q(course_slot_id=course_slot_object) & - Q(student_id__batch=batch) & Q(student_id__batch_id__curriculum__programme__category=programme_type) - ).values_list('student_id', flat=True) - - for student_id in students: - student = Student.objects.get(id=student_id) - semester = Semester.objects.get( - semester_no=sem, curriculum=student.batch_id.curriculum - ) - - regis = InitialRegistration.objects.filter( - course_slot_id_id=course_slot_object, - student_id_id=student_id - ).values_list('registration_type', flat=True).first() - - course_id = InitialRegistration.objects.filter( - course_slot_id_id=course_slot_object, - student_id_id=student_id - ).values_list('course_id', flat=True).first() - - course = Course.objects.get(id=course_id) - - prev_registration_id = InitialRegistration.objects.filter( - course_slot_id_id=course_slot_object, - student_id_id=student_id - ).values_list('old_course_registration', flat=True).first() - if (prev_registration_id): - prev_registration_id = course_registration.objects.get(id=prev_registration_id) - - FinalRegistration.objects.create( - student_id=student, - verified=False, - semester_id=semester, - course_id=course, - course_slot_id=course_slot_object, - registration_type=regis, - old_course_registration=prev_registration_id - ) - print(f"{course}") - - unique_course_name.append(course_slot_object.name) - - elif course_slot_object.type == "Open Elective": - if course_slot_object.name not in unique_course_name: - stat = random_algo(batch, sem, year, course_slot_object.name, programme_type) - unique_course_name.append(course_slot_object.name) - if stat == -1: - raise Exception(f"Seats not enough for course_slot {course_slot_object.name}") - - return JsonResponse({'status': 1, 'message': "Course allocation successful"}) - - except: - return JsonResponse({'status': -1, 'message': "Seats not enough for some course_slot"}) - -def view_alloted_course(request) : - batch = request.POST.get('batch') - sem = request.POST.get('sem') - verified = request.POST.get('year') - course = request.POST.get('course') - - registrations = FinalRegistration.objects.filter(Q(student_id__batch = batch) & Q(semester_id__semester_no = sem) & Q(course_id__code = course)) - return_list = [] - for registration in registrations: - obj = { - 'student':registration.student_id.id.id - } - return_list.append(obj) - return JsonResponse({'status':1 , 'student_list':return_list }) \ No newline at end of file diff --git a/FusionIIIT/applications/academic_information/views.py b/FusionIIIT/applications/academic_information/views.py index 1081e550d..22210a52e 100755 --- a/FusionIIIT/applications/academic_information/views.py +++ b/FusionIIIT/applications/academic_information/views.py @@ -16,23 +16,21 @@ from django.views.decorators.csrf import csrf_exempt from django.template.loader import render_to_string from django.contrib.auth.decorators import login_required -from django.contrib import messages -from applications.academic_procedures.models import MinimumCredits, Register, InitialRegistration, course_registration, AssistantshipClaim,Assistantship_status,FinalRegistration, StudentRegistrationChecks +from applications.academic_procedures.models import MinimumCredits, Register, InitialRegistration, course_registration, AssistantshipClaim,Assistantship_status from applications.globals.models import (Designation, ExtraInfo, HoldsDesignation, DepartmentInfo) -from .forms import AcademicTimetableForm, ExamTimetableForm, MinuteForm, PreRegistrationSearchForm +from .forms import AcademicTimetableForm, ExamTimetableForm, MinuteForm from .models import (Calendar, Course, Exam_timetable, Grades, Curriculum_Instructor,Constants, Meeting, Student, Student_attendance, Timetable,Curriculum) -from applications.programme_curriculum.models import (CourseSlot, Course as Courses, Batch, Semester, Programme, Discipline, CourseInstructor) +from applications.programme_curriculum.models import (CourseSlot, Course as Courses, Batch, Semester, Programme, Discipline) -from applications.academic_procedures.views import acad_proced_global_context , get_sem_courses +from applications.academic_procedures.views import acad_proced_global_context from applications.programme_curriculum.models import Batch -from django.db.models import Q -from .utils import check_for_registration_complete,allocate,view_alloted_course + @login_required @@ -109,8 +107,9 @@ def get_context(request): # course_type = Constants.COURSE_TYPE # timetable = Timetable.objects.all() # exam_t = Exam_timetable.objects.all() + procedures_context = acad_proced_global_context() - notifs = request.user.notifications.all() + try: examTtForm = ExamTimetableForm() acadTtForm = AcademicTimetableForm() @@ -130,7 +129,6 @@ def get_context(request): assistant_flag ="" hod_flag = "" account_flag = "" - PreRegistrationsrchform = PreRegistrationSearchForm() for obj in assis_stat: assistant_flag = obj.student_status @@ -178,9 +176,7 @@ def get_context(request): 'batch_branch_data' : procedures_context['batch_branch_data'], 'assistant_flag' : assistant_flag, 'hod_flag' : hod_flag, - 'account_flag' : account_flag, - 'notifications': notifs, - 'preregistrationsrchform': PreRegistrationsrchform, + 'account_flag' : account_flag } return context @@ -218,50 +214,8 @@ def homepage(request): """ if user_check(request): return HttpResponseRedirect('/academic-procedures/') - + context = get_context(request) - - if request.method == "POST": - if 'check_allocation' in request.POST : - return JsonResponse(check_for_registration_complete(request.POST.get('batch'), request.POST.get('sem'), request.POST.get('year'))) - if 'start_allocation' in request.POST : - return allocate(request) - if 'view_allocation' in request.POST : - return view_alloted_course(request) - if 'search_preregistration' in request.POST or 'delete_preregistration' in request.POST: - form = PreRegistrationSearchForm(request.POST) - if form.is_valid(): - roll_no = form.cleaned_data['roll_no'].upper() - semester_no = form.cleaned_data['semester_no'] - print(roll_no, semester_no) - - # Fetch student object by roll number - # student = get_object_or_404(Student, id=roll_no) - - # Fetch semester by semester number - # semester = get_object_or_404(Semester, semester_no=semester_no) - # print(f"Student -> {student}") - - # Search for all initial registrations and student registration check - initial_registrations = InitialRegistration.objects.filter( - student_id_id=roll_no, semester_id__semester_no=semester_no - ) - student_registration_check = StudentRegistrationChecks.objects.filter( - student_id_id=roll_no, semester_id__semester_no=semester_no - ).first() - if ('delete_preregistration' in request.POST): - print(initial_registrations, student_registration_check) - try: - initial_registrations.delete() - student_registration_check.delete() - messages.success(request, "Student's pre registration data successfully deleted.") - except: - messages.error(request, "An error occured while deleting.") - context['delete_preregistration'] = True - else : - context['initial_registrations'] = initial_registrations - context['student_registration_check'] = student_registration_check - context['delete_preregistration'] = True return render(request, "ais/ais.html", context) @@ -860,156 +814,107 @@ def sem_for_generate_sheet(): @login_required def generatexlsheet(request): + """ + to generate Course List of Registered Students + + @param: + request - contains metadata about the requested page + + @variables: + batch - gets the batch + course - gets the course + curr_key - gets the curriculum from database + obj - get stdents data from database + ans - Formatted Array to be converted to xlsx + k -temporary array to add data to formatted array/variable + output - io Bytes object to write to xlsx file + book - workbook of xlsx file + title - formatting variable of title the workbook + subtitle - formatting variable of subtitle the workbook + normaltext - formatting variable for normal text + sheet - xlsx sheet to be rendered + titletext - formatting variable of title text + dep - temporary variables + z - temporary variables for final output + b - temporary variables for final output + c - temporary variables for final output + st - temporary variables for final output + """ if user_check(request): return HttpResponseRedirect('/academic-procedures/') - + try: - course_id = int(request.POST['course']) - academic_year = request.POST.get('academic_year', '') - semester_type = request.POST.get('semester_type', '') - - if not (academic_year and semester_type): - return HttpResponse("Missing academic_year or semester_type", status=400) - - start_year = int(academic_year.split('-')[0]) - course = Courses.objects.get(id=course_id) - - # Get all instructors - instructor_objs = CourseInstructor.objects.filter( - course_id=course_id, - year=start_year, - semester_type=semester_type - ) - - instructor_names = [] - for inst in instructor_objs: - if hasattr(inst.instructor_id, 'user'): - name = f"{inst.instructor_id.user.first_name} {inst.instructor_id.user.last_name}" - instructor_names.append(name) - course_instructor_name = ", ".join(instructor_names) if instructor_names else "" - - # Get registered students - registered_courses = course_registration.objects.filter( - Q(course_id=course), - Q(working_year=start_year), - Q(semester_type=semester_type), - Q(student_id__finalregistration__verified=True) - ) - + batch = request.POST['batch'] + course = Courses.objects.get(id = request.POST['course']) + obj = course_registration.objects.all().filter(course_id = course) except Exception as e: - print(str(e)) - return HttpResponse("Invalid data or internal error", status=500) - + batch="" + course="" + curr_key="" + obj="" + + registered_courses = [] + for i in obj: + if i.student_id.batch_id.year == int(batch): + registered_courses.append(i) ans = [] - student_ids = set() - for reg in registered_courses: - sid = reg.student_id.id.id - if sid not in student_ids: - student_ids.add(sid) - first_name = reg.student_id.id.user.first_name - last_name = reg.student_id.id.user.last_name - department = 'CSE' # You can fetch this dynamically if needed - email = reg.student_id.id.user.email - ans.append([sid, first_name, last_name, department, email]) - ans.sort(key=lambda x: x[0]) - - # Create Excel + for i in registered_courses: + k = [] + k.append(i.student_id.id.id) + k.append(i.student_id.id.user.first_name) + k.append(i.student_id.id.user.last_name) + k.append(i.student_id.id.department) + ans.append(k) + ans.sort() output = BytesIO() - book = Workbook(output, {'in_memory': True}) - - big_title_format = book.add_format({ - 'bold': True, 'font_size': 9, 'font_color': 'black', - 'align': 'right', 'valign': 'vcenter', 'bg_color': '#FFFFFF', - }) - subtitle_format = book.add_format({ - 'bold': True, 'font_size': 12, 'align': 'center', - 'valign': 'vcenter', 'bg_color': '#FFFFFF', 'border': 1 - }) - header_format = book.add_format({ - 'bold': True, 'font_size': 11, 'align': 'center', - 'valign': 'vcenter', 'bg_color': '#E5E4E2', 'border': 1 - }) - normaltext = book.add_format({ - 'bold': False, 'font_size': 11, 'align': 'center', - 'valign': 'vcenter', 'border': 1 - }) - smalltext_format = book.add_format({ - 'bold': False, 'font_size': 10, 'align': 'left', - 'valign': 'vcenter' - }) - bold_key_format = book.add_format({ - 'bold': True, 'font_size': 10, 'align': 'left', - 'valign': 'vcenter' - }) + book = Workbook(output,{'in_memory':True}) + title = book.add_format({'bold': True, + 'font_size': 22, + 'align': 'center', + 'valign': 'vcenter'}) + subtitle = book.add_format({'bold': True, + 'font_size': 15, + 'align': 'center', + 'valign': 'vcenter'}) + normaltext = book.add_format({'bold': False, + 'font_size': 15, + 'align': 'center', + 'valign': 'vcenter'}) sheet = book.add_worksheet() - sheet.set_column('A:A', 12) - sheet.set_column('B:B', 10) - sheet.set_column('C:C', 30) - sheet.set_column('D:D', 10) - sheet.set_column('E:E', 25) - sheet.set_column('F:F', 15) - - sheet.set_row(0, 25) - sheet.set_row(1, 20) - sheet.set_row(2, 15) - sheet.set_row(3, 15) - sheet.set_row(4, 15) - sheet.set_row(5, 20) - - # Headers - sheet.merge_range('A1:F1', - "PDPM INDIAN INSTITUTE OF INFORMATION TECHNOLOGY, DESIGN AND MANUFACTURING JABALPUR", - big_title_format - ) - sheet.merge_range('A2:F2', f"{semester_type.upper()}, {academic_year}", subtitle_format) - - sheet.write('A3', "Course No:", bold_key_format) - sheet.merge_range('B3:F3', f"{course.code}", smalltext_format) - - sheet.write('A4', "Course Title:", bold_key_format) - sheet.merge_range('B4:F4', f"{course.name}", smalltext_format) - - sheet.write('A5', "Instructor(s):", bold_key_format) - sheet.merge_range('B5:F5', f"{course_instructor_name}", smalltext_format) - - # Table Headers - sheet.write_string('A6', "Sl. No", header_format) - sheet.write_string('B6', "Roll No", header_format) - sheet.write_string('C6', "Name", header_format) - sheet.write_string('D6', "Discipline", header_format) - sheet.write_string('E6', "Email", header_format) - sheet.write_string('F6', "Signature", header_format) - - # Table Body - row = 6 - sno = 1 - for student in ans: - sheet.set_row(row, 30) - roll_no = str(student[0]) - full_name = f"{student[1]} {student[2]}" - discipline = student[3] - email = student[4] - - sheet.write_number(row, 0, sno, normaltext) - sheet.write_string(row, 1, roll_no, normaltext) - sheet.write_string(row, 2, full_name, normaltext) - sheet.write_string(row, 3, discipline, normaltext) - sheet.write_string(row, 4, email, normaltext) - sheet.write_string(row, 5, '', normaltext) - sno += 1 - row += 1 - - sheet.set_landscape() - sheet.set_paper(9) - sheet.fit_to_pages(1, 1) + title_text = ((str(course.name)+" : "+str(str(batch)))) + sheet.set_default_row(25) + + sheet.merge_range('A2:E2', title_text, title) + sheet.write_string('A3',"Sl. No",subtitle) + sheet.write_string('B3',"Roll No",subtitle) + sheet.write_string('C3',"Name",subtitle) + sheet.write_string('D3',"Discipline",subtitle) + sheet.write_string('E3','Signature',subtitle) + sheet.set_column('A:A',20) + sheet.set_column('B:B',20) + sheet.set_column('C:C',60) + sheet.set_column('D:D',15) + sheet.set_column('E:E',30) + k = 4 + num = 1 + for i in ans: + sheet.write_number('A'+str(k),num,normaltext) + num+=1 + z,b,c = str(i[0]),i[1],i[2] + name = str(b)+" "+str(c) + temp = str(i[3]).split() + dep = str(temp[len(temp)-1]) + sheet.write_string('B'+str(k),z,normaltext) + sheet.write_string('C'+str(k),name,normaltext) + sheet.write_string('D'+str(k),dep,normaltext) + k+=1 book.close() output.seek(0) - - response = HttpResponse(output.read(), content_type='application/vnd.ms-excel') - filename = f"{course.code}_CourseList.xlsx" - response['Content-Disposition'] = f'attachment; filename="{filename}"' + response = HttpResponse(output.read(),content_type = 'application/vnd.ms-excel') + st = 'attachment; filename = ' + course.code + '.xlsx' + response['Content-Disposition'] = st return response @@ -1124,14 +1029,9 @@ def generate_preregistration_report(request): max_width = max(max_width,len(choices_of_current_student)) for choice in range(1,len(choices_of_current_student)+1): - try: - current_choice = InitialRegistration.objects.get(student_id=student, semester_id__semester_no=sem, course_slot_id=slot, priority=choice) - z.append(str(current_choice.course_id.code) + "-" + str(current_choice.course_id.name)) - except : - z.append("No registration found") - # current_choice = InitialRegistration.objects.get(student_id=student, semester_id__semester_no=sem,course_slot_id = slot,priority = choice) - # # #print("current choice is ",current_choice) - # z.append(str(current_choice.course_id.code)+"-"+str(current_choice.course_id.name)) + current_choice = InitialRegistration.objects.get(student_id=student, semester_id__semester_no=sem,course_slot_id = slot,priority = choice) + # #print("current choice is ",current_choice) + z.append(str(current_choice.course_id.code)+"-"+str(current_choice.course_id.name)) data.append(z) m+=1 @@ -1221,52 +1121,7 @@ def generate_preregistration_report(request): st = 'attachment; filename = ' + batch.name + batch.discipline.acronym + str(batch.year) + '-preresgistration.xlsx' response['Content-Disposition'] = st return response - -@login_required -def get_excel(request): - batch = request.POST.get('batch-check-view') - sem = request.POST.get('semester-check-view') - year = request.POST.get('year-check-view') - course = request.POST.get('Course-check-view') - registrations = FinalRegistration.objects.filter(Q(student_id__batch = batch) & Q(semester_id__semester_no = sem) & Q(course_id__code = course)) - return_list = [] - for registration in registrations: - return_list.append(registration.student_id.id.id) - - return_list.sort() - output = BytesIO() - book = Workbook(output,{'in_memory':True}) - title = book.add_format({'bold': True, - 'font_size': 22, - 'align': 'center', - 'valign': 'vcenter'}) - subtitle = book.add_format({'bold': True, - 'font_size': 15, - 'align': 'center', - 'valign': 'vcenter'}) - normaltext = book.add_format({'bold': False, - 'font_size': 15, - 'align': 'center', - 'valign': 'vcenter'}) - sheet = book.add_worksheet() - sheet.set_default_row(25) - sheet.write_string('A1','Student Roll no',subtitle) - sheet.write_string('B1','Student name',subtitle) - k=2 - for no in return_list : - student= User.objects.get(username=no) - sheet.write_string('A'+str(k),no,normaltext) - sheet.write_string('B'+str(k),student.first_name+student.last_name,normaltext) - k+=1 - - book.close() - output.seek(0) - - response = HttpResponse(output.read(),content_type = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') - response['Content-Disposition'] = 'attachment; filename='+ course +'_student_list.xlsx' - response['Content-Transfer-Encoding'] = 'binary' - return response @login_required def add_new_profile (request): @@ -1314,9 +1169,9 @@ def add_new_profile (request): } if request.method == 'POST' and request.FILES: profiles=request.FILES['profiles'] - excel = xlrd.open_workbook(profiles.name,file_contents=profiles.read()) + excel = xlrd.open_workbook(file_contents=profiles.read()) sheet=excel.sheet_by_index(0) - for i in range(1,sheet.nrows): + for i in range(sheet.nrows): roll_no=sheet.cell(i,0).value first_name=str(sheet.cell(i,1).value) last_name=str(sheet.cell(i,2).value) @@ -1336,7 +1191,7 @@ def add_new_profile (request): category="" phone_no=0 address="" - dept=str(sheet.cell(i,11).value) + dept=str(sheet.cell(i,12).value) specialization=str(sheet.cell(i,12).value) hall_no=None @@ -1354,6 +1209,7 @@ def add_new_profile (request): batch_year=request.POST['Batch'] batch = Batch.objects.all().filter(name = programme_name, discipline__acronym = dept, year = batch_year).first() + user = User.objects.create_user( username=roll_no, password='hello123', @@ -1361,7 +1217,6 @@ def add_new_profile (request): last_name=last_name, email=email, ) - einfo = ExtraInfo.objects.create( id=roll_no, @@ -1398,11 +1253,6 @@ def add_new_profile (request): working=user, designation=desig, ) - - user.save() - einfo.save() - stud_data.save() - hold_des.save() sem_id = Semester.objects.get(curriculum = batch.curriculum, semester_no = sem) course_slots = CourseSlot.objects.all().filter(semester = sem_id) @@ -2170,10 +2020,6 @@ def view_all_student_data(request): """ views all the students """ - if request.session.get('currentDesignationSelected') in ["studentacadadmin"]: - return HttpResponseRedirect('/aims/') - - data = [] #students = Student.objects.select_related('batch_id', 'id__user', 'batch_id__discipline', 'id') .filter(batch=2019).order_by('id').all().only('batch', 'id__id', 'id__user', 'programme', 'batch_id__discipline__acronym', 'specialization', 'id__sex', 'category', 'id__phone_no', 'id__date_of_birth', 'id__user__first_name', 'id__user__last_name', 'id__user__email', 'father_name', 'mother_name', 'id__address')[0:20] @@ -2209,23 +2055,22 @@ def view_all_student_data(request): students = Student.objects.select_related('batch_id', 'id__user', 'batch_id__discipline', 'id').filter(**filter_names).order_by('id').all().only('batch', 'id__id', 'id__user', 'programme','pwd_status', 'father_mobile_no', 'mother_mobile_no', 'batch_id__discipline__acronym', 'specialization', 'id__sex', 'category', 'id__phone_no', 'id__date_of_birth', 'id__user__first_name', 'id__user__last_name', 'id__user__email', 'father_name', 'mother_name', 'id__address') for student in students: obj = { - "admissionYear" : student.batch_id.year if student.batch_id else student.batch, + "admissionYear" : student.batch, "RollNo" : student.id.id, "name" : student.id.user.get_full_name(), - "program": student.programme if student.programme else (student.batch_id.name if student.batch_id else ""), + "program": student.programme, "discipline": student.batch_id.discipline.acronym, "specailization": student.specialization, "gender" : student.id.sex, "category": student.category, - # "pwd_status": student.pwd_status, - "pwd_status": False, + "pwd_status": student.pwd_status, "Mobile": student.id.phone_no, "dob" : student.id.date_of_birth, "emailid" : student.id.user.email, "father_name": student.father_name, - # "father_mobile_no": student.father_mobile_no, + "father_mobile_no": student.father_mobile_no, "mother_name": student.mother_name, - # "mother_mobile_no": student.mother_mobile_no, + "mother_mobile_no": student.mother_mobile_no, "address": student.id.address } data.append(obj) @@ -2295,9 +2140,9 @@ def generatestudentxlsheet(request): data = None else: if(request_rollno != ""): - students = Student.objects.select_related('batch_id', 'id__user', 'batch_id__discipline', 'id').filter(id = request_rollno).only('batch', 'id__id', 'id__user', 'programme', 'batch_id__discipline__acronym', 'specialization', 'id__sex', 'category', 'id__phone_no', 'id__date_of_birth', 'id__user__first_name', 'id__user__last_name', 'id__user__email', 'father_name', 'mother_name', 'id__address') + students = Student.objects.select_related('batch_id', 'id__user', 'batch_id__discipline', 'id').filter(id = request_rollno).only('batch', 'id__id', 'id__user', 'programme','pwd_status', 'father_mobile_no', 'mother_mobile_no', 'batch_id__discipline__acronym', 'specialization', 'id__sex', 'category', 'id__phone_no', 'id__date_of_birth', 'id__user__first_name', 'id__user__last_name', 'id__user__email', 'father_name', 'mother_name', 'id__address') else: - students = Student.objects.select_related('batch_id', 'id__user', 'batch_id__discipline', 'id').filter(**filter_names).order_by('id').all().only('batch', 'id__id', 'id__user', 'programme', 'batch_id__discipline__acronym', 'specialization', 'id__sex', 'category', 'id__phone_no', 'id__date_of_birth', 'id__user__first_name', 'id__user__last_name', 'id__user__email', 'father_name', 'mother_name', 'id__address') + students = Student.objects.select_related('batch_id', 'id__user', 'batch_id__discipline', 'id').filter(**filter_names).order_by('id').all().only('batch', 'id__id', 'id__user', 'programme','pwd_status', 'father_mobile_no', 'mother_mobile_no', 'batch_id__discipline__acronym', 'specialization', 'id__sex', 'category', 'id__phone_no', 'id__date_of_birth', 'id__user__first_name', 'id__user__last_name', 'id__user__email', 'father_name', 'mother_name', 'id__address') for i in students: obj = [] obj.append(i.batch) @@ -2308,17 +2153,14 @@ def generatestudentxlsheet(request): obj.append(i.specialization) obj.append(i.id.sex) obj.append(i.category) - #obj.append(i.pwd_status) - obj.append(None) + obj.append(i.pwd_status) obj.append(i.id.phone_no) obj.append(i.id.date_of_birth) obj.append(i.id.user.email) obj.append(i.father_name) - #obj.append(i.father_mobile_no) - obj.append(None) + obj.append(i.father_mobile_no) obj.append(i.mother_name) - # obj.append(i.mother_mobile_no) - obj.append(None) + obj.append(i.mother_mobile_no) obj.append(i.id.address) data.append(obj) data.sort() diff --git a/FusionIIIT/applications/academic_procedures/api/serializers.py b/FusionIIIT/applications/academic_procedures/api/serializers.py index 4da54ada7..e8020d0e0 100644 --- a/FusionIIIT/applications/academic_procedures/api/serializers.py +++ b/FusionIIIT/applications/academic_procedures/api/serializers.py @@ -1,15 +1,13 @@ from rest_framework.authtoken.models import Token from rest_framework import serializers -from applications.academic_procedures.models import (ThesisTopicProcess, InitialRegistrations,InitialRegistration, - FinalRegistration, FinalRegistrations, SemesterMarks, - BranchChange , StudentRegistrationChecks, Semester, backlog_course , CourseSlot , FeePayments , course_registration) - -from applications.programme_curriculum.models import Course +from applications.academic_procedures.models import (ThesisTopicProcess, InitialRegistrations, + FinalRegistrations, SemesterMarks, + BranchChange) from applications.academic_information.api.serializers import (CurriculumInstructorSerializer, - CurriculumSerializer , CourseSerializer , StudentSerializers ) -from applications.globals.api.serializers import (UserSerializer, HoldsDesignationSerializer , ExtraInfoSerializer) + CurriculumSerializer) +from applications.globals.api.serializers import (UserSerializer, HoldsDesignationSerializer) class ThesisTopicProcessSerializer(serializers.ModelSerializer): @@ -29,17 +27,6 @@ class Meta: model = FinalRegistrations fields = ('__all__') -class CourseSlotMinimalSerializer(serializers.ModelSerializer): - class Meta: - model = CourseSlot - fields = '__all__' - -class StudentRegistrationChecksSerializer(serializers.ModelSerializer): - class Meta: - model = StudentRegistrationChecks - fields = '__all__' - - class SemesterMarksSerializer(serializers.ModelSerializer): class Meta: @@ -51,59 +38,3 @@ class BranchChangeSerializer(serializers.ModelSerializer): class Meta: model = BranchChange fields = ('__all__') - -class SemesterSerializer(serializers.ModelSerializer): - - class Meta: - model = Semester - fields = ('__all__') - -class CourseSerializer(serializers.ModelSerializer): - - class Meta: - model = Course - fields = ('__all__') - -class CourseSerializer(serializers.ModelSerializer): - - class Meta: - model = Course - fields = ('__all__') - -class CourseRegistrationSerializer(serializers.ModelSerializer): - course_id = CourseSerializer(read_only=True) - semester_id= SemesterSerializer(read_only=True) - course_id = CourseSerializer(read_only=True) - semester_id= SemesterSerializer(read_only=True) - class Meta: - model = course_registration - fields = ('__all__') - - - -class CourseSlotSerializer(serializers.ModelSerializer): - courses = CourseSerializer(read_only=True, many=True) # Serialize related courses - semester = SemesterSerializer(read_only=True) # Serialize related semester - - courses = CourseSerializer(read_only=True, many=True) # Serialize related courses - semester = SemesterSerializer(read_only=True) # Serialize related semester - - class Meta: - model = CourseSlot - fields = ('__all__') - -class FinalRegistrationSerializer(serializers.ModelSerializer): - course_id = CourseSerializer(read_only=True) - semester_id= SemesterSerializer(read_only=True) - old_course_registration = CourseRegistrationSerializer(read_only=True) - class Meta: - model = FinalRegistration - fields = ('__all__') - -class InitialRegistrationSerializer(serializers.ModelSerializer): - course_id = CourseSerializer(read_only=True) - semester_id= SemesterSerializer(read_only=True) - course_slot_id = CourseSlotMinimalSerializer(read_only=True) - class Meta: - model = InitialRegistration - fields = ('__all__') \ No newline at end of file diff --git a/FusionIIIT/applications/academic_procedures/api/urls.py b/FusionIIIT/applications/academic_procedures/api/urls.py index 7235495c2..3abeace02 100644 --- a/FusionIIIT/applications/academic_procedures/api/urls.py +++ b/FusionIIIT/applications/academic_procedures/api/urls.py @@ -1,122 +1,12 @@ from django.conf.urls import url -from . import views +from . import views urlpatterns = [ - # url(r'^stu/details', views.academic_procedures_student, name='student_procedures'), - # url(r'^stu/pre_registration' , views.student_pre_registration , name = 'pre_registration'), - url(r'^stu/final_registration' , views.final_registration , name = 'final_registration'), - # url(r'^stu/add_one_course/' , views.add_one_course , name = 'add_one_course'), - url(r'^stu/view_registration' , views.student_view_registration , name = 'view_registration'), - url(r'^stu/view_offered_courses' , views.view_offered_courses , name = 'student_view_offered_courses'), - # url(r'^stu/backlog_courses', views.student_backlog_courses , name = 'student_backlog_courses'), - url(r'^stu/add_course/$' , views.add_course , name ='add_course') , - url(r'^stu/add_course_slots/$' , views.get_student_add_course_slots , name ='student_add_course_slots') , - url(r'^stu/add_course_courses/$' , views.get_student_add_courses , name ='student_add_courses') , - # url(r'^stu/drop_course/$' , views.drop_course , name = 'drop_course'), - # url(r'^stu/swayam_add_course/', views.student_swayam_add_course, name = 'student_swayam_add_course'), - # url(r'^stu/replaceCourse' , views.replaceCourse , name = 'replaceCourse') - # url(r'^stu/next_sem_courses/', views.student_next_sem_courses, name = 'student_next_sem_courses'), - # url(r'^stu/current_courseregistration/$', views.current_courseregistration, name='current_courseregistration'), - url(r'^stu/preregistration/$', views.get_preregistration_data, name='get_preregistration_data'), - url(r'^stu/preregistration/submit/$', views.submit_preregistration, name='preregistration-submit'), - url(r'^stu/swayam_courses/$', views.get_swayam_registration_data, name='get_swayam_data'), - url(r'^stu/swayam/submit/$', views.submit_swayam_registration, name='swayam-submit'), - url(r'^stu/next_sem_courses/', views.student_next_sem_courses, name = 'student_next_sem_courses'), - url(r'^stu/current_courseregistration/$', views.course_registration_view, name='current_courseregistration'), - url(r'^stu/finalregistrationpage/$', views.final_registration_page, name='final_registration_page'), - url(r'^fac/academic_procedures_faculty', views.academic_procedures_faculty_api, name='academic_procedures_faculty_api'), - url(r'^stu/registered-slots/$', views.registered_slots, name='registered_slots'), - url(r'^stu/batch-create/$', views.batch_create_requests, name='batch_create_requests'), - url(r'^stu/replacement-requests/$', views.student_list_requests, name='list_requests'), - url(r'^stu/registrations_drop/$', views.student_registrations_for_drop, name='student_registrations_drop'), - url(r'^stu/drop-course/$', views.drop_course, name='drop_course'), - url(r'^stu/drop-requests/$', views.student_list_drop_requests, name='student_list_drop_requests'), - url(r'^stu/add-requests/$', views.student_list_add_requests, name='student_list_add_requests'), - url(r'^stu/calendar/student/$', views.student_calendar_view, name='student-calendar'), - url(r'^stu/course_reg/semesters/$', views.student_registration_semesters_view, name='student-course_reg-semesters'), - url(r'^stu/feedback_questions/$', views.student_questions, name='student-questions'), - url(r'^stu/feedback_submit/$', views.student_submit, name='student-feedback-submit'), - - - - url(r'^acad/change-requests/allocate_all/$', views.allocate_all, name='allocate_all'), - url(r'^acad/view_registrations' , views.acad_view_reigstrations , name='acad_view_registrations'), - url(r'^acad/verify_registration' , views.verify_registration , name='verify_registration'), - url(r'^acad/verify_course/drop/$' , views.dropcourseadmin , name='dropcourseadmin'), - url(r'^acad/verify_course' , views.verify_course , name='verify_course'), - url(r'^acad/get_add_course_slots' , views.get_add_course_slots , name = 'get_add_course_slots' ), - url(r'^acad/get_add_courses' , views.get_add_course_courses , name = 'get_add_course_slots' ), - url(r'^acad/addCourse/', views.acad_add_course, name="acad_add_course"), - url(r'^acad/get_course_list' , views.get_course_list , name = 'get_course_list' ), - url(r'^acad/get_all_courses' , views.get_all_courses , name = 'get_all_courses' ), - # url(r'^acad/gen_roll_list' , views.gen_roll_list , name = 'gen_roll_list' ), - url(r'^acad/student_list/$' , views.student_list , name = 'student_list_with_slash' ), - url(r'^acad/student_list$' , views.student_list , name = 'student_list_without_slash' ), - url(r'^acad/course_list' , views.course_list , name = 'course_list' ), - url(r'^acad/configure_pre_registration' , views.configure_pre_registration_date , name = 'configure_pre_registration'), - url(r'^acad/configure_final_registration' , views.configure_final_registration_date , name = 'configure_final_registration'), - # url(r'^acad/add_course_to_slot' , views.add_course_to_slot , name = 'add_course_to_slot'), - # url(r'^acad/remove_course_from_slot' , views.remove_course_from_slot , name = 'remove_course_from_slot'), - url(r'^acad/search_preregistration' , views.search_preregistration , name = 'search_preregistration'), - url(r'^acad/delete_preregistration' , views.delete_preregistration , name = 'delete_preregistration'), - url(r'^acad/allot_courses' , views.allot_courses , name = 'allot_courses'), - url(r'^acad/change-requests/allocate_all/$', views.allocate_all, name='allocate_all'), - url(r'^acad/replacement-requests/$', views.admin_list_requests, name='admin_list_requests'), - url(r'^acad/replacement-requests/revert/$', views.revert_replacement_to_pending, name='revert_replacement_to_pending'), - url(r'^acad/replacement-requests/delete/$', views.delete_replacement_requests, name='delete_replacement_requests'), - url(r'^acad/drop-requests/$', views.admin_list_drop_requests, name='admin_list_drop_requests'), - url(r'^acad/drop-requests/approve/$', views.approve_drop_requests, name='approve_drop_requests'), - url(r'^acad/drop-requests/delete/$', views.delete_drop_requests, name='delete_drop_requests'), - url(r'^acad/add-requests/$', views.admin_list_add_requests, name='admin_list_add_requests'), - url(r'^acad/add-requests/approve/$', views.approve_add_requests, name='approve_add_requests'), - url(r'^acad/add-requests/delete/$', views.delete_add_requests, name='delete_add_requests'), - url(r'^acad/student-search/$',views.student_search,name='student-search'), - url(r"^acad/feedback_courses/$", views.admin_course_list, name="admin-course-list"), - url(r"^acad/stats/all/$", views.admin_all_stats, name="admin-all-stats"), - url(r'^acad/batch_change/batches/$', views.list_batches, name='batch-list'), - url(r'^acad/batch_change/students/$', views.list_students_in_batch, name='batch-students'), - url(r'^acad/batch_change/apply/$', views.apply_batch_changes, name='batch-apply'), - url(r'^acad/promote/students/$', views.list_students_in_batch_semester_promotion, name='promote-batch-students'), - url(r'^acad/promote/apply/$', views.apply_promotion, name='promote-apply'), - url(r'^get_next_sem_courses' , views.get_next_sem_courses , name= 'get_next_sem_courses'), - - - url(r'^fac/view_assigned_courses' , views.faculty_assigned_courses , name = 'faculty_assigned_courses'), - # url(r'^fac/get_roll_list' , views.fetch_roll_list , name = 'fetch_roll_list'), - url(r"^inst/courses/$", views.inst_courses, name="inst-courses"), - url(r"^inst/stats/all/$", views.inst_all_stats, name="inst-all-stats"), - - - - url(r'^get_user_info' , views.get_user_info , name = 'get_user_info'), - - # these urls were designed previously and are not working any more - - # url(r'^fac/', views.academic_procedures_faculty, name='faculty_procedures'), - # url(r'^stu', views.academic_procedures_student, name='student_procedures'), - # url(r'^addThesis/', views.add_thesis, name='add_thesis'), - # url(r'^approve_thesis/(?P[0-9]+)/', views.approve_thesis, name='approve_thesis') - url(r'^upload-excel_relacement' , views.upload_excel_replacement , name = 'upload_excel_replacement'), - - - url(r'^api/tas/$', views.tas_list, name='tas_list'), - url(r'^api/faculties/$', views.faculties_list, name='faculties_list'), - - #pg urls - url(r'^hod/students/$', views.hod_students), - url(r'^hod/assign/$', views.hod_assign_manual), - url(r'^hod/assign/upload-excel/$', views.hod_upload_excel), - url(r'^hod/pending/$', views.hod_pending), - url(r'^hod/approved/$', views.hod_approved), - url(r'^hod/approve/(?P\d+)/$', views.hod_approve), - # pg Faculty - url(r'^faculty/assignments/$', views.faculty_assignments), - url(r'^faculty/pending/$', views.faculty_pending), - url(r'^faculty/approved/$', views.faculty_approved), - url(r'^faculty/approve/(?P\d+)/$', views.faculty_approve), + url(r'^fac/', views.academic_procedures_faculty, name='faculty_procedures'), + url(r'^stu/', views.academic_procedures_student, name='student_procedures'), + url(r'^addThesis/', views.add_thesis, name='add_thesis'), + url(r'^approve_thesis/(?P[0-9]+)/', views.approve_thesis, name='approve_thesis'), - # pg TA - url(r'^ta/stipends/$', views.ta_stipends), -] \ No newline at end of file +] diff --git a/FusionIIIT/applications/academic_procedures/api/views.py b/FusionIIIT/applications/academic_procedures/api/views.py index b8b33368d..aab6deaa7 100644 --- a/FusionIIIT/applications/academic_procedures/api/views.py +++ b/FusionIIIT/applications/academic_procedures/api/views.py @@ -1,60 +1,29 @@ import datetime -import random -import logging -from collections import defaultdict, deque, OrderedDict -from functools import wraps -from datetime import date -from django.utils import timezone -logger = logging.getLogger(__name__) + from django.contrib.auth import get_user_model from django.shortcuts import get_object_or_404, redirect -from django.db import transaction -from django.db.models import Prefetch -from django.db.models.functions import Concat,ExtractYear,ExtractMonth,ExtractDay,Cast -from django.db.models import Max,Value,IntegerField,CharField,F,Sum, Case, When -from io import BytesIO -import json -import xlrd -from xlsxwriter.workbook import Workbook -from django.db.models import Prefetch -import json -from django.views.decorators.csrf import csrf_exempt -from django.http import JsonResponse, HttpResponse + from rest_framework.permissions import IsAuthenticated from rest_framework.authentication import TokenAuthentication from rest_framework import status from rest_framework.decorators import api_view, permission_classes,authentication_classes from rest_framework.permissions import AllowAny from rest_framework.response import Response -import pandas as pd -from rest_framework.decorators import ( - api_view, parser_classes, permission_classes -) -from rest_framework.parsers import MultiPartParser, FormParser -from applications.globals.models import Faculty, HoldsDesignation, Designation, ExtraInfo -from applications.programme_curriculum.models import ( CourseInstructor, CourseSlot, Course as Courses, Batch, Semester) -# from applications.programme_curriculum.models import Course +from applications.academic_information.models import Curriculum +from applications.academic_procedures.models import ThesisTopicProcess +from applications.globals.models import HoldsDesignation, Designation, ExtraInfo +from applications.programme_curriculum.models import (CourseSlot, Course as Courses, Batch, Semester) -from applications.academic_procedures.models import ( MTechGraduateSeminarReport, PhDProgressExamination, Student, Curriculum , ThesisTopicProcess, InitialRegistrations, - FinalRegistration, SemesterMarks,backlog_course, - BranchChange , StudentRegistrationChecks, Semester , FeePayments , course_registration, course_replacement, AssistantshipClaim, Assignment, StipendRequest, CourseReplacementRequest, CourseDropRequest, CourseAddRequest, BatchChangeHistory, FeedbackQuestion, FeedbackResponse, FeedbackFilled, FeedbackOption) - -from applications.academic_information.models import (Curriculum_Instructor , Calendar) -from applications.online_cms.models import Student_grades from applications.academic_procedures.views import (get_user_semester, get_acad_year, get_currently_registered_courses, get_current_credits, get_branch_courses, Constants, get_faculty_list, get_registration_courses, get_add_course_options, + get_pre_registration_eligibility, get_final_registration_eligibility, - get_add_or_drop_course_date_eligibility, - get_detailed_sem_courses, - InitialRegistration) - -from applications.academic_procedures.views import get_sem_courses, get_student_registrtion_check, get_cpi, academics_module_notif, get_final_registration_choices, get_currently_registered_course, get_add_course_options, get_drop_course_options, get_replace_course_options -from applications.examination.api.views import parse_academic_year + get_add_or_drop_course_date_eligibility) from . import serializers @@ -62,4838 +31,368 @@ date_time = datetime.datetime.now() -def make_label(no: int, sem_type: str) -> str: - """ - - odd → "Semester " - - even & Even Semester → "Semester " - - even & Summer Semester → "Summer " - """ - if no % 2 == 1: - return f"Semester {no}" - if sem_type == "Summer Semester": - return f"Summer {no // 2}" - return f"Semester {no}" - - -def get_semester_type(semester): - """ - Returns the semester type string. - """ - if semester % 2 == 1: - return "Odd Semester" - elif semester % 2 == 0: - return "Even Semester" - else: - return "Summer Semester" - - -def generate_current_session(current_year, semester) : - """ - Returns a tuple of (session, semester_type). - """ - semester_type = get_semester_type(semester) - - if semester_type == "Odd Semester": - session = f"{current_year}-{str(current_year + 1)[-2:]}" - else: # Even or Summer - session = f"{current_year - 1}-{str(current_year)[-2:]}" - - return session, semester_type - - -def generate_next_session(current_year, next_semester) : - """ - Returns a tuple of (session, semester_type) for student's next semester. - """ - semester_type = get_semester_type(next_semester) - - if semester_type == "Odd Semester" or semester_type == "Even Semester": - session = f"{current_year}-{str(current_year + 1)[-2:]}" - else: - session = f"{current_year - 1}-{str(current_year)[-2:]}" - - return session, semester_type - - -def role_required(allowed_roles): - """ - Decorator factory that accepts a list of allowed role names. - Accepts multiple HoldsDesignation records per user. - """ - allowed_lower = {role.lower() for role in allowed_roles} - - def decorator(view_func): - @wraps(view_func) - def _wrapped_view(request, *args, **kwargs): - # Fetch all designations for this user - user_roles = ( - HoldsDesignation.objects - .select_related('designation') - .filter(user=request.user) - .values_list('designation__name', flat=True) # or whichever field holds the string - ) - - # Normalize to lowercase for comparison - user_roles_lower = {r.lower() for r in user_roles} - - # Check intersection - if not (user_roles_lower & allowed_lower): - return Response( - {"error": "Permission denied: one of %s required" % allowed_roles}, - status=status.HTTP_403_FORBIDDEN - ) - - return view_func(request, *args, **kwargs) - return _wrapped_view - return decorator - - - -#--------------------------------------- APIs of student---------------------------------------------------------- - -demo_date = timezone.now() - -@api_view(['GET']) -@permission_classes([IsAuthenticated]) -@role_required(['acadadmin']) -def get_all_courses(request): - try: - obj = Courses.objects.all() - serializer = serializers.CourseSerializer(obj, many=True).data - - return Response(serializer, status=status.HTTP_200_OK) - except Exception as e: - return Response(data = str(e) , status=status.HTTP_500_INTERNAL_SERVER_ERROR) - -# @api_view(['POST']) -# @role_required(['acadadmin']) -# def gen_roll_list(request): -# try: -# batch = request.data['batch'] -# course_id = request.data['course'] -# course = Courses.objects.get(id = course_id) -# #obj = course_registration.objects.all().filter(course_id = course) -# obj=course_registration.objects.filter(course_id__id=course_id, student_id__batch=batch).select_related( -# 'student_id__id__user','student_id__id__department').only('student_id__batch', -# 'student_id__id__user__first_name', 'student_id__id__user__last_name', -# 'student_id__id__department__name','student_id__id__user__username') -# except Exception as e: -# batch="" -# course="" -# obj="" -# students = [] -# for i in obj: -# students.append({"rollno":i.student_id.id.user.username, -# "name":i.student_id.id.user.first_name+" "+i.student_id.id.user.last_name, -# "department":i.student_id.id.department.name}) -# # {'students': students, 'batch':batch, 'course':course_id} -# return JsonResponse({'students': students, 'batch':batch, 'course':course_id}, status=200) - - -# api for student for adding courses -# @api_view(['POST']) -# @permission_classes([IsAuthenticated]) -# def add_course(request): -# try: -# current_user = request.user -# current_user = ExtraInfo.objects.all().filter(user=current_user).first() -# current_user = Student.objects.all().filter(id=current_user.id).first() - -# sem_id_instance = Semester.objects.get(id = request.data['semester']) - -# count = request.data['ct'] -# count = int(count) -# reg_curr = [] - -# for i in range(1, count+1): -# choice = "choice["+str(i)+"]" -# slot = "slot["+str(i)+"]" -# try: -# course_id_instance = Courses.objects.get(id = request.data[choice]) -# courseslot_id_instance = CourseSlot.objects.get(id = request.data[slot]) - -# print(courseslot_id_instance.max_registration_limit) -# if course_registration.objects.filter(working_year = current_user.batch_id.year, course_id = course_id_instance).count() < courseslot_id_instance.max_registration_limit and (course_registration.objects.filter(course_id=course_id_instance, student_id=current_user).count() == 0): -# print("space left = True") -# p = course_registration( -# course_id=course_id_instance, -# student_id=current_user, -# course_slot_id=courseslot_id_instance, -# semester_id=sem_id_instance -# ) -# print(serializers.course_registration(p)) -# if p not in reg_curr: -# reg_curr.append(p) -# else: -# print("already exist") -# except Exception as e: -# error_message = str(e) -# resp = {'message': 'Course addition failed', 'error': error_message} -# return Response(resp, status=status.HTTP_400_BAD_REQUEST) -# print(reg_curr) -# course_registration_data = course_registration.objects.bulk_create(reg_curr) -# course_registration_data = serializers.CourseRegistrationSerializer(course_registration_data , many = True).data -# res = {'message' : 'Courses successfully added' , "courses_added" : course_registration_data } -# return Response(data = res , status = status.HTTP_200_OK) -# except Exception as e: -# print(e) -# return Response(data = str(e) , status= status.HTTP_500_INTERNAL_SERVER_ERROR) - -# API for student to add BL courses -@api_view(['POST']) -@permission_classes([IsAuthenticated]) -@authentication_classes([TokenAuthentication]) -@role_required(['student']) -def add_course(request): - try: - student = Student.objects.select_related('batch_id__curriculum').get( - id__user=request.user - ) - - eligibility_resp = get_add_registration_eligibility( - timezone.now().date(), - student.curr_semester_no, - datetime.datetime.now().year - ) - if isinstance(eligibility_resp, JsonResponse): - return eligibility_resp - - course = Courses.objects.get(id=request.data.get('course_id')) - slot = CourseSlot.objects.get(id=request.data.get('slot_id')) - - if not slot.name.startswith('BL'): - return Response({ - 'error': f'Only BL slots allowed. "{slot.name}" is not a BL slot.' - }, status=status.HTTP_400_BAD_REQUEST) - - backlog_grades = ['F', 'X', 'CD'] - improvement_grades = ['C', 'D+', 'D'] - allowed_grades = backlog_grades + improvement_grades - - student_grade = Student_grades.objects.filter( - roll_no=request.user.username, - course_id=course - ).order_by('-year', '-semester').first() - - if not student_grade: - return Response({ - 'error': 'You can only register for BL courses if you have a grade below C+ in this course. No grade record found.' - }, status=status.HTTP_400_BAD_REQUEST) - - if student_grade.grade not in allowed_grades: - return Response({ - 'error': f'You can only register for BL courses with grades below C+. Your grade: {student_grade.grade}' - }, status=status.HTTP_400_BAD_REQUEST) - - current_year = datetime.datetime.now().year - session, semester_type = generate_current_session(current_year, student.curr_semester_no) - - old_course_reg = course_registration.objects.filter( - student_id=student, - course_id=course - ).order_by('-working_year', '-semester_id__semester_no').first() - - existing_request = CourseAddRequest.objects.filter( - student=student, - course=course, - course_slot=slot, - academic_year=session, - semester_type=semester_type, - status__in=['Pending', 'Approved'] - ).first() - - if existing_request: - return Response({ - 'error': f'You already have a {existing_request.status.lower()} request for this slot in the current semester.' - }, status=status.HTTP_400_BAD_REQUEST) - - try: - add_request = CourseAddRequest.objects.create( - student=student, - course=course, - course_slot=slot, - academic_year=session, - semester_type=semester_type, - old_course_registration=old_course_reg, - status='Pending' - ) - except Exception as create_error: - import logging - logger = logging.getLogger(__name__) - error_msg = str(create_error) - if 'duplicate' in error_msg.lower() or 'unique constraint' in error_msg.lower(): - return Response({ - 'error': 'You have already submitted a request for this slot in the current semester.' - }, status=status.HTTP_400_BAD_REQUEST) - else: - logger.error(f"Error creating add course request: {error_msg}", exc_info=True) - return Response({ - 'error': 'Failed to submit course add request. Please try again or contact support.' - }, status=status.HTTP_500_INTERNAL_SERVER_ERROR) - - return Response({ - 'message': 'Course add request submitted successfully. Awaiting Academic approval.', - 'data': { - 'id': add_request.id, - 'course': course.code, - 'course_name': course.name, - 'slot': slot.name, - 'status': add_request.status, - 'created_at': add_request.created_at.isoformat() - } - }, status=status.HTTP_201_CREATED) - - except Student.DoesNotExist: - return Response({'error': 'Student not found'}, status=status.HTTP_404_NOT_FOUND) - except Courses.DoesNotExist: - return Response({'error': 'Course not found'}, status=status.HTTP_404_NOT_FOUND) - except CourseSlot.DoesNotExist: - return Response({'error': 'Course slot not found'}, status=status.HTTP_404_NOT_FOUND) - except Exception as e: - return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) - - -# API to get available course slots for student (only BL slots) -@api_view(['GET']) -@permission_classes([IsAuthenticated]) -@authentication_classes([TokenAuthentication]) -@role_required(['student']) -def get_student_add_course_slots(request): - try: - current_user = request.user - extra_info = ExtraInfo.objects.filter(user=current_user).first() - - if not extra_info: - return Response({ - 'error': 'User information not found' - }, status=status.HTTP_400_BAD_REQUEST) - - student = Student.objects.filter(id=extra_info.id).first() - - if not student: - return Response({ - 'error': 'Student information not found' - }, status=status.HTTP_400_BAD_REQUEST) - - eligibility_resp = get_add_registration_eligibility( - timezone.now().date(), - student.curr_semester_no, - datetime.datetime.now().year - ) - if isinstance(eligibility_resp, JsonResponse): - return eligibility_resp - - batch = student.batch_id - if not batch or not batch.curriculum: - return Response({ - 'error': 'Student batch or curriculum not found' - }, status=status.HTTP_400_BAD_REQUEST) - - current_semester = Semester.objects.filter( - curriculum=batch.curriculum, - semester_no=student.curr_semester_no - ).first() - - if not current_semester: - return Response({ - 'error': 'Current semester not found' - }, status=status.HTTP_404_NOT_FOUND) - - bl_slots = CourseSlot.objects.filter( - semester=current_semester, - name__startswith='BL' - ) - - registered_slots = course_registration.objects.filter( - student_id=student, - semester_id=current_semester - ).values_list('course_slot_id', flat=True) - - current_year = datetime.datetime.now().year - academic_year, semester_type = generate_current_session(current_year, student.curr_semester_no) - - pending_request_slots = CourseAddRequest.objects.filter( - student=student, - academic_year=academic_year, - semester_type=semester_type, - status='Pending' - ).values_list('course_slot_id', flat=True) - - available_slots = [] - - for slot in bl_slots: - if slot.id not in registered_slots and slot.id not in pending_request_slots: - course_count = slot.courses.count() - - available_slots.append({ - 'id': slot.id, - 'name': slot.name, - 'type': slot.type, - 'max_registration_limit': slot.max_registration_limit, - 'academic_year': academic_year, - 'semester_type': semester_type, - 'semester_no': student.curr_semester_no, - 'course_count': course_count - }) - - return Response(available_slots, status=status.HTTP_200_OK) - - except Exception as e: - return Response({ - 'error': str(e) - }, status=status.HTTP_500_INTERNAL_SERVER_ERROR) - - -# API to get available courses for a specific slot (only BL slots) @api_view(['GET']) -@permission_classes([IsAuthenticated]) -@authentication_classes([TokenAuthentication]) -@role_required(['student']) -def get_student_add_courses(request): - try: - current_user = request.user - extra_info = ExtraInfo.objects.filter(user=current_user).first() - - if not extra_info: - return Response({ - 'error': 'User information not found' - }, status=status.HTTP_400_BAD_REQUEST) - - student = Student.objects.filter(id=extra_info.id).first() - - if not student: - return Response({ - 'error': 'Student information not found' - }, status=status.HTTP_400_BAD_REQUEST) - - batch = student.batch_id - if not batch or not batch.curriculum: - return Response({ - 'error': 'Student batch or curriculum not found' - }, status=status.HTTP_400_BAD_REQUEST) - - curriculum = batch.curriculum - current_sem_no = student.curr_semester_no - - current_semester = Semester.objects.filter( - curriculum=curriculum, - semester_no=current_sem_no - ).first() - - if not current_semester: - return Response({ - 'error': 'Current semester not found' - }, status=status.HTTP_404_NOT_FOUND) - - slot_id = request.query_params.get('slot_id') - - if slot_id: - bl_slots = CourseSlot.objects.filter( - id=slot_id, - semester=current_semester, - name__startswith='BL' - ) +def academic_procedures_faculty(request): + current_user = request.user + user_details = current_user.extrainfo + des = current_user.holds_designations.all().first() + + if str(des.designation) == 'student': + return Response({'error':'Not a faculty'}, status=status.HTTP_400_BAD_REQUEST) + elif str(current_user) == 'acadadmin': + return Response({'error':'User is acadadmin'}, status=status.HTTP_400_BAD_REQUEST) + + elif str(des.designation) == "Associate Professor" or str(des.designation) == "Professor" or str(des.designation) == "Assistant Professor": + faculty_object = user_details.faculty + month = int(date_time.month) + sem = [] + if month>=7 and month<=12: + sem = [1,3,5,7] else: - bl_slots = CourseSlot.objects.filter( - semester=current_semester, - name__startswith='BL' - ) - - courses_list = [] - - for slot in bl_slots: - courses = slot.courses.all() - - for course in courses: - already_registered = course_registration.objects.filter( - course_id=course, - student_id=student - ).exists() - - courses_list.append({ - 'id': course.id, - 'code': course.code, - 'name': course.name, - 'credit': course.credit, - 'slot': slot.name, - 'slot_id': slot.id, - 'already_registered': already_registered - }) - - return Response(courses_list, status=status.HTTP_200_OK) - - except Exception as e: - return Response({ - 'error': str(e) - }, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + sem = [2,4,6,8] + student_flag = False + fac_flag = True + thesis_supervision_request_list = faculty_object.thesistopicprocess_supervisor.all() + thesis_supervision_request_list_data = serializers.ThesisTopicProcessSerializer(thesis_supervision_request_list, many=True).data + approved_thesis_request_list = serializers.ThesisTopicProcessSerializer(thesis_supervision_request_list.filter(approval_supervisor = True), many=True).data + pending_thesis_request_list = serializers.ThesisTopicProcessSerializer(thesis_supervision_request_list.filter(pending_supervisor = True), many=True).data + courses_list = serializers.CurriculumInstructorSerializer(user_details.curriculum_instructor_set.all(), many=True).data + fac_details = serializers.UserSerializer(current_user).data -# @api_view(['GET']) -# @permission_classes([IsAuthenticated]) -# def drop_course(request): -# if not request.user.is_authenticated: -# return Response({'message': 'Login required '}, status=status.HTTP_400_BAD_REQUEST) -# data = request.GET.get('id') -# reg_id = int(data) -# current_user = request.user -# current_user = ExtraInfo.objects.all().filter(user=current_user).first() -# current_user = Student.objects.all().filter(id = current_user.id).first() -# try: -# course_registration.objects.filter(id = reg_id, student_id = current_user).delete() -# except Exception as e: -# resp = {"message" : "Course drop failed", "error" : str(e)} -# return Response(data = resp, status = status.HTTP_400_BAD_REQUEST) - -# resp = {"message" : "Course successfully dropped"} -# return Response(data = resp , status = status.HTTP_200_OK) - - -# @api_view(['POST']) -# @permission_classes([IsAuthenticated]) -# def student_swayam_add_course(request): -# if not request.user.is_authenticated: -# return JsonResponse({'message': 'Login required '}, status=401) -# try: -# current_user = request.user -# current_user = ExtraInfo.objects.all().filter(user=current_user).first() -# current_user = Student.objects.all().filter(id = current_user.id).first() -# course_id = request.POST["course_id"] -# courseslot_id = request.POST["courseslot_id"] -# registration_type = request.POST["registration_type"] -# if (not course_id) or (not courseslot_id) or (not registration_type): -# return JsonResponse({'message': 'Enter Complete Form Details '}, status=400) -# course = Courses.objects.get(id=course_id) -# courseslot = CourseSlot.objects.get(id=courseslot_id) -# semester_no = current_user.curr_semester_no -# curr_id = current_user.batch_id.curriculum -# semester = Semester.objects.get(curriculum = curr_id, semester_no = semester_no) -# try: -# course_registration.objects.get(course_slot_id = courseslot, student_id = current_user) -# return JsonResponse({'message': 'already registered a course in course slot'}, status=400) -# except: -# pass -# try: -# course_registration.objects.get(course_id = course, student_id = current_user) -# return JsonResponse({'message': 'already registered a particular course'}, status=400) -# except: -# pass -# cr = course_registration( -# course_slot_id=courseslot, course_id=course, student_id=current_user, semester_id=semester , working_year = datetime.datetime.now().year, registration_type=registration_type) -# cr.save() -# return JsonResponse({'message': 'Successfully added swayam course' }, status=200) -# except Exception as e: -# print(str(e)) -# return JsonResponse({'message': 'Error adding course '}, status=500) - + resp = { + 'student_flag' : student_flag, + 'fac_flag' : fac_flag, + 'thesis_supervision_request_list' : thesis_supervision_request_list_data, + 'pending_thesis_request_list' : pending_thesis_request_list, + 'approved_thesis_request_list' : approved_thesis_request_list, + 'courses_list': courses_list, + 'faculty': fac_details + } + return Response(data=resp, status=status.HTTP_200_OK) -# simple api for getting to know the details of user who have logined in the system @api_view(['GET']) -@permission_classes([IsAuthenticated]) -def get_user_info(request): +def academic_procedures_student(request): current_user = request.user - details1 = serializers.UserSerializer(current_user).data - details2 = serializers.ExtraInfoSerializer(current_user.extrainfo).data - details = { - "user_serializer_Data" : details1, - "ExtraInfoSerializer_Data" : details2 + current_user_data = { + 'first_name': current_user.first_name, + 'last_name': current_user.last_name, + 'username': current_user.username, + 'email': current_user.email } - return Response(data = details , status= status.HTTP_200_OK) - - -# with this api student can see the list of courses offered to him in upcoming semester -@api_view(['POST']) -@permission_classes([IsAuthenticated]) -@role_required(['student']) -def view_offered_courses(request): - try : - obj = Curriculum.objects.filter( - programme = request.data['programme'], - branch = request.data['branch'], - batch = request.data["batch"], - sem = request.data["semester"] - ) - serializer = serializers.CurriculumSerializer(obj, many=True).data - return Response(serializer, status=status.HTTP_200_OK) - except Exception as e: - return Response(data = str(e) , status=status.HTTP_500_INTERNAL_SERVER_ERROR) - # try: - # ug_flag = True - # masters_flag = False - # phd_flag = False - # current_semester = get_user_semester(request.user, ug_flag, masters_flag, phd_flag) - # current_year = date_time.date().year - - # return Response(data= { } , status=status.HTTP_200_OK) - # except Exception as e: - # return Response(data = {"error" : str(e)} , status= status.HTTP_500_INTERNAL_SERVER_ERROR) - - -# with this student can know status of pre registration and final registration -@api_view(['GET']) -@permission_classes([IsAuthenticated]) -@role_required(['student']) -def student_view_registration(request): - try: - # getting the registration status of current user for the given semester - current_user = request.user - student_id = current_user.extrainfo.id - - sem_id = Semester.objects.get(id = request.data.get('semester')) - sem_id = serializers.SemesterSerializer(sem_id).data["id"] - - # filter based on the semester id and student id - obj = StudentRegistrationChecks.objects.filter(semester_id_id = sem_id, student_id = student_id) - - # serialize the data for displaying - serializer = serializers.StudentRegistrationChecksSerializer(obj, many=True).data - - return Response(serializer, status=status.HTTP_200_OK) - except Exception as e: - return Response(data = str(e) , status=status.HTTP_500_INTERNAL_SERVER_ERROR) - - -# with this student can do his pre registration for the upcoming semester -# @api_view(['POST']) -# @transaction.atomic -# def student_pre_registration(request): -# try: -# current_user = request.user -# current_user_id = serializers.UserSerializer(current_user).data["id"] -# s_id = current_user.extrainfo.id - -# current_user = ExtraInfo.objects.all().select_related('user','department').filter(user=current_user_id).first() -# current_user = serializers.ExtraInfoSerializer(current_user).data - -# current_user_instance = Student.objects.all().filter(id=current_user["id"]).first() -# current_user = serializers.StudentSerializers(current_user_instance).data - -# sem_id_instance = Semester.objects.get(id = request.data.get('semester')) -# sem_id = serializers.SemesterSerializer(sem_id_instance).data["id"] - -# # filter based on the semester id and student id -# obj = StudentRegistrationChecks.objects.filter(semester_id_id = sem_id, student_id = s_id) -# # serialize the data for displaying -# student_registration_check = serializers.StudentRegistrationChecksSerializer(obj, many=True).data - -# try: -# # check if user have already done pre registration -# if(student_registration_check and student_registration_check[0]["pre_registration_flag"] ): -# return Response(data = {"message" : "You have already registered for this semester" }, status=status.HTTP_400_BAD_REQUEST) -# except Exception as e: -# return Response(data = str(e) , status=status.HTTP_500_INTERNAL_SERVER_ERROR) - -# course_slots=request.data.get("course_slot") -# reg_curr = [] - - -# for course_slot in course_slots : -# course_priorities = request.data.get("course_priority-"+course_slot) -# if(course_priorities[0] == 'NULL'): -# print("NULL FOUND") -# continue -# course_slot_id_for_model = CourseSlot.objects.get(id = int(course_slot)) - -# # return Response(data = course_slots , status=status.HTTP_200_OK) -# for course_priority in course_priorities: -# priority_of_current_course,course_id = map(int,course_priority.split("-")) -# # get course id for the model -# course_id_for_model = Courses.objects.get(id = course_id) -# print("check") -# p = InitialRegistration( -# course_id = course_id_for_model, -# semester_id = sem_id_instance, -# student_id = current_user_instance, -# course_slot_id = course_slot_id_for_model, -# priority = priority_of_current_course -# ) -# p.save() -# reg_curr.append(p) - - -# try: -# serialized_reg_curr = serializers.InitialRegistrationSerializer(reg_curr, many=True).data - -# registration_check = StudentRegistrationChecks( -# student_id = current_user_instance, -# pre_registration_flag = True, -# final_registration_flag = False, -# semester_id = sem_id_instance -# ) -# registration_check.save() -# return Response(data={"message": "Successfully Registered for the courses.", "registrations": serialized_reg_curr}, status=status.HTTP_200_OK) -# except Exception as e: -# return Response(data = {"message" : "Error in Registration." , "error" : str(e)} , status=status.HTTP_500_INTERNAL_SERVER_ERROR) -# except Exception as e: -# return Response(data = {"message" : "Error in Registration." , "error" : str(e)} , status=status.HTTP_500_INTERNAL_SERVER_ERROR) - -@api_view(['POST']) -@permission_classes([IsAuthenticated]) -@role_required(['student']) -def final_registration(request): - try: - with transaction.atomic(): - current_user = request.user - extra_info = current_user.extrainfo - student = Student.objects.filter(id=extra_info).first() - - sem_id = Semester.objects.get(id=request.data.get('semester')) - - mode = str(request.data.get('mode')) - transaction_id = str(request.data.get('transaction_id')) - deposit_date = request.data.get('deposit_date') - utr_number = str(request.data.get('utr_number')) - fee_paid = request.data.get('fee_paid') - actual_fee = request.data.get('actual_fee') - reason = str(request.data.get('reason')) or None # Handle empty string - fee_receipt = request.FILES['fee_receipt'] - # Save FeePayments object - obj = FeePayments( - student_id=student, - semester_id=sem_id, - mode=mode, - transaction_id=transaction_id, - deposit_date=deposit_date, - utr_number=utr_number, - fee_paid=fee_paid, - actual_fee=actual_fee, - reason=reason, - fee_receipt=fee_receipt - ) - obj.save() - - # Update StudentRegistrationChecks - StudentRegistrationChecks.objects.filter( - student_id=student, - semester_id=sem_id - ).update(final_registration_flag=True) - - return JsonResponse({'message': 'Final Registration Successful'}) - - except Exception as e: - return JsonResponse({'message': f'Final Registration Failed: {str(e)}'}, status=500) - - -# with this student can do his final registration for the upcoming semester -@api_view(['POST']) -@permission_classes([IsAuthenticated]) -@transaction.atomic -def student_final_registration(request): - try: - current_user = request.user - current_user_id = serializers.UserSerializer(current_user).data["id"] - s_id = current_user.extrainfo.id - - current_user = ExtraInfo.objects.all().select_related('user','department').filter(user=current_user).first() - current_user = serializers.ExtraInfoSerializer(current_user).data - - current_user_instance = Student.objects.all().filter(id=current_user["id"]).first() - current_user = serializers.StudentSerializers(current_user_instance).data + user_details = current_user.extrainfo + des = current_user.holds_designations.all().first() + if str(des.designation) == 'student': + obj = user_details.student + + if obj.programme.upper() == "PH.D": + student_flag = True + ug_flag = False + masters_flag = False + phd_flag = True + fac_flag = False + des_flag = False + + elif obj.programme.upper() == "M.DES": + student_flag = True + ug_flag = False + masters_flag = True + phd_flag = False + fac_flag = False + des_flag = True + + elif obj.programme.upper() == "B.DES": + student_flag = True + ug_flag = True + masters_flag = False + phd_flag = False + fac_flag = False + des_flag = True + + elif obj.programme.upper() == "M.TECH": + student_flag = True + ug_flag = False + masters_flag = True + phd_flag = False + fac_flag = False + des_flag = False + + elif obj.programme.upper() == "B.TECH": + student_flag = True + ug_flag = True + masters_flag = False + phd_flag = False + fac_flag = False + des_flag = False - # these details we need from the body of the request fot doing final registration - sem_id_instance = Semester.objects.get(id = request.data.get('semester')) - sem_id = serializers.SemesterSerializer(sem_id_instance).data["id"] - registration_status = StudentRegistrationChecks.objects.filter(student_id = current_user["id"], semester_id = sem_id) - registration_status = serializers.StudentRegistrationChecksSerializer(registration_status , many = True ).data - - if(len(registration_status)>0 and registration_status[0]["pre_registration_flag"] == False): - return Response(data = {"message" : "Student haven't done pre registration yet."} , status= status.HTTP_400_BAD_REQUEST ) - mode = str(request.data.get('mode')) - transaction_id = str(request.data.get('transaction_id')) - deposit_date = request.data.get('deposit_date') - utr_number = str(request.data.get('utr_number')) - fee_paid = request.data.get('fee_paid') - actual_fee = request.data.get('actual_fee') - reason = str(request.data.get('reason')) - if reason=="": - reason=None - # fee_receipt = request.FILES['fee_receipt'] + else: + return Response({'message':'Student has no record'}, status=status.HTTP_400_BAD_REQUEST) - # print(fee_receipt) - obj = FeePayments( - student_id = current_user_instance, - semester_id = sem_id_instance, - mode = mode, - transaction_id = transaction_id, - # fee_receipt = fee_receipt, - deposit_date = deposit_date, - utr_number = utr_number, - fee_paid = fee_paid, - actual_fee = actual_fee, - reason = reason - ) - obj.save() + current_date = date_time.date() + current_year = date_time.year + batch = obj.batch_id + user_sem = get_user_semester(request.user, ug_flag, masters_flag, phd_flag) + acad_year = get_acad_year(user_sem, current_year) + user_branch = user_details.department.name + cpi = obj.cpi + cur_spi='Sem results not available' # To be fetched from db if result uploaded + + details = { + 'current_user': current_user_data, + 'year': acad_year, + 'user_sem': user_sem, + 'user_branch' : str(user_branch), + 'cpi' : cpi, + 'spi' : cur_spi + } + currently_registered_courses = get_currently_registered_courses(user_details.id, user_sem) + currently_registered_courses_data = serializers.CurriculumSerializer(currently_registered_courses, many=True).data try: - registration_status = StudentRegistrationChecks.objects.filter(student_id = current_user_instance, semester_id = sem_id).update(final_registration_flag = True) - return Response(data = {"message" : "Final Registration Successfull" } , status= status.HTTP_200_OK) - except Exception as e: - return Response(data = {"message" : "Final Registration Failed " , "error" : str(e)} , status = status.HTTP_500_INTERNAL_SERVER_ERROR) - except Exception as e: - return Response(data = {"message" : "Final Registration Failed " , "error" : str(e)} , status=status.HTTP_500_INTERNAL_SERVER_ERROR) - - -# with this api student can get his backlog courses list -# @api_view(['GET']) -# def student_backlog_courses(request): -# try : -# stu_id = Student.objects.select_related('id','id__user','id__department').get(id=request.user.username) -# backlogCourseList = [] -# backlogCourses = backlog_course.objects.select_related('course_id' , 'student_id' , 'semester_id' ).filter(student_id=stu_id) -# for i in backlogCourses: -# obj = { -# "course_id" : i.course_id.id, -# "course_name" : i.course_id.course_name, -# "faculty" : i.course_id.course_details, -# "semester" : i.semester_id.semester_no, -# "is_summer_course" : i.is_summer_course -# } -# backlogCourseList.append(obj) - -# return Response(backlogCourseList, status=status.HTTP_200_OK) -# except Exception as e: -# return Response(data = str(e) , status=status.HTTP_500_INTERNAL_SERVER_ERROR) - - - -#--------------------------------------- APIs of acad person---------------------------------------------------------- + pre_registered_courses = obj.initialregistrations_set.all().filter(semester = user_sem) + pre_registered_courses_show = obj.initialregistrations_set.all().filter(semester = user_sem+1) + except: + pre_registered_courses = None + pre_registered_courses_show=None + try: + final_registered_courses = obj.finalregistrations_set.all().filter(semester = user_sem) + except: + final_registered_courses = None + pre_registered_courses_data = serializers.InitialRegistrationsSerializer(pre_registered_courses, many=True).data + pre_registered_courses_show_data = serializers.InitialRegistrationsSerializer(pre_registered_courses_show, many=True).data + final_registered_courses_data = serializers.FinalRegistrationsSerializer(final_registered_courses, many=True).data -# with this acad admin can fetch the list of courses for any batch , semester and brach -@api_view(['POST']) -@permission_classes([IsAuthenticated]) -@role_required(['acadadmin']) -def get_course_list(request): - - programme = request.data['programme'] - branch = request.data['branch'] - batch = request.data['batch'] + current_credits = get_current_credits(currently_registered_courses) - try : - obj = Curriculum.objects.filter( - programme = request.data['programme'], - branch = request.data['branch'], - batch = request.data["batch"] - ) - serializer = serializers.CurriculumSerializer(obj, many=True).data - return Response(serializer, status=status.HTTP_200_OK) - except Exception as e: - return Response(data = str(e) , status=status.HTTP_500_INTERNAL_SERVER_ERROR) - # obj = Curriculum.objects.filter(curriculum_id_=curriculum_id, course_type_ = course_type, programme_ = programme, batch_ = batch, branch_ = branch, sem_ = sem, optional_ = optional) + next_sem_branch_courses = get_branch_courses(current_user, user_sem+1, user_branch) + next_sem_branch_courses_data = serializers.CurriculumSerializer(next_sem_branch_courses, many=True).data + fee_payment_mode_list = dict(Constants.PaymentMode) -# with this api acad person can see the list of students who have completed their pre and final registrations for any semester -@api_view(['GET']) -@permission_classes([IsAuthenticated]) -@role_required(['acadadmin']) -def acad_view_reigstrations(request): - try: - semester = request.data["semester"] - sem_id_instance = Semester.objects.get(id = request.data.get('semester')) - sem_id = serializers.SemesterSerializer(sem_id_instance).data["id"] - obj = StudentRegistrationChecks.objects.filter(semester_id_id = sem_id, final_registration_flag =True) - student_registration_check = serializers.StudentRegistrationChecksSerializer(obj, many=True).data + next_sem_branch_registration_courses = get_registration_courses(next_sem_branch_courses) + next_sem_branch_registration_courses_data = [] + for choices in next_sem_branch_registration_courses: + next_sem_branch_registration_courses_data.append(serializers.CurriculumSerializer(choices, many=True).data) + # next_sem_branch_registration_courses_data = serializers.CurriculumSerializer(next_sem_branch_registration_courses, many=True).data - return Response(data= student_registration_check , status=status.HTTP_200_OK) - except Exception as e: - return Response(data = {"error" : str(e)} , status= status.HTTP_500_INTERNAL_SERVER_ERROR) + final_registration_choices = get_registration_courses(get_branch_courses(request.user, user_sem, user_branch)) + final_registration_choices_data = [] + for choices in final_registration_choices: + final_registration_choices_data.append(serializers.CurriculumSerializer(choices, many=True).data) + performance_list = [] + result_announced = False + for i in currently_registered_courses: + try: + performance_obj = obj.semestermarks_set.all().filter(curr_id = i).first() + except: + performance_obj = None + performance_list.append(performance_obj) + performance_list_data = serializers.SemesterMarksSerializer(performance_list, many=True).data -# with this api acad person set the date of pre registration date for any semester -@api_view(['POST']) -@permission_classes([IsAuthenticated]) -@role_required(['acadadmin']) -def configure_pre_registration_date(request): - try: - try: - from_date = request.data.get('from_date') - to_date = request.data.get('to_date') - semester = request.data.get('semester') - current_year = date_time.date().year - desc = "Pre Registration " + str(semester) +" " + str(current_year) - from_date = from_date.split('-') - from_date = [int(i) for i in from_date] - from_date = datetime.datetime(*from_date).date() - to_date = to_date.split('-') - to_date = [int(i) for i in to_date] - to_date = datetime.datetime(*to_date).date() - except Exception as e: - from_date="" - to_date="" - desc="" - pass - c = Calendar( - from_date=from_date, - to_date=to_date, - description=desc) - c.save() - return Response(data = {"message" : "Pre registration for semester " + str(semester) + " will be opened from " + str(from_date) + " to " + str(to_date) + ". " , } , status= status.HTTP_200_OK) - except Exception as e: - return Response(data = {"error " : str(e)} , status= status.HTTP_500_INTERNAL_SERVER_ERROR) + thesis_request_list = serializers.ThesisTopicProcessSerializer(obj.thesistopicprocess_set.all(), many=True).data + pre_existing_thesis_flag = True if obj.thesistopicprocess_set.all() else False -# with this api request acad person can set the date of final registration -@api_view(['POST']) -@permission_classes([IsAuthenticated]) -@role_required(['acadadmin']) -def configure_final_registration_date(request): - try: - try: - from_date = request.data.get('from_date') - to_date = request.data.get('to_date') - semester = request.data.get('semester') - current_year = date_time.date().year - desc = "Physical Reporting at the Institute" - from_date = from_date.split('-') - from_date = [int(i) for i in from_date] - from_date = datetime.datetime(*from_date).date() - to_date = to_date.split('-') - to_date = [int(i) for i in to_date] - to_date = datetime.datetime(*to_date).date() - except Exception as e: - from_date="" - to_date="" - desc="" - pass - c = Calendar( - from_date=from_date, - to_date=to_date, - description=desc) - c.save() - return Response(data = {"message" : "Physical Reporting at the Institute will be opened from " + str(from_date) + " to " + str(to_date) + ". " , } , status= status.HTTP_200_OK) - except Exception as e: - return Response(data = {"error " : str(e)} , status= status.HTTP_500_INTERNAL_SERVER_ERROR) + current_sem_branch_courses = get_branch_courses(current_user, user_sem, user_branch) -# with this api request acad person can add any courses in a specific slot -# @api_view(['POST']) -# def add_course_to_slot(request): -# course_code = request.data.get('course_code') -# course_slot_name = request.data.get('course_slot_name') -# try: -# course_slot = CourseSlot.objects.get(name=course_slot_name) -# course = Courses.objects.get(code=course_code) -# course_slot.courses.add(course) - -# return JsonResponse({'message': f'Course {course_code} added to slot {course_slot_name} successfully.'}, status=200) -# except CourseSlot.DoesNotExist: -# return JsonResponse({'error': 'Course slot does not exist.'}, status=400) -# except Courses.DoesNotExist: -# return JsonResponse({'error': 'Course does not exist.'}, status=400) + pre_registration_date_flag = get_pre_registration_eligibility(current_date) + final_registration_date_flag = get_final_registration_eligibility(current_date) -# # with this api request acad person can remove any course from a specific slot -# @api_view(['POST']) -# def remove_course_from_slot(request): -# course_code = request.data.get('course_code') -# course_slot_name = request.data.get('course_slot_name') -# try: -# course_slot = CourseSlot.objects.get(name=course_slot_name) -# course = Courses.objects.get(code=course_code) -# course_slot.courses.remove(course) -# return JsonResponse({'message': f'Course {course_code} removed from slot {course_slot_name} successfully.'}, status=200) -# except CourseSlot.DoesNotExist: -# return JsonResponse({'error': 'Course slot does not exist.'}, status=400) -# except Course.DoesNotExist: -# return JsonResponse({'error': 'Course does not exist.'}, status=400) - + add_or_drop_course_date_flag = get_add_or_drop_course_date_eligibility(current_date) + student_registration_check_pre = obj.studentregistrationcheck_set.all().filter(semester=user_sem+1) + student_registration_check_final = obj.studentregistrationcheck_set.all().filter(semester=user_sem) + pre_registration_flag = False + final_registration_flag = False + if(student_registration_check_pre): + pre_registration_flag = student_registration_check_pre.pre_registration_flag + if(student_registration_check_final): + final_registration_flag = student_registration_check_final.final_registration_flag -#--------------------------------------- APIs of faculty---------------------------------------------------------- + teaching_credit_registration_course = None + if phd_flag: + teaching_credit_registration_course = Curriculum.objects.all().filter(batch = 2016, sem =6) + teaching_credit_registration_course_data = serializers.CurriculumSerializer(teaching_credit_registration_course, many=True).data -# with this api faculty can know what are the courses assigned to him -@api_view(['GET']) -@permission_classes([IsAuthenticated]) -def faculty_assigned_courses(request): - - - try: - current_user = request.user - curriculum_ids = Curriculum_Instructor.objects.filter(instructor_id=current_user.id).values_list('curriculum_id', flat=True) - course_infos = [] - for curriculum_id in curriculum_ids: - course_info = Curriculum.objects.filter(curriculum_id=curriculum_id).values_list('course_code','course_type','programme','branch','sem','course_id_id').first() - # course_infos.append(course_info) - context = { - "course_code": course_info[0], - "course_type": course_info[1], - "programme": course_info[2], - "branch": course_info[3], - "sem": course_info[4], - "course_id": course_info[5] + if student_flag: + try: + due = obj.dues_set.get() + lib_d = due.library_due + pc_d = due.placement_cell_due + hos_d = due.hostel_due + mess_d = due.mess_due + acad_d = due.academic_due + except: + lib_d, pc_d, hos_d, mess_d, acad_d = 0, 0, 0, 0, 0 + + tot_d = lib_d + acad_d + pc_d + hos_d + mess_d + + registers = obj.register_set.all() + course_list = [] + for i in registers: + course_list.append(i.curr_id) + attendence = [] + for i in course_list: + instructors = i.curriculum_instructor_set.all() + pr,ab=0,0 + for j in list(instructors): + + presents = obj.student_attendance_set.all().filter(instructor_id=j, present=True) + absents = obj.student_attendance_set.all().filter(instructor_id=j, present=False) + pr += len(presents) + ab += len(absents) + attendence.append((i,pr,pr+ab)) + attendance_data = {} + for course in attendence: + attendance_data[course[0].course_id.course_name] = { + 'present' : course[1], + 'total' : course[2] } - course_infos.append(context) - - return Response(data= course_infos , status=status.HTTP_200_OK) - except Exception as e: - return Response(data = {"error" : str(e)} , status= status.HTTP_500_INTERNAL_SERVER_ERROR) - - -@api_view(['POST']) -@permission_classes([IsAuthenticated]) -def get_next_sem_courses(request): - try: - next_sem = request.data.get('next_sem') - branch = request.data.get('branch') - programme = request.data.get('programme') - batch = request.data.get('batch') - - # we go to student table and apply filters and get batch_id of the students with these filter - batch_id = Student.objects.filter(programme = programme , batch = batch , specialization = branch)[0].batch_id - curr_id = batch_id.curriculum - next_sem_id = Semester.objects.get(curriculum = curr_id, semester_no = next_sem) - - if next_sem_id: - next_sem_registration_courses = get_detailed_sem_courses(next_sem_id ) - return JsonResponse(next_sem_registration_courses, safe=False) - except Exception as e: - return Response(data = {"error" : str(e)} , status= status.HTTP_500_INTERNAL_SERVER_ERROR) + branchchange_flag = False + if user_sem == 2: + branchchange_flag=True -# @api_view(['POST']) -# @permission_classes([IsAuthenticated]) -# def add_one_course(request): -# try: -# print(request.data) -# current_user = get_object_or_404(User, username=request.data.get('user')) -# current_user = ExtraInfo.objects.all().filter(user=current_user).first() -# current_user = Student.objects.all().filter(id=current_user.id).first() + # faculty_list = serializers.HoldsDesignationSerializer(get_faculty_list(), many=True).data -# sem_id = Semester.objects.get(id=request.data.get('semester')) -# choice = request.data.get('choice') -# slot = request.data.get('slot') - -# try: -# course_id = Courses.objects.get(id=choice) -# courseslot_id = CourseSlot.objects.get(id=slot) -# print(courseslot_id.id) -# print(courseslot_id.type) -# print(courseslot_id.max_registration_limit) -# if course_registration.objects.filter(course_slot_id_id=courseslot_id, student_id=current_user).count() == 1 and courseslot_id.type != "Swayam": -# already_registered_course_id = course_registration.objects.filter(course_slot_id_id=courseslot_id, student_id=current_user)[0].course_id -# print(already_registered_course_id) -# msg = 'Already Registered in the course : ' +already_registered_course_id.code + '-'+ already_registered_course_id.name -# return JsonResponse({'message' : msg}) -# if((course_registration.objects.filter(course_id=course_id, student_id=current_user).count() >= 1)): -# return JsonResponse({'message': 'Already registered in this course!'}, status=200) -# # Check if maximum course registration limit has not been reached -# if course_registration.objects.filter(student_id__batch_id__year=current_user.batch_id.year, course_id=course_id).count() < courseslot_id.max_registration_limit and \ -# (course_registration.objects.filter(course_id=course_id, student_id=current_user).count() == 0): -# p = course_registration( - -# course_id=course_id, -# student_id=current_user, -# course_slot_id=courseslot_id, -# semester_id=sem_id -# ) -# p.save() -# return JsonResponse({'message': 'Course added successfully'}, status=200) -# else: -# return JsonResponse({'message': 'Course not added because seats are full!'}, status=404) -# except Exception as e: -# print(e) -# return JsonResponse({'message': 'Error adding course'}, status=500) -# except Exception as e: -# return JsonResponse({'message': 'Error adding course'}, status=500) - + resp = { + 'details': details, + 'currently_registered': currently_registered_courses_data, + 'pre_registered_courses' : pre_registered_courses_data, + 'pre_registered_courses_show' : pre_registered_courses_show_data, + 'final_registered_courses' : final_registered_courses_data, + 'current_credits' : current_credits, + 'courses_list': next_sem_branch_courses_data, + 'fee_payment_mode_list' : fee_payment_mode_list, + 'next_sem_branch_registration_courses' : next_sem_branch_registration_courses_data, + 'final_registration_choices' : final_registration_choices_data, + 'performance_list' : performance_list_data, + 'thesis_request_list' : thesis_request_list, + 'student_flag' : student_flag, + 'ug_flag' : ug_flag, + 'masters_flag' : masters_flag, + 'phd_flag' : phd_flag, + 'fac_flag' : fac_flag, + 'des_flag' : des_flag, + 'thesis_flag' : pre_existing_thesis_flag, + 'drop_courses_options' : currently_registered_courses_data, + 'pre_registration_date_flag': pre_registration_date_flag, + 'final_registration_date_flag': final_registration_date_flag, + 'add_or_drop_course_date_flag': add_or_drop_course_date_flag, + 'pre_registration_flag' : pre_registration_flag, + 'final_registration_flag': final_registration_flag, + 'teaching_credit_registration_course' : teaching_credit_registration_course_data, + 'lib_d':lib_d, + 'acad_d':acad_d, + 'mess_d':mess_d, + 'pc_d':pc_d, + 'hos_d':hos_d, + 'tot_d':tot_d, + 'attendance': attendance_data, + 'Branch_Change_Flag':branchchange_flag + # 'faculty_list' : faculty_list + } + return Response(data=resp, status=status.HTTP_200_OK) -@transaction.atomic @api_view(['POST']) -@permission_classes([IsAuthenticated]) -@authentication_classes([TokenAuthentication]) -@role_required(['acadadmin']) -def verify_registration(request): - data = json.loads(request.body) - if data.get('status_req') == "accept" : - student_id = data.get('student_id') - student = Student.objects.get(id = student_id) - batch = student.batch_id - curr_id = batch.curriculum - - if(student.curr_semester_no+1 >= 9): - # print('----------------------------------------------------------------' , student.curr_semester_no) - sem_no = 8 +def add_thesis(request): + current_user = request.user + profile = current_user.extrainfo + if profile.user_type == 'student': + if not 'thesis_topic' in request.data: + return Response({'error':'Thesis topic is required'}, status=status.HTTP_400_BAD_REQUEST) + if not 'research_area' in request.data: + return Response({'error':'Research area is required'}, status=status.HTTP_400_BAD_REQUEST) + if 'supervisor_id' in request.data: + try: + supervisor_faculty = User.objects.get(username=request.data['supervisor_id']) + supervisor_faculty = supervisor_faculty.extrainfo + request.data['supervisor_id'] = supervisor_faculty + except: + return Response({'error':'Wrong supervisor id. User does not exist.'}, status=status.HTTP_400_BAD_REQUEST) + else: + return Response({'error':'supervisor id is required'}, status=status.HTTP_400_BAD_REQUEST) + if 'co_supervisor_id' in request.data: + try: + co_supervisor_faculty = User.objects.get(username=request.data['co_supervisor_id']) + co_supervisor_faculty = co_supervisor_faculty.extrainfo + request.data['co_supervisor_id'] = co_supervisor_faculty + except: + return Response({'error':'Wrong co_supervisor id. User does not exist.'}, status=status.HTTP_400_BAD_REQUEST) else: - # print('----------------------------------------------------------------' , student.curr_semester_no) - sem_no = student.curr_semester_no+1 - sem_id = Semester.objects.get(curriculum = curr_id, semester_no = sem_no) - # print('----------------------------------------------------------------' , student.curr_semester_no) - - final_register_list = FinalRegistration.objects.all().filter(student_id = student, verified = False, semester_id = sem_id) - - # final_register_list = FinalRegistration.objects.all().filter(student_id = student, verified = False) - - with transaction.atomic(): - ver_reg = [] - for obj in final_register_list: - p = course_registration( - course_id=obj.course_id, - student_id=student, - semester_id=obj.semester_id, - course_slot_id = obj.course_slot_id, - working_year = datetime.datetime.now().year, - registration_type=obj.registration_type - ) - # ver_reg.append(p) - p.save() - if (obj.old_course_registration): - course_replacement.objects.create(new_course_registration=p, old_course_registration=obj.old_course_registration) - o = FinalRegistration.objects.filter(id= obj.id).update(verified = True) - # course_registration.objects.bulk_create(ver_reg) - academics_module_notif(request.user, student.id.user, 'registration_approved') - - Student.objects.filter(id = student_id).update(curr_semester_no = sem_no) - return JsonResponse({'status': 'success', 'message': 'Successfully Accepted'}) - - elif data.get('status_req') == "reject" : - reject_reason = data.get('reason', '') - student_id = data.get('student_id') - student_id = Student.objects.get(id = student_id) - batch = student_id.batch_id - curr_id = batch.curriculum - if(student_id.curr_semester_no+1 >= 9): - sem_no = 8 + co_supervisor_faculty = None + if 'curr_id' in request.data: + curr_id = None + student = profile.student + request.data['student_id'] = profile + request.data['submission_by_student'] = True + serializer = serializers.ThesisTopicProcessSerializer(data=request.data) + if serializer.is_valid(): + serializer.save() + return Response(serializer.data, status=status.HTTP_200_OK) else: - sem_no = student_id.curr_semester_no+1 - sem_id = Semester.objects.get(curriculum = curr_id, semester_no = sem_no) - with transaction.atomic(): - academicadmin = get_object_or_404(User, username = request.user.username) - # print(sem_id) - # FinalRegistration.objects.filter(student_id = student_id, verified = False, semester_id = sem_id).delete() - # StudentRegistrationChecks.objects.filter(student_id = student_id, semester_id = sem_id).update(final_registration_flag = False) - FeePayments.objects.filter(student_id = student_id, semester_id = sem_id).delete() - academics_module_notif(academicadmin, student_id.id.user, 'Registration Declined - '+reject_reason) - return JsonResponse({'status': 'success', 'message': 'Successfully Rejected'}) - - return JsonResponse({'status': 'error', 'message': 'Error in processing'}) - -@api_view(['POST']) -@authentication_classes([TokenAuthentication]) -@permission_classes([IsAuthenticated]) -@role_required(['acadadmin']) -def verify_course(request): - roll_no = request.data.get("rollno") - if not roll_no: - return Response({"error": "rollno is required"}, status=status.HTTP_400_BAD_REQUEST) - - # Convert to uppercase after null check - roll_no = roll_no.strip().upper() - - # First check main academic tables - student = Student.objects.filter(id_id=roll_no).first() - if not student: - # If not found in main tables, check StudentBatchUpload - try: - from applications.programme_curriculum.models_student_management import StudentBatchUpload - batch_student = StudentBatchUpload.objects.filter(roll_number=roll_no).first() - if batch_student: - if batch_student.reported_status == 'REPORTED': - return Response({ - "error": f"Student {roll_no} has reported but not yet transferred to main academic system. Please contact academic office." - }, status=status.HTTP_400_BAD_REQUEST) - else: - return Response({ - "error": f"Student {roll_no} found in upcoming batches but status is '{batch_student.reported_status}'. Student must report first." - }, status=status.HTTP_400_BAD_REQUEST) - except ImportError: - pass - - return Response({"error": "Student record not found"}, status=status.HTTP_400_BAD_REQUEST) - - - extra = student.id - user_obj = extra.user - - # name & roll for frontend - dict2 = { - "roll_no": roll_no, - "firstname": user_obj.first_name or "", - "lastname": user_obj.last_name or "", - } - - # current curriculum & semester - handle None batch_id case - if not student.batch_id: - return Response({ - "error": f"Student {roll_no} does not have a valid batch assignment. Please contact academic office to complete student setup." - }, status=status.HTTP_400_BAD_REQUEST) - - if not student.batch_id.curriculum: - return Response({ - "error": f"Student {roll_no}'s batch does not have a curriculum assigned. Please contact academic office." - }, status=status.HTTP_400_BAD_REQUEST) - - curr = student.batch_id.curriculum - curr_sem = Semester.objects.filter(curriculum=curr, semester_no=student.curr_semester_no).first() - if not curr_sem: - return Response({"error": "Current semester not found"}, status=status.HTTP_404_NOT_FOUND) - - # gather registered courses for this semester - regs = course_registration.objects.filter(student_id=student).order_by('-semester_id__semester_no') - details = [] - for reg in regs: - slot_course = Courses.objects.filter(id=reg.course_id.id).first() - repl_qs = course_replacement.objects.filter(old_course_registration=reg) - replaced_list = [] - for repl in repl_qs: - nr = repl.new_course_registration - replaced_list.append({ - "course_id": { - "code": nr.course_id.code, - "name": nr.course_id.name, - }, - "semester_id": { - "semester_no": nr.semester_id.semester_no, - }, - }) - - details.append({ - "id": reg.id, - "reg_id": reg.id, - "rid": f"{roll_no} - {reg.course_id.code}", - "course_id": reg.course_id.code, - "course_name": reg.course_id.name, - "sem": reg.semester_id.semester_no, - "semester_type" : reg.semester_type, - "credits": slot_course.credit if slot_course else 0, - "registration_type": reg.registration_type, - "replaced_by": replaced_list, - }) - - # lists for selects (no serializers) - course_list = list(Courses.objects.values("id", "code", "name", "credit")) - semester_list = list( - Semester.objects.filter(curriculum=curr).values("id", "semester_no") - ) - courseslot_list = list( - CourseSlot.objects.filter(semester__in=[s["id"] for s in semester_list]).values("id", "name") - ) - - # academic year & semflag - today = date.today() - year = today.year - semflag = 1 if today.month >= 7 else 2 - yearr = f"{year}-{year+1}" - if today.month >= 7: - current_semester_type = "Odd Semester" + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) else: - current_semester_type = "Even Semester" - - return Response({ - "details": details, - "dict2": dict2, - "course_list": course_list, - "semester_list": semester_list, - "courseslot_list": courseslot_list, - "date": {"year": yearr, "semflag": semflag}, - "current_semester": { - "semester_no": student.curr_semester_no, - "semester_type": current_semester_type - } - }) - - -# These apis were implemented before but now don't use them they have some errors - - -# @api_view(['GET']) -# def academic_procedures_faculty(request): -# current_user = request.user -# user_details = current_user.extrainfo -# des = current_user.holds_designations.all().first() - -# if str(des.designation) == 'student': -# return Response({'error':'Not a faculty'}, status=status.HTTP_400_BAD_REQUEST) -# elif str(current_user) == 'acadadmin': -# return Response({'error':'User is acadadmin'}, status=status.HTTP_400_BAD_REQUEST) - -# elif str(des.designation) == "Associate Professor" or str(des.designation) == "Professor" or str(des.designation) == "Assistant Professor": -# faculty_object = user_details.faculty -# month = int(date_time.month) -# sem = [] -# if month>=7 and month<=12: -# sem = [1,3,5,7] -# else: -# sem = [2,4,6,8] -# student_flag = False -# fac_flag = True - -# thesis_supervision_request_list = faculty_object.thesistopicprocess_supervisor.all() -# thesis_supervision_request_list_data = serializers.ThesisTopicProcessSerializer(thesis_supervision_request_list, many=True).data -# approved_thesis_request_list = serializers.ThesisTopicProcessSerializer(thesis_supervision_request_list.filter(approval_supervisor = True), many=True).data -# pending_thesis_request_list = serializers.ThesisTopicProcessSerializer(thesis_supervision_request_list.filter(pending_supervisor = True), many=True).data -# courses_list = serializers.CurriculumInstructorSerializer(user_details.curriculum_instructor_set.all(), many=True).data -# fac_details = serializers.UserSerializer(current_user).data - -# resp = { -# 'student_flag' : student_flag, -# 'fac_flag' : fac_flag, -# 'thesis_supervision_request_list' : thesis_supervision_request_list_data, -# 'pending_thesis_request_list' : pending_thesis_request_list, -# 'approved_thesis_request_list' : approved_thesis_request_list, -# 'courses_list': courses_list, -# 'faculty': fac_details -# } -# return Response(data=resp, status=status.HTTP_200_OK) - - - - - - -# @api_view(['GET']) -# def academic_procedures_student(request): -# current_user = request.user -# current_user_data = { -# 'first_name': current_user.first_name, -# 'last_name': current_user.last_name, -# 'username': current_user.username, -# 'email': current_user.email -# } -# user_details = current_user.extrainfo -# des = current_user.holds_designations.all().first() -# if str(des.designation) == 'student': -# obj = user_details.student - -# if obj.programme.upper() == "PH.D": -# student_flag = True -# ug_flag = False -# masters_flag = False -# phd_flag = True -# fac_flag = False -# des_flag = False - -# elif obj.programme.upper() == "M.DES": -# student_flag = True -# ug_flag = False -# masters_flag = True -# phd_flag = False -# fac_flag = False -# des_flag = True - -# elif obj.programme.upper() == "B.DES": -# student_flag = True -# ug_flag = True -# masters_flag = False -# phd_flag = False -# fac_flag = False -# des_flag = True - -# elif obj.programme.upper() == "M.TECH": -# student_flag = True -# ug_flag = False -# masters_flag = True -# phd_flag = False -# fac_flag = False -# des_flag = False - -# elif obj.programme.upper() == "B.TECH": -# student_flag = True -# ug_flag = True -# masters_flag = False -# phd_flag = False -# fac_flag = False -# des_flag = False - -# else: -# return Response({'message':'Student has no record'}, status=status.HTTP_400_BAD_REQUEST) - -# current_date = date_time.date() -# current_year = date_time.year -# batch = obj.batch_id -# user_sem = get_user_semester(request.user, ug_flag, masters_flag, phd_flag) -# acad_year = get_acad_year(user_sem, current_year) -# user_branch = user_details.department.name -# cpi = obj.cpi -# cur_spi='Sem results not available' # To be fetched from db if result uploaded - -# details = { -# 'current_user': current_user_data, -# 'year': acad_year, -# 'user_sem': user_sem, -# 'user_branch' : str(user_branch), -# 'cpi' : cpi, -# 'spi' : cur_spi -# } - -# currently_registered_courses = get_currently_registered_courses(user_details.id, user_sem) -# currently_registered_courses_data = serializers.CurriculumSerializer(currently_registered_courses, many=True).data -# try: -# pre_registered_courses = obj.initialregistrations_set.all().filter(semester = user_sem) -# pre_registered_courses_show = obj.initialregistrations_set.all().filter(semester = user_sem+1) -# except: -# pre_registered_courses = None -# pre_registered_courses_show=None -# try: -# final_registered_courses = obj.finalregistrations_set.all().filter(semester = user_sem) -# except: -# final_registered_courses = None - -# pre_registered_courses_data = serializers.InitialRegistrationsSerializer(pre_registered_courses, many=True).data -# pre_registered_courses_show_data = serializers.InitialRegistrationsSerializer(pre_registered_courses_show, many=True).data -# final_registered_courses_data = serializers.FinalRegistrationsSerializer(final_registered_courses, many=True).data - -# current_credits = get_current_credits(currently_registered_courses) -# print(current_user, user_sem+1, user_branch) -# try: -# next_sem_branch_courses = get_branch_courses(current_user, user_sem+1, user_branch) -# except Exception as e: -# return Response(data = str(e)) -# next_sem_branch_courses_data = serializers.CurriculumSerializer(next_sem_branch_courses, many=True).data - -# fee_payment_mode_list = dict(Constants.PaymentMode) - -# next_sem_branch_registration_courses = get_registration_courses(next_sem_branch_courses) -# next_sem_branch_registration_courses_data = [] -# for choices in next_sem_branch_registration_courses: -# next_sem_branch_registration_courses_data.append(serializers.CurriculumSerializer(choices, many=True).data) -# # next_sem_branch_registration_courses_data = serializers.CurriculumSerializer(next_sem_branch_registration_courses, many=True).data - -# final_registration_choices = get_registration_courses(get_branch_courses(request.user, user_sem, user_branch)) -# final_registration_choices_data = [] -# for choices in final_registration_choices: -# final_registration_choices_data.append(serializers.CurriculumSerializer(choices, many=True).data) - -# performance_list = [] -# result_announced = False -# for i in currently_registered_courses: -# try: -# performance_obj = obj.semestermarks_set.all().filter(curr_id = i).first() -# except: -# performance_obj = None -# performance_list.append(performance_obj) -# performance_list_data = serializers.SemesterMarksSerializer(performance_list, many=True).data - -# thesis_request_list = serializers.ThesisTopicProcessSerializer(obj.thesistopicprocess_set.all(), many=True).data - -# pre_existing_thesis_flag = True if obj.thesistopicprocess_set.all() else False - -# current_sem_branch_courses = get_branch_courses(current_user, user_sem, user_branch) - -# # pre_registration_date_flag = get_pre_registration_eligibility(current_date) -# final_registration_date_flag = get_final_registration_eligibility(current_date) - -# add_or_drop_course_date_flag = get_add_or_drop_course_date_eligibility(current_date) - -# student_registration_check_pre = obj.studentregistrationcheck_set.all().filter(semester=user_sem+1) -# student_registration_check_final = obj.studentregistrationcheck_set.all().filter(semester=user_sem) -# pre_registration_flag = False -# final_registration_flag = False -# if(student_registration_check_pre): -# pre_registration_flag = student_registration_check_pre.pre_registration_flag -# if(student_registration_check_final): -# final_registration_flag = student_registration_check_final.final_registration_flag - -# teaching_credit_registration_course = None -# if phd_flag: -# teaching_credit_registration_course = Curriculum.objects.all().filter(batch = 2016, sem =6) -# teaching_credit_registration_course_data = serializers.CurriculumSerializer(teaching_credit_registration_course, many=True).data - -# if student_flag: -# try: -# due = obj.dues_set.get() -# lib_d = due.library_due -# pc_d = due.placement_cell_due -# hos_d = due.hostel_due -# mess_d = due.mess_due -# acad_d = due.academic_due -# except: -# lib_d, pc_d, hos_d, mess_d, acad_d = 0, 0, 0, 0, 0 - -# tot_d = lib_d + acad_d + pc_d + hos_d + mess_d - -# registers = obj.register_set.all() -# course_list = [] -# for i in registers: -# course_list.append(i.curr_id) -# attendence = [] -# for i in course_list: -# instructors = i.curriculum_instructor_set.all() -# pr,ab=0,0 -# for j in list(instructors): - -# presents = obj.student_attendance_set.all().filter(instructor_id=j, present=True) -# absents = obj.student_attendance_set.all().filter(instructor_id=j, present=False) -# pr += len(presents) -# ab += len(absents) -# attendence.append((i,pr,pr+ab)) -# attendance_data = {} -# for course in attendence: -# attendance_data[course[0].course_id.course_name] = { -# 'present' : course[1], -# 'total' : course[2] -# } - -# branchchange_flag = False -# if user_sem == 2: -# branchchange_flag=True - -# faculty_list = serializers.HoldsDesignationSerializer(get_faculty_list(), many=True).data - -# resp = { -# 'details': details, -# 'currently_registered': currently_registered_courses_data, -# # 'pre_registered_courses' : pre_registered_courses_data, -# # 'pre_registered_courses_show' : pre_registered_courses_show_data, -# 'final_registered_courses' : final_registered_courses_data, -# 'current_credits' : current_credits, -# 'courses_list': next_sem_branch_courses_data, -# 'fee_payment_mode_list' : fee_payment_mode_list, -# 'next_sem_branch_registration_courses' : next_sem_branch_registration_courses_data, -# 'final_registration_choices' : final_registration_choices_data, -# 'performance_list' : performance_list_data, -# 'thesis_request_list' : thesis_request_list, -# 'student_flag' : student_flag, -# 'ug_flag' : ug_flag, -# 'masters_flag' : masters_flag, -# 'phd_flag' : phd_flag, -# 'fac_flag' : fac_flag, -# 'des_flag' : des_flag, -# 'thesis_flag' : pre_existing_thesis_flag, -# 'drop_courses_options' : currently_registered_courses_data, -# # 'pre_registration_date_flag': pre_registration_date_flag, -# 'final_registration_date_flag': final_registration_date_flag, -# 'add_or_drop_course_date_flag': add_or_drop_course_date_flag, -# # 'pre_registration_flag' : pre_registration_flag, -# 'final_registration_flag': final_registration_flag, -# 'teaching_credit_registration_course' : teaching_credit_registration_course_data, -# 'lib_d':lib_d, -# 'acad_d':acad_d, -# 'mess_d':mess_d, -# 'pc_d':pc_d, -# 'hos_d':hos_d, -# 'tot_d':tot_d, -# 'attendance': attendance_data, -# 'Branch_Change_Flag':branchchange_flag -# # 'faculty_list' : faculty_list -# } -# return Response(data=resp, status=status.HTTP_200_OK) - - - - - - - + return Response({'error':'Cannot add thesis'}, status=status.HTTP_400_BAD_REQUEST) - - - - - - - - - - - - - - -# These apis are not needed in this module - - - -# @api_view(['POST']) -# def add_thesis(request): -# current_user = request.user -# profile = current_user.extrainfo -# if profile.user_type == 'student': -# if not 'thesis_topic' in request.data: -# return Response({'error':'Thesis topic is required'}, status=status.HTTP_400_BAD_REQUEST) -# if not 'research_area' in request.data: -# return Response({'error':'Research area is required'}, status=status.HTTP_400_BAD_REQUEST) -# if 'supervisor_id' in request.data: -# try: -# supervisor_faculty = User.objects.get(username=request.data['supervisor_id']) -# supervisor_faculty = supervisor_faculty.extrainfo -# request.data['supervisor_id'] = supervisor_faculty -# except: -# return Response({'error':'Wrong supervisor id. User does not exist.'}, status=status.HTTP_400_BAD_REQUEST) -# else: -# return Response({'error':'supervisor id is required'}, status=status.HTTP_400_BAD_REQUEST) -# if 'co_supervisor_id' in request.data: -# try: -# co_supervisor_faculty = User.objects.get(username=request.data['co_supervisor_id']) -# co_supervisor_faculty = co_supervisor_faculty.extrainfo -# request.data['co_supervisor_id'] = co_supervisor_faculty -# except: -# return Response({'error':'Wrong co_supervisor id. User does not exist.'}, status=status.HTTP_400_BAD_REQUEST) -# else: -# co_supervisor_faculty = None -# if 'curr_id' in request.data: -# curr_id = None -# student = profile.student -# request.data['student_id'] = profile -# request.data['submission_by_student'] = True -# serializer = serializers.ThesisTopicProcessSerializer(data=request.data) -# if serializer.is_valid(): -# serializer.save() -# return Response(serializer.data, status=status.HTTP_200_OK) -# else: -# return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) -# else: -# return Response({'error':'Cannot add thesis'}, status=status.HTTP_400_BAD_REQUEST) - - - - - - - - - - - - - - - - - -# @api_view(['PUT']) -# def approve_thesis(request, id): -# current_user = request.user -# profile = current_user.extrainfo -# if profile.user_type == 'faculty': -# try: -# thesis = ThesisTopicProcess.objects.get(id=id) -# except: -# return Response({'error':'This thesis does not exist'}, status=status.HTTP_400_BAD_REQUEST) -# if 'member1' in request.data: -# try: -# user1 = User.objects.get(username=request.data['member1']) -# member1 = user1.extrainfo -# request.data['member1'] = member1 -# except: -# return Response({'error':'Wrong username of member 1. User does not exist.'}, status=status.HTTP_400_BAD_REQUEST) -# else: -# return Response({'error':'Member 1 is required'}, status=status.HTTP_400_BAD_REQUEST) -# if 'member2' in request.data: -# try: -# user2 = User.objects.get(username=request.data['member2']) -# member2 = user2.extrainfo -# request.data['member2'] = member2 -# except: -# return Response({'error':'Wrong username of member 2. User does not exist.'}, status=status.HTTP_400_BAD_REQUEST) -# else: -# return Response({'error':'Member 2 is required'}, status=status.HTTP_400_BAD_REQUEST) -# if 'member3' in request.data: -# try: -# user3 = User.objects.get(username=request.data['member3']) -# member3 = user3.extrainfo -# request.data['member3'] = member3 -# except: -# return Response({'error':'Wrong username of member 3. User does not exist.'}, status=status.HTTP_400_BAD_REQUEST) -# else: -# member3 = None -# if not 'approval' in request.data: -# return Response({'error':'Approval value is required.'}, status=status.HTTP_400_BAD_REQUEST) -# elif request.data['approval'] != 'yes' and request.data['approval'] != 'no': -# return Response({'error':'Wrong approval value provided. Approval value should be yes or no'}, status=status.HTTP_400_BAD_REQUEST) -# if request.data['approval'] == 'yes': -# request.data.pop('approval', None) -# request.data['pending_supervisor'] = False -# request.data['approval_supervisor'] = True -# request.data['forwarded_to_hod'] = True -# request.data['pending_hod'] = True -# else: -# request.data.pop('approval', None) -# request.data['pending_supervisor'] = False -# request.data['approval_supervisor'] = False -# request.data['forwarded_to_hod'] = False -# request.data['pending_hod'] = False -# serializer = serializers.ThesisTopicProcessSerializer(thesis, data=request.data, partial=True) -# if serializer.is_valid(): -# serializer.save() -# return Response(serializer.data, status=status.HTTP_200_OK) -# else: -# return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) -# else: -# return Response({'error':'Cannot approve thesis'}, status=status.HTTP_400_BAD_REQUEST) - -#--------------------------------------- New APIs Made for React ---------------------------------------------------------- - -# @api_view(['GET']) -# @permission_classes([IsAuthenticated]) -# @authentication_classes([TokenAuthentication]) -# def student_next_sem_courses(request): -# """ -# REST API endpoint to return the courses_list as JSON. Uses DRF authentication. -# """ - -# user_details = ExtraInfo.objects.select_related('user', 'department').get(user=request.user) # Changed to user=request.user -# des = HoldsDesignation.objects.all().select_related().filter(user=request.user).first() - -# if str(des.designation) != "student": -# return Response({"error": "User is not a student"}, status=status.HTTP_403_FORBIDDEN) # 403 Forbidden - DRF style - -# obj = Student.objects.select_related('id', 'id__user', 'id__department').get(id=user_details.id) -# batch = obj.batch_id -# curr_id = batch.curriculum - -# try: -# semester_no = obj.curr_semester_no -# sem_no = semester_no + 1 -# next_sem_id = Semester.objects.get(curriculum=curr_id, semester_no=sem_no) -# except Semester.DoesNotExist: # Handle the case where next semester doesn't exist. -# return Response({"error": "Next semester not found"}, status=status.HTTP_404_NOT_FOUND) - - -# # Serialize the data (using DRF serializers is highly recommended) -# course_slot = CourseSlot.objects.all().filter(semester_id = next_sem_id).prefetch_related(Prefetch('courses', queryset=Courses.objects.all())) -# print(course_slot[0].courses) -# serializer = serializers.CourseSlotSerializer(course_slot, many=True) # Assuming you have a CourseSerializer -# courses_list_data = serializer.data - -# return Response({"courses_list": courses_list_data}, status=status.HTTP_200_OK) - -# @api_view(['GET']) -# @authentication_classes([TokenAuthentication]) -# @permission_classes([IsAuthenticated]) -# def current_courseregistration(request): -# try: -# current_user = request.user -# user_details = current_user.extrainfo - -# student = Student.objects.get(id=user_details) - -# current_semester = student.curr_semester_no - -# current_courses = course_registration.objects.filter( -# student_id=student, semester_id__semester_no=current_semester -# ) -# print(current_courses) - -# serializer = serializers.CourseRegistrationSerializer(current_courses, many=True) -# print(serializer.data) -# return Response(serializer.data, status=status.HTTP_200_OK) - -# except Student.DoesNotExist: -# return Response({"error": "Student profile not found"}, status=status.HTTP_404_NOT_FOUND) -# except Exception as e: -# return Response({"error": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) - -@api_view(['GET']) -@authentication_classes([TokenAuthentication]) -@permission_classes([IsAuthenticated]) -@role_required(['student']) -def final_registration_page(request): - try: - current_user = request.user - user_details = current_user.extrainfo - student = Student.objects.get(id=user_details) - curr_id = student.batch_id.curriculum - next_sem_id = Semester.objects.get(curriculum=curr_id, semester_no=student.curr_semester_no+1) - current_date = date_time.date() - final_registration_date_flag = get_final_registration_eligibility(current_date) - student_registration_check = get_student_registrtion_check(student, next_sem_id) - final_registration_flag = False - if student_registration_check: - final_registration_flag = student_registration_check.final_registration_flag - - final_registration = FinalRegistration.objects.filter( - student_id=user_details.id, semester_id=next_sem_id - ) - if final_registration.exists(): - final_registration = serializers.FinalRegistrationSerializer(final_registration, many=True).data +@api_view(['PUT']) +def approve_thesis(request, id): + current_user = request.user + profile = current_user.extrainfo + if profile.user_type == 'faculty': + try: + thesis = ThesisTopicProcess.objects.get(id=id) + except: + return Response({'error':'This thesis does not exist'}, status=status.HTTP_400_BAD_REQUEST) + if 'member1' in request.data: + try: + user1 = User.objects.get(username=request.data['member1']) + member1 = user1.extrainfo + request.data['member1'] = member1 + except: + return Response({'error':'Wrong username of member 1. User does not exist.'}, status=status.HTTP_400_BAD_REQUEST) else: - final_registration = None - resp = { - 'frd': final_registration_date_flag, - 'final_registration_flag': final_registration_flag, - 'final_registration': final_registration, - } - return Response(data=resp, status=status.HTTP_200_OK) - except Exception as e: - return Response({"error": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) - - -@api_view(['GET', 'POST']) -@authentication_classes([TokenAuthentication]) -@permission_classes([IsAuthenticated]) -@role_required(['acadadmin']) -def student_list(request): - if request.method == 'POST': - excel_export = request.GET.get("excel_export", "false") - data = json.loads(request.body) - batch = data.get('batch') - - year = demo_date.year - month = demo_date.month - yearr = f'{year}-{year+1}' - semflag = 1 if month >= 7 else 2 - queryflag = 1 - - batch_id = Batch.objects.get(id=batch) - student_obj = FeePayments.objects.all().select_related('student_id').filter(student_id__batch_id=batch_id) - - if excel_export == "false": - if student_obj: - reg_table = list(student_obj.prefetch_related('student_id__studentregistrationchecks') - .filter(semester_id=student_obj[0].semester_id, student_id__studentregistrationchecks__final_registration_flag=True, - student_id__finalregistration__verified=False, student_id__finalregistration__semester_id=student_obj[0].semester_id) - .select_related('student_id', 'student_id__id', 'student_id__id__user', 'student_id__id__department') - .values( - 'student_id__id', 'student_id__id__user__first_name', 'student_id__id__user__last_name', - 'student_id__batch', 'student_id__id__department__name', 'student_id__programme', - 'student_id__curr_semester_no', 'student_id__id__sex', 'student_id__id__phone_no', - 'student_id__category', 'student_id__specialization', 'mode', 'transaction_id', 'deposit_date', - 'fee_paid', 'utr_number', 'reason', 'fee_receipt', 'actual_fee', - 'student_id__id__user__username' - ).distinct()) - - else: - reg_table = [] - - response_data = { - 'date': {'year': yearr, 'month': month, 'semflag': semflag, 'queryflag': queryflag}, - 'students': reg_table - } - - return JsonResponse(response_data, safe=False) - - elif excel_export == "true": - if student_obj: - table = [("Admission Year", "Semester", "Roll Number", "Full Name", "Program", "Discipline", "Specialization", "Gender", "Category", "Mobile Number", "Actual Fee", "Fee Paid By Student", "Reason", "Date", "Mode", "UTR Number", "Fee Receipt")] - - table += student_obj.prefetch_related('student_id__studentregistrationchecks').filter(semester_id=student_obj[0].semester_id, student_id__studentregistrationchecks__final_registration_flag=True).select_related('student_id', 'student_id__id', 'student_id__id__user', 'student_id__id__department').annotate( - admission_year = F('student_id__batch'), - semester=F('student_id__curr_semester_no') + 1, - roll_no=F('student_id__id__user__username'), - full_name=Concat('student_id__id__user__first_name', Value(' '), 'student_id__id__user__last_name'), - program=F('student_id__programme'), - discipline=F('student_id__id__department__name'), - specialization=F('student_id__specialization'), - gender=F('student_id__id__sex'), - category=F('student_id__category'), - phone_no=F('student_id__id__phone_no'), - date_deposited=Concat(Cast(ExtractDay('deposit_date'), CharField()), Value('/'), - Cast(ExtractMonth('deposit_date'), CharField()), Value('/'), - Cast(ExtractYear('deposit_date'), CharField()), output_field=CharField()) - ).values_list('admission_year', 'semester', 'roll_no', 'full_name', 'program', 'discipline', 'specialization', 'gender', 'category', 'phone_no', 'actual_fee', 'fee_paid', 'reason', 'date_deposited', 'mode', 'utr_number', 'fee_receipt').distinct() - - excel_response = BytesIO() - final_register_workbook = Workbook(excel_response) - final_register_worksheet = final_register_workbook.add_worksheet() - - for i, row in enumerate(table): - for j, cell in enumerate(row): - final_register_worksheet.write(i, j, cell) - - final_register_workbook.close() - excel_response.seek(0) - - response = HttpResponse(excel_response.read(), content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') - response['Content-Disposition'] = f'attachment; filename="{batch_id.name}_{batch_id.discipline.acronym}_{batch_id.year}_final_registered.xlsx"' - return response - - else: - return JsonResponse({'error': 'No registered students found'}, status=404) - - return JsonResponse({'error': 'Invalid request'}, status=400) - - -@api_view(['POST']) -@authentication_classes([TokenAuthentication]) -@permission_classes([IsAuthenticated]) -@role_required(['acadadmin']) -def course_list(request): - request_body = json.loads(request.body) - student_id = request_body['student_id'] - semester_no = request_body['semester_no'] - - # final_registration_table = FinalRegistration.objects.all().filter(semester_id = semester_id, verified = False) - # final = final_registration_table.filter(student_id = student_id, semester_id = semester_id) - final = FinalRegistration.objects.all().filter(semester_id__semester_no = semester_no, student_id__id=student_id, verified = False) - if final.exists(): - final_registration = serializers.FinalRegistrationSerializer(final, many=True).data - else: - final_registration = None - resp = { - 'final_registration': final_registration, - } - return Response(data=resp, status=status.HTTP_200_OK) - -@api_view(['POST']) -@authentication_classes([TokenAuthentication]) -@permission_classes([IsAuthenticated]) -@role_required(['acadadmin']) -def dropcourseadmin(request): - try: - reg_id = request.data.get('id') - roll_no = request.data.get('roll_no') - - if not reg_id or not roll_no: - return JsonResponse({'error': 'Missing registration ID or roll number'}, status=400) - - reg_id = int(reg_id) - course_registration.objects.filter(id=reg_id).delete() - - return JsonResponse({'message': 'Success!'}, status=200) - except Exception as e: - return JsonResponse({'error': str(e)}, status=400) - - -@api_view(['POST']) -@authentication_classes([TokenAuthentication]) -@permission_classes([IsAuthenticated]) -@role_required(['acadadmin']) -def acad_add_course(request): - data = request.data - for fld in ("roll_no", "semester_id", "courseslot_id", "course_id", "academic_year", "registration_type", "semester_type"): - if not data.get(fld): - return Response({ "error": f"{fld} is required" }, status=status.HTTP_400_BAD_REQUEST) - student = get_object_or_404(Student, id=data["roll_no"].upper()) - semester = get_object_or_404(Semester, id=data["semester_id"]) - slot = get_object_or_404(CourseSlot, id=data["courseslot_id"]) - course = get_object_or_404(Courses, id=data["course_id"]) - session = data["academic_year"] - reg_type = data["registration_type"] - old_id = data.get("old_course") - sem_type = data["semester_type"] - with transaction.atomic(): - cr = course_registration.objects.create( - student_id = student, - semester_id = semester, - course_slot_id = slot, - course_id = course, - session = session, - registration_type= reg_type, - semester_type = sem_type, - working_year = parse_academic_year(academic_year=session, semester_type=sem_type)[0] - ) - if old_id: - old = course_registration.objects.filter(id=old_id).first() - if old: - course_replacement.objects.create( - old_course_registration = old, - new_course_registration = cr, - ) - - return Response({ "message": "Course added successfully" }, status=status.HTTP_200_OK) - -@api_view(["GET"]) -@permission_classes([IsAuthenticated]) -def academic_procedures_faculty_api(request): - try: - # Ensure the user is a faculty member - if request.user.extrainfo.user_type != 'faculty': - return Response({"error": "Unauthorized access. Faculty only."}, status=403) - - user_details = ExtraInfo.objects.select_related("department").get(user=request.user) - - # Get courses taught by the faculty - courses = CourseInstructor.objects.filter(instructor_id=user_details.id).select_related("course_id") - - current_year = timezone.now().year - response_data = [] - - for course in courses: - # Calculate academic year from calendar year + semester - - response_data.append({ - "course_id": course.course_id.id, - "course_code": course.course_id.code, - "course_name": course.course_id.name, - "version": course.course_id.version, - "semester_type": course.semester_type, - "academic_year": course.academic_year, - }) - - return Response({"assigned_courses": response_data}) - - except Exception as e: - return Response({"error": str(e)}, status=500) - -@api_view(['POST']) -@authentication_classes([TokenAuthentication]) -@permission_classes([IsAuthenticated]) -@role_required(['acadadmin']) -def search_preregistration(request): - try: - roll_no=request.data.get("roll_no") - sem_no=request.data.get("sem_no") - initial_registrations = InitialRegistration.objects.filter( - student_id_id=roll_no, semester_id__semester_no=sem_no - ) - student_registration_check = StudentRegistrationChecks.objects.filter( - student_id_id=roll_no, semester_id__semester_no=sem_no - ).first() - initial = serializers.InitialRegistrationSerializer(initial_registrations, many=True) - student_registration_check_data = serializers.StudentRegistrationChecksSerializer(student_registration_check) - return Response({ - "initial_registration": initial.data, # Send serialized initial registrations - "student_registration_check": student_registration_check_data.data if student_registration_check else None - }) - except Exception as e: - return Response( - {"error": f"An error occurred: {str(e)}"}, - status=status.HTTP_500_INTERNAL_SERVER_ERROR - ) - - -@api_view(['POST']) -@authentication_classes([TokenAuthentication]) -@permission_classes([IsAuthenticated]) -@role_required(['acadadmin']) -def delete_preregistration(request): - try: - # Extract roll_no and sem_no from the request - roll_no = request.data.get("roll_no") - sem_no = request.data.get("sem_no") - - # Validate input data - if not roll_no or not sem_no: - return Response( - {"error": "Both roll_no and sem_no are required"}, - status=status.HTTP_400_BAD_REQUEST - ) - - # Delete initial registration entries - initial_registrations = InitialRegistration.objects.filter( - student_id_id=roll_no, semester_id__semester_no=sem_no - ) - initial_count = initial_registrations.delete() - - # Delete student registration check entries - student_registration_check = StudentRegistrationChecks.objects.filter( - student_id_id=roll_no, semester_id__semester_no=sem_no - ) - student_registration_check_count = student_registration_check.delete() - - # Return a success response with counts of deleted entries - return Response({ - "message": "Successfully Deleted." - }) - - except Exception as e: - return Response( - {"error": f"An error occurred: {str(e)}"}, - status=status.HTTP_500_INTERNAL_SERVER_ERROR - ) - - -@api_view(['POST']) -@authentication_classes([TokenAuthentication]) -@permission_classes([IsAuthenticated]) -@role_required(['acadadmin']) -def allot_courses(request): - if 'allotedCourses' not in request.FILES: - return Response({'error': 'Excel file not provided.'}, - status=status.HTTP_400_BAD_REQUEST) - - batch_id = request.data.get('batch') - sem_no = request.data.get('semester') - sem_type = request.data.get('semester_type') - academic_year = request.data.get('academic_year') - working_year, _ = parse_academic_year(academic_year=academic_year, semester_type=sem_type) - - if not all([batch_id, sem_no, sem_type, academic_year]): - return Response({'error': 'Missing required fields.'}, - status=status.HTTP_400_BAD_REQUEST) - - try: - sem_no = int(sem_no) - except ValueError: - return Response({'error': 'Semester must be integer.'}, - status=status.HTTP_400_BAD_REQUEST) - - try: - with transaction.atomic(): - batch = Batch.objects.get(id=batch_id) - sem = Semester.objects.get( - curriculum=batch.curriculum, - semester_no=sem_no - ) - book = xlrd.open_workbook(file_contents=request.FILES['allotedCourses'].read()) - sheet = book.sheet_by_index(0) - - checks, pre_regs, final_regs, course_regs = [], [], [], [] - seen = set() - - for i in range(1, sheet.nrows): - - try: - roll_no = str(sheet.cell_value(i,0)).split('.')[0].strip() - slot_name = sheet.cell_value(i,1).strip() - code = sheet.cell_value(i,2).strip() - - # user = User.objects.get(username=roll_no) - student = Student.objects.get(id__user__username=roll_no) - slot = CourseSlot.objects.get(name=slot_name, semester=sem) - course = slot.courses.get(code=code) - if roll_no not in seen: - checks.append(StudentRegistrationChecks( - student_id=student, - semester_id=sem, - pre_registration_flag=True, - final_registration_flag=True - )) - seen.add(roll_no) - - pre_regs.append(InitialRegistration( - student_id=student, - course_slot_id=slot, - course_id=course, - semester_id=sem, - priority=1 - )) - final_regs.append(FinalRegistration( - student_id=student, - course_slot_id=slot, - course_id=course, - semester_id=sem, - verified=True - )) - course_regs.append(course_registration( - session=academic_year, - working_year = working_year, - course_id=course, - semester_id=sem, - student_id=student, - course_slot_id=slot, - semester_type = sem_type - )) - except Exception as e: - pass # Handle error silently or log it - - StudentRegistrationChecks.objects.bulk_create(checks, ignore_conflicts=True) - InitialRegistration.objects.bulk_create(pre_regs, ignore_conflicts=True) - FinalRegistration.objects.bulk_create(final_regs, ignore_conflicts=True) - course_registration.objects.bulk_create(course_regs, ignore_conflicts=True) - - return Response({'message': 'Successfully uploaded!'}) - except Batch.DoesNotExist: - return Response({'error': 'Invalid batch id.'}, status=status.HTTP_400_BAD_REQUEST) - except Semester.DoesNotExist: - return Response({'error': 'Invalid semester or type.'}, status=status.HTTP_400_BAD_REQUEST) - except xlrd.XLRDError: - return Response({'error': 'Invalid Excel format.'}, status=status.HTTP_400_BAD_REQUEST) - except Exception as e: - return Response({'error': f'Processing error: {str(e)}'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) - - -@api_view(['GET']) -@permission_classes([IsAuthenticated]) -@authentication_classes([TokenAuthentication]) -@role_required(['student']) -def student_next_sem_courses(request): - """ - REST API endpoint to return the courses_list as JSON. Uses DRF authentication. - """ - - user_details = ExtraInfo.objects.select_related('user', 'department').get(user=request.user) # Changed to user=request.user - des = HoldsDesignation.objects.all().select_related().filter(user=request.user).first() - - if str(des.designation) != "student": - return Response({"error": "User is not a student"}, status=status.HTTP_403_FORBIDDEN) # 403 Forbidden - DRF style - - obj = Student.objects.select_related('id', 'id__user', 'id__department').get(id=user_details.id) - batch = obj.batch_id - curr_id = batch.curriculum - - try: - semester_no = obj.curr_semester_no - sem_no = semester_no + 1 - next_sem_id = Semester.objects.get(curriculum=curr_id, semester_no=sem_no) - except Semester.DoesNotExist: # Handle the case where next semester doesn't exist. - return Response({"error": "Next semester not found"}, status=status.HTTP_404_NOT_FOUND) - - - # Serialize the data (using DRF serializers is highly recommended) - course_slot = CourseSlot.objects.all().filter(semester_id = next_sem_id).prefetch_related(Prefetch('courses', queryset=Courses.objects.all())) - serializer = serializers.CourseSlotSerializer(course_slot, many=True) # Assuming you have a CourseSerializer - courses_list_data = serializer.data - - return Response({"courses_list": courses_list_data}, status=status.HTTP_200_OK) - -# @api_view(['GET']) -# @authentication_classes([TokenAuthentication]) -# @permission_classes([IsAuthenticated]) -# def current_courseregistration(request): -# try: -# current_user = request.user -# user_details = current_user.extrainfo - -# student = Student.objects.get(id=user_details) - -# current_semester = student.curr_semester_no -# print(current_semester) - -# try: -# semester = Semester.objects.get(curriculum=student.batch_id.curriculum, semester_no=current_semester) -# except Semester.DoesNotExist: -# return JsonResponse({"error": "semester not found."}, status=404) - -# print(student) -# current_courses = course_registration.objects.filter( -# student_id=student, semester_id=semester -# ) -# print(current_courses) - -# serializer = serializers.CourseRegistrationSerializer(current_courses, many=True) -# print(serializer.data) -# return Response(serializer.data, status=status.HTTP_200_OK) - -# except Student.DoesNotExist: -# return Response({"error": "Student profile not found"}, status=status.HTTP_404_NOT_FOUND) -# except Exception as e: -# return Response({"error": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) - -@api_view(['GET']) -@authentication_classes([TokenAuthentication]) -@permission_classes([IsAuthenticated]) -@role_required(['student']) -def course_registration_view(request): - try: - current_user = request.user - user_details = current_user.extrainfo - student = Student.objects.get(id=user_details) - - semester_no = request.query_params.get('semester', student.curr_semester_no) - semester_type = request.query_params.get('semester_type', 'Even Semester' if student.curr_semester_no%2==0 else 'Odd Semester') - try: - semester = Semester.objects.get(curriculum=student.batch_id.curriculum, semester_no=semester_no) - except Semester.DoesNotExist: - return JsonResponse({"error": "Semester not found."}, status=404) - - courses = course_registration.objects.filter(student_id=student, semester_id=semester, semester_type=semester_type) - - result = [] - for reg in courses: - course_data = serializers.CourseRegistrationSerializer(reg).data - - replacements = course_replacement.objects.filter(old_course_registration=reg) - replaced_by_list = [] - - for replacement in replacements: - new_reg = replacement.new_course_registration - replaced_by_list.append({ - "code": new_reg.course_id.code, - "name": new_reg.course_id.name, - "semester_no": new_reg.semester_id.semester_no, - "label" : make_label(new_reg.semester_id.semester_no, new_reg.semester_type) - }) - - course_data["replaced_by"] = replaced_by_list - result.append(course_data) - return Response({"reg_data": result, "sem_no": semester_no, "semester_type": semester_type}, status=status.HTTP_200_OK) - - except Student.DoesNotExist: - return Response({"error": "Student profile not found"}, status=status.HTTP_404_NOT_FOUND) - except Exception as e: - return Response({"error": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) - - -def get_add_registration_eligibility(current_date, user_sem, year = datetime.datetime.now().year): - try: - add_date = Calendar.objects.get(description=f"Add {user_sem} {year}") - add_start_date = add_date.from_date - add_end_date = add_date.to_date - if current_date add_end_date: - return JsonResponse({f"error": "Add course period has ended"}, status=400) - except Calendar.DoesNotExist: - return JsonResponse({f"error": "Add course date is not yet decided"}, status=400) - except Exception as e: - return JsonResponse({f"error": str(e)}, status=400) - -def get_drop_registration_eligibility(current_date, user_sem, year = datetime.datetime.now().year): - try: - drop_date = Calendar.objects.get(description=f"Drop {user_sem} {year}") - drop_start_date = drop_date.from_date - drop_end_date = drop_date.to_date - if current_date drop_end_date: - return JsonResponse({f"error": "Drop course period has ended"}, status=400) - except Calendar.DoesNotExist: - return JsonResponse({f"error": "Drop course date is not yet decided"}, status=400) - except Exception as e: - return JsonResponse({f"error": str(e)}, status=400) - -def get_replace_registration_eligibility(current_date, user_sem, year = datetime.datetime.now().year): - try: - replace_date = Calendar.objects.get(description=f"Replace {user_sem} {year}") - replace_start_date = replace_date.from_date - replace_end_date = replace_date.to_date - if current_date replace_end_date: - return JsonResponse({f"error": "Replace course period has ended"}, status=400) - except Calendar.DoesNotExist: - return JsonResponse({f"error": "Replace course date is not yet decided"}, status=400) - except Exception as e: - return JsonResponse({f"error": str(e)}, status=400) - -def get_pre_registration_eligibility(current_date, user_sem, year = datetime.datetime.now().year): - try: - pre_registration_date = Calendar.objects.get(description=f"Pre Registration {user_sem} {year}") - prd_start_date = pre_registration_date.from_date - prd_end_date = pre_registration_date.to_date - if current_date prd_end_date: - return JsonResponse({f"error": "Pre Registration Registration has ended"}, status=400) - except Calendar.DoesNotExist: - return JsonResponse({f"error": "Pre Registration Date is not yet Decided"}, status=400) - except Exception as e: - pass - -def get_swayam_registration_eligibility(current_date, user_sem, year = datetime.datetime.now().year): - try: - swayam_add_date = Calendar.objects.get(description=f"Swayam {user_sem} {year}") - swayam_add_start_date = swayam_add_date.from_date - swayam_add_end_date = swayam_add_date.to_date - if current_date swayam_add_end_date: - return JsonResponse({f"error": "Swayam Registration has ended"}, status=400) - except Calendar.DoesNotExist: - return JsonResponse({f"error": "Swayam Registration Date is not yet Decided"}, status=400) - except Exception as e: - pass - -def get_student_registrtion_check(student, sem): - return StudentRegistrationChecks.objects.filter(student_id=student, semester_id=sem).first() - - - -def get_student_registrations(student, semester): - """ - Returns a QuerySet of InitialRegistration entries for the given student and semester. - - Args: - student (Student): The student instance. - semester (Semester): The semester instance. - - Returns: - QuerySet: Registrations for the student in the given semester. - """ - return InitialRegistration.objects.filter(student_id=student, semester_id=semester) - - -@api_view(['GET']) -@authentication_classes([TokenAuthentication]) -@permission_classes([IsAuthenticated]) -@role_required(['student']) -def get_preregistration_data(request): - """ - Returns the list of course slots available for the student's next semester, - along with the list of courses available in each slot. - If the student has already completed pre-registration, returns the registered - courses with their priorities. - """ - try: - current_user = request.user - user_details = current_user.extrainfo - student = Student.objects.get(id=user_details) - semester_no = student.curr_semester_no - next_sem_no = semester_no+1 - try: - next_semester = Semester.objects.get(curriculum=student.batch_id.curriculum, semester_no=next_sem_no) - except Semester.DoesNotExist: - return JsonResponse({"error": "Not Eligible for Pre Registration"}, status=400) - - # Check if the student has already completed pre-registration. - registration_check = get_student_registrtion_check(student, next_semester) - course_slots = CourseSlot.objects.filter(semester=next_semester)\ - .exclude(name__icontains='SW') - data = [] - if registration_check and registration_check.pre_registration_flag: - registrations = get_student_registrations(student, next_semester) - regular_regs = registrations.exclude(course_slot_id__name__icontains='BL') - backlog_regs = registrations.filter( - course_slot_id__name__startswith='BL' - ) - # Build a lookup dictionary: {(slot_id, course_id): priority} - reg_lookup = { - (reg.course_slot_id.id if reg.course_slot_id else None, reg.course_id.id): reg.priority - for reg in regular_regs - } - for slot in course_slots: - courses = slot.courses.all() - # print(slot.id) - course_choices = [ - { - "id": course.id, - "code": course.code, - "name": course.name, - "credits": course.credit, - "priority": reg_lookup.get((slot.id, course.id), "") - } - for course in courses - ] - data.append({ - "sno": slot.id, - "slot_name": slot.name, - "slot_type": slot.type, - "semester": next_sem_no, - "course_choices": course_choices, - }) - - backlog_data = [] - for reg in backlog_regs: - backlog_data.append({ - "sno": reg.course_slot_id.id if reg.course_slot_id else None, - "slot_name": reg.course_slot_id.name if reg.course_slot_id else "Unknown", - "course_choices": [{ - "id": reg.course_id.id, - "code": reg.course_id.code, - "name": reg.course_id.name - }], - "prev_registration": { - "id": reg.old_course_registration.id if reg.old_course_registration else "", - "code": reg.old_course_registration.course_id.code if reg.old_course_registration and reg.old_course_registration.course_id else "", - "name": reg.old_course_registration.course_id.name if reg.old_course_registration and reg.old_course_registration.course_id else "", - "semester_no": reg.old_course_registration.semester_id.semester_no if reg.old_course_registration and reg.old_course_registration.semester_id else "", - } - }) - return JsonResponse({"message": "Already registered", "data": data, "backlog_data":backlog_data}, safe=False) - else: - # If not already registered, return slots without pre-set priorities. - eligibility_resp = get_pre_registration_eligibility(timezone.now().date(), next_sem_no) - if isinstance(eligibility_resp, JsonResponse): - return eligibility_resp - prev_registrations = serializers.CourseRegistrationSerializer(course_registration.objects.filter(student_id=student), many=True).data - for slot in course_slots: - courses = slot.courses.all() - course_choices = [ - { - "id": course.id, - "code": course.code, - "name": course.name, - "credits": course.credit - } - for course in courses - ] - data.append({ - "sno": slot.id, - "slot_name": slot.name, - "slot_type": slot.type, - "semester": next_sem_no, - "course_choices": course_choices, - "prev_registrations": prev_registrations - }) - return JsonResponse(data, safe=False) - except Student.DoesNotExist: - return Response({"error": "Student profile not found"}, status=400) - except Exception as e: - return Response({"error": str(e)}, status=500) - - -@api_view(['POST']) -@permission_classes([IsAuthenticated]) -@role_required(['student']) -def submit_preregistration(request): - """ - Expects a POST request with JSON data containing an array "registrations". - Each registration entry should include: - - slot_id: the ID of the CourseSlot - - course_id: the chosen Course ID for that slot - - priority: the priority assigned by the student - If the student has not pre-registered, the registrations will be created. - """ - if request.method != "POST": - return JsonResponse({"error": "Invalid method"}, status=400) - try: - data = json.loads(request.body) - except json.JSONDecodeError: - return Response({"Invalid JSON"}) - - registrations = data.get("registrations", []) - backlog_registrations = data.get("backlog_registrations", []) - try: - current_user = request.user - user_details = current_user.extrainfo - student = Student.objects.get(id=user_details) - semester_no = student.curr_semester_no - # Here you may want to use next_sem_no = semester_no + 1 if that is the logic. - next_sem_no = semester_no+1 - try: - next_semester = Semester.objects.get(curriculum=student.batch_id.curriculum, semester_no=next_sem_no) - except Semester.DoesNotExist: - return JsonResponse({"error": "Not Eligible for Pre Registration"}, status=400) - eligibility_resp = get_pre_registration_eligibility(timezone.now().date(), next_sem_no) - if isinstance(eligibility_resp, JsonResponse): - return eligibility_resp - except Student.DoesNotExist: - return Response({"error": "Student profile not found"}, status=400) - - for reg in registrations: - slot_id = reg.get("slot_id") - course_id = reg.get("course_id") - priority = reg.get("priority") - - InitialRegistration.objects.create( - course_id_id=course_id, - semester_id_id=next_semester.id, - student_id=student, - course_slot_id_id=slot_id, - priority=priority, - timestamp=timezone.now() - ) - - for reg in backlog_registrations: - slot_id = reg.get("slot_id") - course_id = reg.get("course_id") - priority = reg.get("priority") - prev_registration_id = reg.get("prev_registration_id") - - InitialRegistration.objects.create( - course_id_id=course_id, - semester_id_id=next_semester.id, - student_id=student, - course_slot_id_id=slot_id, - priority=priority, - registration_type='Backlog', - old_course_registration_id = prev_registration_id, - timestamp=timezone.now() - ) - - # Optionally, update the StudentRegistrationChecks record to mark pre-registration as complete. - reg_check, created = StudentRegistrationChecks.objects.get_or_create( - student_id=student, semester_id_id=next_semester.id, - defaults={'pre_registration_flag': True} - ) - if not created: - reg_check.pre_registration_flag = True - reg_check.save() - - return JsonResponse({"status": "success"}, status=201) - - -@api_view(['GET']) -@authentication_classes([TokenAuthentication]) -@permission_classes([IsAuthenticated]) -@role_required(['student']) -def get_swayam_registration_data(request): - """ - Returns the list of course slots available for Swayam registration for the student's next semester, - along with the list of courses available in each slot. - (Only course slots whose name starts with "SW" are returned. - No registration check is performed.) - """ - try: - current_user = request.user - user_details = current_user.extrainfo # assuming extrainfo holds the student id/reference - student = Student.objects.get(id=user_details) - semester_no = student.curr_semester_no - try: - current_semester = Semester.objects.get( - curriculum=student.batch_id.curriculum, - semester_no=semester_no - ) - except Semester.DoesNotExist: - return JsonResponse({"error": "Not Eligible for Swayam Registration"}, status=400) - - eligibility_resp = get_swayam_registration_eligibility(timezone.now().date(), semester_no) - if isinstance(eligibility_resp, JsonResponse): - return eligibility_resp - # For Swayam registration, fetch only those course slots whose name starts with "SW". - course_slots = CourseSlot.objects.filter(semester=current_semester, name__startswith="SW") - data = [] - for slot in course_slots: - courses = slot.courses.all() - course_choices = [ - { - "id": course.id, - "code": course.code, - "name": course.name, - "credits": course.credit - } - for course in courses - ] - data.append({ - "sno": slot.id, - "slot_name": slot.name, - "slot_type": slot.type, - "semester": semester_no, - "course_choices": course_choices, - }) - return JsonResponse(data, safe=False) - except Student.DoesNotExist: - return Response({"error": "Student profile not found"}, status=404) - except Exception as e: - return Response({"error": str(e)}, status=500) - - -@api_view(['POST']) -@authentication_classes([TokenAuthentication]) -@permission_classes([IsAuthenticated]) -@role_required(['student']) -def submit_swayam_registration(request): - """ - Accepts a POST request with JSON data for Swayam registration. - - Expected payload structure: - { - "registrations": [ - { - "slot_id": , - "course_id": , - "selected_option": "", - "remark": "" - }, - ... - ] - } - - For each registration entry, a record is created in InitialRegistrations. - Since Swayam courses are not tied to a course slot in the same way as other courses, - the fields course_slot_id and priority are set to None. - """ - if request.method != "POST": - return JsonResponse({"error": "Invalid method"}, status=400) - try: - payload = json.loads(request.body) - except Exception: - return JsonResponse({"error": "Invalid JSON"}, status=400) - try: - current_user = request.user - user_details = current_user.extrainfo - student = Student.objects.get(id=user_details) - semester_no = student.curr_semester_no - try: - semester = Semester.objects.get( - curriculum=student.batch_id.curriculum, - semester_no=semester_no - ) - except Semester.DoesNotExist: - return JsonResponse({"error": "Not Eligible for Swayam Registration"}, status=400) - except Student.DoesNotExist: - return Response({"error": "Student not found"}, status=404) - - eligibility_resp = get_swayam_registration_eligibility(timezone.now().date(), semester_no) - if isinstance(eligibility_resp, JsonResponse): - return eligibility_resp - - registrations = payload.get("registrations", []) - errors = [] - success_count = 0 - - for reg in registrations: - slot_id = reg.get("slot_id") - course_id = reg.get("course_id") - selected_option = reg.get("selected_option") - remark = reg.get("remark") - - try: - course = Courses.objects.get(id=course_id) - except Courses.DoesNotExist: - errors.append(f"Course with ID {course_id} does not exist") - continue - - # Check if already registered for this course - existing_registration = course_registration.objects.filter( - course_id=course, - semester_id=semester, - student_id=student - ).first() - - if existing_registration: - errors.append(f"Already registered for course: {course.code} - {course.name}") - continue - - try: - course_registration.objects.create( - course_id=course, - semester_id_id=semester.id, - student_id=student, - course_slot_id_id=slot_id - ) - success_count += 1 - except Exception as e: - errors.append(f"Failed to register for {course.code}: {str(e)}") - - if errors and success_count == 0: - return JsonResponse({"error": ", ".join(errors)}, status=400) - elif errors: - return JsonResponse({ - "status": "partial_success", - "message": f"Registered {success_count} course(s) successfully", - "errors": errors - }, status=200) - - return JsonResponse({"status": "success", "message": f"Successfully registered {success_count} course(s)"}, status=201) - - -@api_view(['GET']) -@authentication_classes([TokenAuthentication]) -@permission_classes([IsAuthenticated]) -@role_required(['acadadmin']) -def get_add_course_slots(request): - """ - GET /api/course-slots/?semester_id= - Returns JSON list of { id, name } for all slots in that semester. - """ - sem_id = request.query_params.get("semester_id") - if not sem_id: - return Response( - {"error": "semester_id query parameter is required"}, - status=status.HTTP_400_BAD_REQUEST, - ) - - # ensure the semester exists (404 if not) - get_object_or_404(Semester, id=sem_id) - - # fetch slots and return only id & name - slots = CourseSlot.objects.filter(semester_id=sem_id).values("id", "name") - return Response(list(slots), status=status.HTTP_200_OK) - - -@api_view(['GET']) -@authentication_classes([TokenAuthentication]) -@permission_classes([IsAuthenticated]) -@role_required(['acadadmin']) -def get_add_course_courses(request): - """ - GET /api/courses/?courseslot_id= - Returns JSON list of { id, code, name, credit } for all courses in that slot. - """ - slot_id = request.query_params.get("courseslot_id") - if not slot_id: - return Response( - {"error": "courseslot_id query parameter is required"}, - status=status.HTTP_400_BAD_REQUEST, - ) - - # ensure the slot exists (404 if not) - slot = get_object_or_404(CourseSlot, id=slot_id) - - # via the M2M relationship .courses - courses = slot.courses.all().values("id", "code", "name", "credit") - return Response(list(courses), status=status.HTTP_200_OK) - - -def roman_to_int(s): - roman = {'I': 1, 'II': 2, 'III': 3, 'IV': 4, 'V': 5, 'VI': 6, - 'VII': 7, 'VIII': 8, 'IX': 9, 'X': 10} - return roman.get(s.strip().upper()) - -@api_view(['POST']) -@parser_classes([MultiPartParser]) -@role_required(['acadadmin']) -def upload_excel_replacement(request): - file = request.FILES.get('file') - if not file: - return Response({'error': 'No file uploaded'}, status=400) - - try: - df = pd.read_excel(file) - except Exception as e: - return Response({'error': f'Failed to read Excel: {e}'}, status=400) - - expected = {'student_id', 'old_course_code', 'new_course_code', - 'old_semester_roman', 'new_semester_number'} - if not expected.issubset(df.columns): - return Response( - {'error': f'Missing columns: {expected - set(df.columns)}'}, - status=400 - ) - - valid_entries = [] - failed_rows = [] - - # Validate all rows first - for idx, row in df.iterrows(): - sid = str(row['student_id']).strip() - old_code = str(row['old_course_code']).strip() - new_code = str(row['new_course_code']).strip() - old_rom = str(row['old_semester_roman']).strip() - new_sem = int(row['new_semester_number']) - - sem_old = roman_to_int(old_rom) - if sem_old is None: - msg = f'Row {idx+2} Invalid Roman numeral {old_rom}' - failed_rows.append(msg) - continue - - try: - student = Student.objects.get(id_id=sid) - except Student.DoesNotExist: - msg = f'Row {idx+2} Student {sid} not found' - failed_rows.append(msg) - continue - - try: - old_reg = course_registration.objects.get( - student_id=student, - course_id__code=old_code, - semester_id__semester_no=sem_old - ) - except course_registration.DoesNotExist: - msg = f'Row {idx+2} Old registration not found: {sid}, {old_code}, {sem_old}' - failed_rows.append(msg) - continue - - try: - new_reg = course_registration.objects.get( - student_id=student, - course_id__code=new_code, - semester_id__semester_no=new_sem - ) - except course_registration.DoesNotExist: - msg = f'Row {idx+2} New registration not found: {sid}, {new_code}, {new_sem}' - failed_rows.append(msg) - continue - - valid_entries.append((old_reg, new_reg)) - - # Abort if any error was found - if failed_rows: - return Response({ - 'error': 'Some rows are invalid. No changes were made.', - 'failed_rows': failed_rows - }, status=400) - - # All rows valid: Perform atomic save - try: - with transaction.atomic(): - for old_reg, new_reg in valid_entries: - course_replacement.objects.create( - old_course_registration=old_reg, - new_course_registration=new_reg - ) - except Exception as e: - return Response({'error': str(e)}, status=400) - - return Response({ - 'message': f'{len(valid_entries)} replacements added successfully', - 'failed_rows': [] - }, status=200) - - - -# ─── Mapping HOD designation → allowed Student.specialization ───────────── -HOD_SPECIALIZATION_MAPPING = { - # designation code without spaces/brackets → list of allowed specializations - 'ECE': ['PNC'], # HOD(ECE) can only assign PNC students - 'CSE': ['AIML'], # HOD(CSE) can only assign AIML students - # add more as needed -} - -def check_role(request, required_role): - role = request.query_params.get('role') if request.method=='GET' else request.data.get('role') - return role == required_role - -def get_allowed_specs(user): - """ - Find the HOD's designation name (e.g. "HOD (ECE)"), extract "ECE", - and return the list of allowed student specializations. - """ - des = HoldsDesignation.objects.filter( - working=user, designation__name__startswith='HOD' - ).first() - if not des: - return None - # extract between parentheses - raw = des.designation.name - try: - code = raw.split('(',1)[1].split(')',1)[0].strip() - except: - return None - return HOD_SPECIALIZATION_MAPPING.get(code) - - -@api_view(['GET']) -@permission_classes([IsAuthenticated]) -def tas_list(request): - """GET /api/tas/ → return all TA usernames.""" - data = [{'username': s.id.user.username} for s in Student.objects.all()] - return Response({'tas': data}) - -@api_view(['GET']) -@permission_classes([IsAuthenticated]) -def faculties_list(request): - """GET /api/faculties/ → return all Faculty usernames.""" - data = [{'username': f.id.user.username} for f in Faculty.objects.all()] - return Response({'faculties': data}) - - -# --- HOD endpoints --- - -@api_view(['GET']) -@permission_classes([IsAuthenticated]) -def hod_students(request): - if not check_role(request,'hod'): - return Response({'error':'role=hod required'}, status=status.HTTP_403_FORBIDDEN) - allowed = get_allowed_specs(request.user) - if not allowed: - return Response({'error':'Invalid HOD designation or no mapping'}, status=status.HTTP_403_FORBIDDEN) - qs = Student.objects.filter(specialization__in=allowed) - data = [{'username': s.id.user.username, 'batch': s.batch} for s in qs] - return Response({'students': data}) - -@api_view(['POST']) -@permission_classes([IsAuthenticated]) -def hod_assign_manual(request): - if not check_role(request,'hod'): - return Response({'error':'role=hod required'}, status=status.HTTP_403_FORBIDDEN) - allowed = get_allowed_specs(request.user) - if not allowed: - return Response({'error':'Invalid HOD designation or no mapping'}, status=status.HTTP_403_FORBIDDEN) - d = request.data - try: - stu = Student.objects.get(id__user__username=d['ta_username']) - except Student.DoesNotExist: - return Response({'error':'TA not found'}, status=status.HTTP_404_NOT_FOUND) - if stu.specialization not in allowed: - return Response({'error':'Specialization mismatch'}, status=status.HTTP_403_FORBIDDEN) - try: - fac = Faculty.objects.get(id__user__username=d['faculty_username']) - except Faculty.DoesNotExist: - return Response({'error':'Faculty not found'}, status=status.HTTP_404_NOT_FOUND) - - try: - a = Assignment.objects.create( - ta=stu, faculty=fac, - start_year=int(d['start_year']), start_month=int(d['start_month']), - end_year=int(d['end_year']), end_month=int(d['end_month']) - ) - return Response({'assignment_id': a.id}, status=status.HTTP_201_CREATED) - except KeyError: - return Response({'error':'Missing field'}, status=status.HTTP_400_BAD_REQUEST) - except Exception as e: - return Response({'error':str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) - -@api_view(['POST']) -@permission_classes([IsAuthenticated]) -@parser_classes([MultiPartParser, FormParser]) -def hod_upload_excel(request): - if not check_role(request,'hod'): - return Response({'error':'role=hod required'}, status=status.HTTP_403_FORBIDDEN) - allowed = get_allowed_specs(request.user) - if not allowed: - return Response({'error':'Invalid HOD designation or no mapping'}, status=status.HTTP_403_FORBIDDEN) - f = request.FILES.get('file') - if not f: - return Response({'error':'No file uploaded'}, status=status.HTTP_400_BAD_REQUEST) - - try: - df = pd.read_excel(f) - except: - return Response({'error':'Invalid Excel file'}, status=status.HTTP_400_BAD_REQUEST) - - created, errors = [], [] - for idx, row in df.iterrows(): - try: - stu = Student.objects.get(id__user__username=row['ta_username']) - if stu.specialization not in allowed: - raise PermissionError('Specialization mismatch') - fac = Faculty.objects.get(id__user__username=row['faculty_username']) - a = Assignment.objects.create( - ta=stu, faculty=fac, - start_year=int(row['start_year']), start_month=int(row['start_month']), - end_year=int(row['end_year']), end_month=int(row['end_month']) - ) - created.append(a.id) - except Exception as e: - errors.append({'row': int(idx), 'error': str(e)}) - - return Response({'created': created, 'errors': errors}) - -@api_view(['GET']) -@permission_classes([IsAuthenticated]) -def hod_pending(request): - if not check_role(request,'hod'): - return Response({'error':'role=hod required'}, status=status.HTTP_403_FORBIDDEN) - qs = StipendRequest.objects.filter(status=StipendRequest.FAC_APPROVED) - data = [{ - 'id': s.id, - 'ta': s.assignment.ta.id.user.username, - 'faculty': s.assignment.faculty.id.user.username, - 'month': s.month, 'year': s.year, - 'faculty_remark': s.faculty_remark - } for s in qs] - return Response({'stipends': data}) - -@api_view(['GET']) -@permission_classes([IsAuthenticated]) -def hod_approved(request): - if not check_role(request,'hod'): - return Response({'error':'role=hod required'}, status=status.HTTP_403_FORBIDDEN) - qs = StipendRequest.objects.filter(status=StipendRequest.HOD_APPROVED) - data = [{ - 'id': s.id, - 'ta': s.assignment.ta.id.user.username, - 'faculty': s.assignment.faculty.id.user.username, - 'month': s.month, 'year': s.year, - } for s in qs] - return Response({'stipends': data}) - -@api_view(['POST']) -@permission_classes([IsAuthenticated]) -def hod_approve(request, sid): - if not check_role(request,'hod'): - return Response({'error':'role=hod required'}, status=status.HTTP_403_FORBIDDEN) - try: - s = StipendRequest.objects.get(id=sid) - except StipendRequest.DoesNotExist: - return Response({'error':'Stipend not found'}, status=status.HTTP_404_NOT_FOUND) - - if s.status != StipendRequest.FAC_APPROVED: - return Response({'error':'Faculty must approve first'}, status=status.HTTP_400_BAD_REQUEST) - - s.status = StipendRequest.HOD_APPROVED - s.hod_remark = request.data.get('remark','') - s.save() - return Response({'success': True}) - - - - -@api_view(['GET']) -@permission_classes([IsAuthenticated]) -def faculty_assignments(request): - # if not check_role(request,'faculty'): - # return Response({'error':'role=faculty required'}, status=status.HTTP_403_FORBIDDEN) - qs = Assignment.objects.filter(faculty__id__username='skjain') - data = [{ - 'id': a.id, - 'ta_username': a.ta.id.user.username, - 'start_month': a.start_month, 'start_year': a.start_year, - 'end_month': a.end_month, 'end_year': a.end_year - } for a in qs] - return Response({'assignments': data}) - -from django.db.models import Q - -@api_view(['GET']) -@permission_classes([IsAuthenticated]) -def faculty_pending(request): - if not check_role(request,'faculty'): - return Response({'error':'role=faculty required'}, status=status.HTTP_403_FORBIDDEN) - now = datetime.now() - qs = StipendRequest.objects.filter( - assignment__faculty=request.user.faculty, - status=StipendRequest.PENDING - ).filter( - Q(year__lt=now.year) | - Q(year=now.year, month__lte=now.month) - ) - data = [{'id': s.id, 'ta': s.assignment.ta.id.user.username, - 'month': s.month, 'year': s.year} for s in qs] - return Response({'stipends': data}) - -@api_view(['GET']) -@permission_classes([IsAuthenticated]) -def faculty_approved(request): - if not check_role(request,'faculty'): - return Response({'error':'role=faculty required'}, status=status.HTTP_403_FORBIDDEN) - qs = StipendRequest.objects.filter( - assignment__faculty=request.user.faculty, - status=StipendRequest.FAC_APPROVED - ) - data = [{'id': s.id, 'ta': s.assignment.ta.id.user.username, - 'month': s.month, 'year': s.year} for s in qs] - return Response({'stipends': data}) - -@api_view(['POST']) -@permission_classes([IsAuthenticated]) -def faculty_approve(request, sid): - if not check_role(request,'faculty'): - return Response({'error':'role=faculty required'}, status=status.HTTP_403_FORBIDDEN) - try: - s = StipendRequest.objects.get(id=sid, assignment__faculty=request.user.faculty) - now = datetime.now() - if (s.year, s.month) > (now.year, now.month): - return Response({'error':'Cannot approve future'}, status=status.HTTP_400_BAD_REQUEST) - s.status = StipendRequest.FAC_APPROVED - s.faculty_remark = request.data.get('remark','') - s.save() - return Response({'success': True}) - except StipendRequest.DoesNotExist: - return Response({'error':'Stipend not found'}, status=status.HTTP_404_NOT_FOUND) - -# --- TA endpoints --- - -@api_view(['GET']) -@permission_classes([IsAuthenticated]) -def ta_stipends(request): - if not check_role(request,'ta'): - return Response({'error':'role=ta required'}, status=status.HTTP_403_FORBIDDEN) - qs = StipendRequest.objects.filter(assignment__ta=request.user.student) - data = [{'month': s.month, 'year': s.year, - 'status': s.status, 'faculty_remark': s.faculty_remark} - for s in qs] - return Response({'stipends': data}) - - -@api_view(['GET']) -@permission_classes([IsAuthenticated]) -@role_required(['student']) -def registered_slots(request): - try: - current_user = request.user - user_details = current_user.extrainfo - student = Student.objects.get(id=user_details) - session, semester_type = generate_current_session(datetime.datetime.now().year, student.curr_semester_no) - eligibility_resp = get_replace_registration_eligibility(timezone.now().date(), student.curr_semester_no, datetime.datetime.now().year) - if isinstance(eligibility_resp, JsonResponse): - return eligibility_resp - - # Exclude slots with pending drop requests - pending_drop_slots = CourseDropRequest.objects.filter( - student=student, - academic_year=session, - semester_type=semester_type, - status='Pending' - ).values_list('course_slot_id', flat=True) - - regs = course_registration.objects.filter( - student_id=student, - semester_id__semester_no=student.curr_semester_no - ).exclude( - course_slot_id__name__startswith='SW' - ).exclude( - course_slot_id__name__startswith='BL' - ).exclude( - course_slot_id__in=pending_drop_slots - ) - - payload = [] - for reg in regs: - slot = reg.course_slot_id - others = slot.courses.all().exclude(id=reg.course_id.id) - - payload.append({ - "id": slot.id, - "name": slot.name, - "academic_year": reg.session, - "semester_type": reg.semester_type, - "old_course": {"id": reg.course_id.id, "code": reg.course_id.code, "name" : reg.course_id.name}, - "new_courses": [ - {"id": c.id, "code": c.code, "name": c.name, "seats_available": max(c.max_seats - (course_registration.objects.filter(course_id=c, session = session, semester_type = semester_type).exclude(course_slot_id__name__startswith = 'BL').count()), 0)} for c in others - ], - }) - return JsonResponse(payload, safe=False) - except Student.DoesNotExist: - return Response({"error": "Student profile not found"}, status=400) - except Exception as e: - return Response({"error": str(e)}, status=500) - -@api_view(['POST']) -@permission_classes([IsAuthenticated]) -@role_required(['student']) -def batch_create_requests(request): - try: - current_user = request.user - user_details = current_user.extrainfo - student = Student.objects.get(id=user_details) - eligibility_resp = get_replace_registration_eligibility(timezone.now().date(), student.curr_semester_no, datetime.datetime.now().year) - if isinstance(eligibility_resp, JsonResponse): - return eligibility_resp - data = json.loads(request.body).get('requests', []) - - created = [] - errors = [] - - for idx, item in enumerate(data): + return Response({'error':'Member 1 is required'}, status=status.HTTP_400_BAD_REQUEST) + if 'member2' in request.data: try: - slot_id = item.get('course_slot') - old_id = item.get('old_course') - new_id = item.get('new_course') - ay = item.get('academic_year') - sem = item.get('semester_type') - - if not all([slot_id, old_id, new_id, ay, sem]): - raise ValueError("Missing required fields.") - - slot = CourseSlot.objects.get(pk=slot_id) - old = Courses.objects.get(pk=old_id) - new = Courses.objects.get(pk=new_id) - - reg = course_registration.objects.get( - student_id=student, - course_slot_id=slot, - course_id=old, - session=ay, - semester_type=sem - ) - - if not slot.courses.filter(id=new.id).exists(): - raise ValueError("New course not in selected slot.") - - with transaction.atomic(): - req, created_flag = CourseReplacementRequest.objects.get_or_create( - student=student, - course_slot=slot, - academic_year=ay, - semester_type=sem, - defaults={ - "old_course": old, - "new_course": new, - } - ) - - action = "created" - if not created_flag: - if req.old_course != old or req.new_course != new: - req.old_course = old - req.new_course = new - req.status = "Pending" - req.processed_at = None - req.save() - action = "updated" - else: - action = "unchanged" - - created.append({ - "id": req.id, - "slot": slot.name, - "old": old.code, - "new": new.code, - "status": req.status, - "action": action, - }) - - except Exception as e: - errors.append({"index": idx, "error": str(e)}) - - return JsonResponse({"created": created, "errors": errors}, status=201) - - except Exception as e: - return JsonResponse({"detail": "Something went wrong", "error": str(e)}, status=500) - - -@api_view(['GET']) -@permission_classes([IsAuthenticated]) -@role_required(['acadadmin']) -def admin_list_requests(request): - qs = CourseReplacementRequest.objects.select_related( - 'student', 'student__id__user', 'course_slot', 'old_course', 'new_course' - ).all().order_by('-created_at') - - year = request.GET.get('academic_year') - sem = request.GET.get('semester_type') - if year: - qs = qs.filter(academic_year=year) - if sem: - qs = qs.filter(semester_type=sem) - - out = [] - for r in qs: - out.append({ - 'id': r.id, - 'student': r.student.id.user.username, - 'student_name': r.student.id.user.get_full_name() or r.student.id.user.username, - 'slot': r.course_slot.name, - 'old_course': r.old_course.code, - 'old_course_name': r.old_course.name, - 'new_course': r.new_course.code, - 'new_course_name': r.new_course.name, - 'status': r.status, - 'academic_year': r.academic_year, - 'semester_type': r.semester_type, - 'created_at': r.created_at.isoformat(), - 'processed_at': r.processed_at.isoformat() if r.processed_at else None, - }) - return JsonResponse(out, safe=False) - - - -@api_view(['GET']) -@permission_classes([IsAuthenticated]) -@role_required(['student']) -def student_list_requests(request): - current_user = request.user - user_details = current_user.extrainfo - student = Student.objects.get(id=user_details) - current_reg = course_registration.objects.filter(student_id=student, semester_id__semester_no=student.curr_semester_no).first() - - if current_reg: - academic_year = current_reg.session - semester_type = current_reg.semester_type - else: - academic_year, semester_type = generate_current_session(datetime.datetime.now().year, student.curr_semester_no) - - qs = CourseReplacementRequest.objects.filter(student=student, academic_year=academic_year, semester_type = semester_type).select_related('old_course', 'new_course', 'course_slot').order_by('-created_at') - out = [] - for r in qs: - out.append({ - 'id': r.id, - 'slot': r.course_slot.name, - 'old_course': r.old_course.code, - 'old_course_name': r.old_course.name, - 'new_course': r.new_course.code, - 'new_course_name': r.new_course.name, - 'status': r.status, - 'academic_year': r.academic_year, - 'semester_type': r.semester_type, - 'created_at': r.created_at.isoformat(), - }) - return JsonResponse(out, safe=False) - -@api_view(['POST']) -@permission_classes([IsAuthenticated]) -@transaction.atomic -@role_required(['acadadmin']) -def allocate_all(request): - import json - try: - body = json.loads(request.body) - year = body.get('academic_year') - sem = body.get('semester_type') - except: - return JsonResponse({'error': 'Invalid JSON'}, status=400) - - pending = CourseReplacementRequest.objects.select_for_update().filter( - status="Pending", academic_year=year, semester_type=sem - ) - by_course = defaultdict(list) - for cr in pending: - by_course[cr.new_course].append(cr) - - queue = deque() - in_q = set() - for course, reqs in by_course.items(): - course = Courses.objects.select_for_update().get(pk=course.pk) - used = course_registration.objects.filter(course_id=course, session = year, semester_type = sem).exclude(course_slot_id__name__startswith = 'BL').count() - free = max(course.max_seats - used, 0) - if free > 0: - queue.append(course) - in_q.add(course) - - results = [] - while queue: - course = queue.popleft() - in_q.discard(course) - - while True: - used = course_registration.objects.filter(course_id=course, session=year, semester_type=sem).exclude(course_slot_id__name__startswith='BL').count() - free = max(course.max_seats - used, 0) - reqs = by_course[course] - if free <= 0 or not reqs: - break - - # Always pick one request at a time (FIFO) - cr = reqs[0] - cr.status = "Approved" - cr.processed_at = timezone.now() - cr.save(update_fields=['status', 'processed_at']) - results.append({'id': cr.id, 'status': 'Approved'}) - # print( - # "Looking for old_reg with:", - # "student_id=", cr.student, - # "course_slot_id=", cr.course_slot, - # "session=", cr.academic_year, - # "semester_type=", cr.semester_type - # ) - # swap registrations - old_reg = course_registration.objects.select_for_update().get( - student_id=cr.student, - course_slot_id=cr.course_slot, - session=cr.academic_year, - semester_type=cr.semester_type - ) - old_course = old_reg.course_id - # print(old_course) - semester_id = old_reg.semester_id - old_reg.delete() - - working_year, _ = parse_academic_year(cr.academic_year, cr.semester_type) - course_registration.objects.create( - student_id=cr.student, - course_slot_id=cr.course_slot, - course_id=course, - session=cr.academic_year, - semester_type=cr.semester_type, - semester_id=semester_id, - working_year=working_year - ) - by_course[course].remove(cr) - - # cascade enqueue old_course if it now has free seats and pending requests - used_old = course_registration.objects.filter(course_id=old_course, session=year, semester_type=sem).exclude(course_slot_id__name__startswith='BL').count() - free_old = max(old_course.max_seats - used_old, 0) - if free_old > 0 and by_course.get(old_course) and by_course[old_course] and old_course not in in_q: - queue.append(old_course) - in_q.add(old_course) - - # reject leftovers - for reqs in by_course.values(): - for cr in reqs: - cr.status = "Rejected" - cr.processed_at = timezone.now() - cr.save(update_fields=['status', 'processed_at']) - results.append({'id': cr.id, 'status': 'Rejected'}) - - return JsonResponse(results, safe=False) - - -# Change replacement request status back to pending -@api_view(['POST']) -@permission_classes([IsAuthenticated]) -@transaction.atomic -@role_required(['acadadmin']) -def revert_replacement_to_pending(request): - import json - import logging - logger = logging.getLogger(__name__) - - try: - body = json.loads(request.body) - request_ids = body.get('request_ids', []) - except json.JSONDecodeError: - return JsonResponse({'error': 'Invalid JSON format'}, status=400) - except Exception as e: - logger.error(f"Error parsing request body: {str(e)}") - return JsonResponse({'error': 'Invalid request format'}, status=400) - - if not request_ids or not isinstance(request_ids, list): - return JsonResponse({'error': 'request_ids must be a non-empty array'}, status=400) - - reverted_count = 0 - errors = [] - - for req_id in request_ids: - try: - req_id = int(req_id) - replacement_request = CourseReplacementRequest.objects.get(id=req_id) - - if replacement_request.status != 'Rejected': - errors.append({'id': req_id, 'error': f'Cannot revert request with status: {replacement_request.status}'}) - continue - - replacement_request.status = 'Pending' - replacement_request.processed_at = None - replacement_request.save(update_fields=['status', 'processed_at']) - reverted_count += 1 - - except CourseReplacementRequest.DoesNotExist: - errors.append({'id': req_id, 'error': 'Request not found'}) - except Exception as e: - logger.error(f"Error reverting replacement request {req_id}: {str(e)}") - errors.append({'id': req_id, 'error': str(e)}) - - return JsonResponse({ - 'reverted': reverted_count, - 'total': len(request_ids), - 'errors': errors - }) - - -# Delete replacement requests -@api_view(['POST']) -@permission_classes([IsAuthenticated]) -@transaction.atomic -@role_required(['acadadmin']) -def delete_replacement_requests(request): - import json - import logging - logger = logging.getLogger(__name__) - - try: - body = json.loads(request.body) - request_ids = body.get('request_ids', []) - except json.JSONDecodeError: - return JsonResponse({'error': 'Invalid JSON format'}, status=400) - except Exception as e: - logger.error(f"Error parsing request body: {str(e)}") - return JsonResponse({'error': 'Invalid request format'}, status=400) - - if not request_ids or not isinstance(request_ids, list): - return JsonResponse({'error': 'request_ids must be a non-empty array'}, status=400) - - deleted_count = 0 - errors = [] - - for req_id in request_ids: - try: - req_id = int(req_id) - replacement_request = CourseReplacementRequest.objects.get(id=req_id) - replacement_request.delete() - deleted_count += 1 - except CourseReplacementRequest.DoesNotExist: - errors.append({'id': req_id, 'error': 'Request not found'}) - except Exception as e: - logger.error(f"Error deleting replacement request {req_id}: {str(e)}") - errors.append({'id': req_id, 'error': str(e)}) - - return JsonResponse({ - 'deleted': deleted_count, - 'total': len(request_ids), - 'errors': errors - }) - - -@api_view(['GET']) -@permission_classes([IsAuthenticated]) -@role_required(['student']) -def student_registrations_for_drop(request): - """ - GET /api/student/registrations/ - List all active registrations for the logged-in student. - Excludes courses with pending replacement requests. - """ - current_user = request.user - user_details = current_user.extrainfo - student = Student.objects.get(id=user_details) - eligibility_resp = get_drop_registration_eligibility(timezone.now().date(), student.curr_semester_no, datetime.datetime.now().year) - if isinstance(eligibility_resp, JsonResponse): - return eligibility_resp - - current_year = datetime.datetime.now().year - academic_year, semester_type = generate_current_session(current_year, student.curr_semester_no) - - # Exclude slots with pending replacement or drop requests - pending_replacement_slots = CourseReplacementRequest.objects.filter( - student=student, - academic_year=academic_year, - semester_type=semester_type, - status='Pending' - ).values_list('course_slot_id', flat=True) - - pending_drop_slots = CourseDropRequest.objects.filter( - student=student, - academic_year=academic_year, - semester_type=semester_type, - status='Pending' - ).values_list('course_slot_id', flat=True) - - regs = course_registration.objects.filter( - student_id=student, - semester_id__semester_no=student.curr_semester_no - ).exclude( - course_slot_id__in=pending_replacement_slots - ).exclude( - course_slot_id__in=pending_drop_slots - ).select_related('course_id', 'course_slot_id').order_by('course_slot_id__name') - - out = [] - for reg in regs: - out.append({ - 'id': reg.id, - 'slot': reg.course_slot_id.name, - 'course': reg.course_id.code, - 'course_name': reg.course_id.name, - 'academic_year': reg.session, - 'semester_type': reg.semester_type, - }) - return Response(out, status=status.HTTP_200_OK) - - -@api_view(['POST']) -@permission_classes([IsAuthenticated]) -@role_required(['student']) -def drop_course(request): - """ - POST /api/student/drop-course/ - Body: { "registration_id": } - Creates a drop request instead of directly dropping the course. - Validates eligibility before creating the request. - """ - try: - current_user = request.user - user_details = getattr(current_user, 'extrainfo', None) - if not user_details: - return Response( - {'error': 'User profile not found'}, - status=status.HTTP_404_NOT_FOUND - ) - - student = Student.objects.select_related('id__user', 'batch_id').get(id=user_details) - - eligibility_resp = get_drop_registration_eligibility( - timezone.now().date(), - student.curr_semester_no, - datetime.datetime.now().year - ) - if isinstance(eligibility_resp, JsonResponse): - return eligibility_resp - - reg_id = request.data.get('registration_id') - if not reg_id: - return Response( - {'error': 'registration_id is required'}, - status=status.HTTP_400_BAD_REQUEST - ) - - try: - reg_id = int(reg_id) - except (ValueError, TypeError): - return Response( - {'error': 'Invalid registration_id format'}, - status=status.HTTP_400_BAD_REQUEST - ) - - with transaction.atomic(): - reg = course_registration.objects.select_related( - 'course_id', 'course_slot_id' - ).get(id=reg_id, student_id=student) - - drop_request, created = CourseDropRequest.objects.get_or_create( - student=student, - course_slot=reg.course_slot_id, - academic_year=reg.session, - semester_type=reg.semester_type, - defaults={'course': reg.course_id} - ) - - if not created: - if drop_request.status == "Pending": - return Response( - {'error': 'Drop request already pending for this slot'}, - status=status.HTTP_400_BAD_REQUEST - ) - drop_request.course = reg.course_id - drop_request.status = "Pending" - drop_request.processed_at = None - drop_request.save(update_fields=['course', 'status', 'processed_at']) - - return Response( - { - 'status': 'pending', - 'request_id': drop_request.id, - 'registration_id': reg_id, - 'message': 'Drop request submitted successfully. Waiting for Academic approval.' - }, - status=status.HTTP_201_CREATED if created else status.HTTP_200_OK - ) - - except Student.DoesNotExist: - return Response( - {'error': 'Student profile not found'}, - status=status.HTTP_404_NOT_FOUND - ) - except course_registration.DoesNotExist: - return Response( - {'error': 'Course registration not found or does not belong to you'}, - status=status.HTTP_404_NOT_FOUND - ) - except Exception as e: - import logging - logger = logging.getLogger(__name__) - logger.error(f"Drop course error for user {request.user.username}: {str(e)}", exc_info=True) - return Response( - {'error': 'An error occurred while processing your request'}, - status=status.HTTP_500_INTERNAL_SERVER_ERROR - ) - -@api_view(['GET']) -@permission_classes([IsAuthenticated]) -@role_required(['student']) -def student_calendar_view(request): - calendar_entries = Calendar.objects.all().order_by('from_date') - - result = [ - { - "from_date": entry.from_date, - "to_date": entry.to_date, - "description": entry.description, - } - for entry in calendar_entries - ] - - return Response({"calendar_events": result}) - -# List all drop requests for the logged-in student for current semester -@api_view(['GET']) -@permission_classes([IsAuthenticated]) -@role_required(['student']) -def student_list_drop_requests(request): - try: - current_user = request.user - user_details = getattr(current_user, 'extrainfo', None) - if not user_details: - return JsonResponse({'error': 'User profile not found'}, status=404) - - student = Student.objects.select_related('id__user').get(id=user_details) - - current_reg = course_registration.objects.filter( - student_id=student, - semester_id__semester_no=student.curr_semester_no - ).only('session', 'semester_type').first() - - if current_reg: - academic_year = current_reg.session - semester_type = current_reg.semester_type - else: - academic_year, semester_type = generate_current_session( - datetime.datetime.now().year, - student.curr_semester_no - ) - - qs = CourseDropRequest.objects.filter( - student=student, - academic_year=academic_year, - semester_type=semester_type - ).select_related('course', 'course_slot').order_by('-created_at') - - out = [ - { - 'id': r.id, - 'slot': r.course_slot.name, - 'course': r.course.code, - 'course_name': r.course.name, - 'status': r.status, - 'academic_year': r.academic_year, - 'semester_type': r.semester_type, - 'created_at': r.created_at.isoformat(), - 'processed_at': r.processed_at.isoformat() if r.processed_at else None, - } - for r in qs - ] - - return JsonResponse(out, safe=False) - - except Student.DoesNotExist: - return JsonResponse({'error': 'Student profile not found'}, status=404) - except Exception as e: - import logging - logger = logging.getLogger(__name__) - logger.error(f"Error listing drop requests for {request.user.username}: {str(e)}", exc_info=True) - return JsonResponse({'error': 'An error occurred while fetching requests'}, status=500) - - # Lists all course drop requests with optional filtering -@api_view(['GET']) -@permission_classes([IsAuthenticated]) -@role_required(['acadadmin']) -def admin_list_drop_requests(request): - try: - qs = CourseDropRequest.objects.select_related( - 'student__id__user', - 'course', - 'course_slot' - ).all().order_by('-created_at') - - year = request.GET.get('academic_year', '').strip() - sem = request.GET.get('semester_type', '').strip() - - if year: - qs = qs.filter(academic_year=year) - if sem: - qs = qs.filter(semester_type=sem) - - qs = qs[:500] - - out = [ - { - 'id': r.id, - 'student': r.student.id.user.username, - 'student_name': f"{r.student.id.user.first_name} {r.student.id.user.last_name}".strip(), - 'slot': r.course_slot.name, - 'course': r.course.code, - 'course_name': r.course.name, - 'status': r.status, - 'academic_year': r.academic_year, - 'semester_type': r.semester_type, - 'created_at': r.created_at.isoformat(), - 'processed_at': r.processed_at.isoformat() if r.processed_at else None, - } - for r in qs - ] - - return JsonResponse(out, safe=False) - - except Exception as e: - import logging - logger = logging.getLogger(__name__) - logger.error(f"Error listing drop requests for admin: {str(e)}", exc_info=True) - return JsonResponse({'error': 'An error occurred while fetching requests'}, status=500) - -# Processes multiple drop requests in batch -@api_view(['POST']) -@permission_classes([IsAuthenticated]) -@transaction.atomic -@role_required(['acadadmin']) -def approve_drop_requests(request): - import json - import logging - logger = logging.getLogger(__name__) - - try: - body = json.loads(request.body) - request_ids = body.get('request_ids', []) - action = body.get('action', 'approve').lower().strip() - except json.JSONDecodeError: - return JsonResponse({'error': 'Invalid JSON format'}, status=400) - except Exception as e: - logger.error(f"Error parsing request body: {str(e)}") - return JsonResponse({'error': 'Invalid request format'}, status=400) - - if not request_ids or not isinstance(request_ids, list): - return JsonResponse({'error': 'request_ids must be a non-empty array'}, status=400) - - if action not in ['approve', 'reject']: - return JsonResponse({'error': 'action must be either "approve" or "reject"'}, status=400) - - results = [] - success_count = 0 - error_count = 0 - - for req_id in request_ids: - try: - req_id = int(req_id) - - drop_request = CourseDropRequest.objects.select_for_update().select_related( - 'student', 'course', 'course_slot' - ).get(id=req_id) - - if drop_request.status != "Pending": - results.append({ - 'id': req_id, - 'status': 'already_processed', - 'current_status': drop_request.status - }) - continue - - if action == 'approve': - try: - reg = course_registration.objects.get( - student_id=drop_request.student, - course_slot_id=drop_request.course_slot, - course_id=drop_request.course, - session=drop_request.academic_year, - semester_type=drop_request.semester_type - ) - reg.delete() - drop_request.status = "Approved" - drop_request.processed_at = timezone.now() - drop_request.save(update_fields=['status', 'processed_at']) - results.append({'id': req_id, 'status': 'approved'}) - success_count += 1 - logger.info(f"Approved drop request {req_id} for student {drop_request.student.id}") - except course_registration.DoesNotExist: - drop_request.status = "Approved" - drop_request.processed_at = timezone.now() - drop_request.save(update_fields=['status', 'processed_at']) - results.append({'id': req_id, 'status': 'approved', 'note': 'registration_not_found'}) - success_count += 1 - logger.warning(f"Approved drop request {req_id} but registration not found") - else: # reject - drop_request.status = "Rejected" - drop_request.processed_at = timezone.now() - drop_request.save(update_fields=['status', 'processed_at']) - results.append({'id': req_id, 'status': 'rejected'}) - success_count += 1 - logger.info(f"Rejected drop request {req_id}") - - except (ValueError, TypeError): - results.append({'id': req_id, 'status': 'error', 'detail': 'Invalid ID format'}) - error_count += 1 - except CourseDropRequest.DoesNotExist: - results.append({'id': req_id, 'status': 'not_found'}) - error_count += 1 - except Exception as e: - logger.error(f"Error processing drop request {req_id}: {str(e)}", exc_info=True) - results.append({'id': req_id, 'status': 'error', 'detail': 'Processing error'}) - error_count += 1 - - return JsonResponse({ - 'results': results, - 'summary': { - 'total': len(request_ids), - 'success': success_count, - 'errors': error_count - } - }, safe=False) - - -# Delete drop course requests -@api_view(['POST']) -@permission_classes([IsAuthenticated]) -@transaction.atomic -@role_required(['acadadmin']) -def delete_drop_requests(request): - import json - import logging - logger = logging.getLogger(__name__) - - try: - body = json.loads(request.body) - request_ids = body.get('request_ids', []) - except json.JSONDecodeError: - return JsonResponse({'error': 'Invalid JSON format'}, status=400) - except Exception as e: - logger.error(f"Error parsing request body: {str(e)}") - return JsonResponse({'error': 'Invalid request format'}, status=400) - - if not request_ids or not isinstance(request_ids, list): - return JsonResponse({'error': 'request_ids must be a non-empty array'}, status=400) - - deleted_count = 0 - errors = [] - - for req_id in request_ids: - try: - req_id = int(req_id) - drop_request = CourseDropRequest.objects.get(id=req_id) - drop_request.delete() - deleted_count += 1 - except CourseDropRequest.DoesNotExist: - errors.append({'id': req_id, 'error': 'Request not found'}) - except Exception as e: - logger.error(f"Error deleting drop request {req_id}: {str(e)}") - errors.append({'id': req_id, 'error': str(e)}) - - return JsonResponse({ - 'deleted': deleted_count, - 'total': len(request_ids), - 'errors': errors - }) - - -# ===================== COURSE ADD REQUEST APIs ===================== - -# Lists all add course requests for the current student's active semester -@api_view(['GET']) -@permission_classes([IsAuthenticated]) -@role_required(['student']) -def student_list_add_requests(request): - try: - current_user = request.user - user_details = getattr(current_user, 'extrainfo', None) - if not user_details: - return JsonResponse({'error': 'User profile not found'}, status=404) - - student = Student.objects.select_related('id__user').get(id=user_details) - - current_reg = course_registration.objects.filter( - student_id=student, - semester_id__semester_no=student.curr_semester_no - ).only('session', 'semester_type').first() - - if current_reg: - academic_year = current_reg.session - semester_type = current_reg.semester_type + user2 = User.objects.get(username=request.data['member2']) + member2 = user2.extrainfo + request.data['member2'] = member2 + except: + return Response({'error':'Wrong username of member 2. User does not exist.'}, status=status.HTTP_400_BAD_REQUEST) else: - academic_year, semester_type = generate_current_session( - datetime.datetime.now().year, - student.curr_semester_no - ) - - qs = CourseAddRequest.objects.filter( - student=student, - academic_year=academic_year, - semester_type=semester_type - ).select_related('course', 'course_slot').order_by('-created_at') - - out = [ - { - 'id': r.id, - 'slot': r.course_slot.name, - 'course': r.course.code, - 'course_name': r.course.name, - 'status': r.status, - 'academic_year': r.academic_year, - 'semester_type': r.semester_type, - 'created_at': r.created_at.isoformat(), - 'processed_at': r.processed_at.isoformat() if r.processed_at else None, - } - for r in qs - ] - - return JsonResponse(out, safe=False) - - except Student.DoesNotExist: - return JsonResponse({'error': 'Student profile not found'}, status=404) - except Exception as e: - import logging - logger = logging.getLogger(__name__) - logger.error(f"Error listing add requests for {request.user.username}: {str(e)}", exc_info=True) - return JsonResponse({'error': 'An error occurred while fetching requests'}, status=500) - -# Lists all course add requests with optional filtering -@api_view(['GET']) -@permission_classes([IsAuthenticated]) -@role_required(['acadadmin']) -def admin_list_add_requests(request): - try: - qs = CourseAddRequest.objects.select_related( - 'student__id__user', - 'course', - 'course_slot' - ).all().order_by('-created_at') - - year = request.GET.get('academic_year', '').strip() - sem = request.GET.get('semester_type', '').strip() - - if year: - qs = qs.filter(academic_year=year) - if sem: - qs = qs.filter(semester_type=sem) - - qs = qs[:500] - - out = [ - { - 'id': r.id, - 'student': r.student.id.user.username, - 'student_name': f"{r.student.id.user.first_name} {r.student.id.user.last_name}".strip(), - 'slot': r.course_slot.name, - 'course': r.course.code, - 'course_name': r.course.name, - 'status': r.status, - 'academic_year': r.academic_year, - 'semester_type': r.semester_type, - 'created_at': r.created_at.isoformat(), - 'processed_at': r.processed_at.isoformat() if r.processed_at else None, - } - for r in qs - ] - - return JsonResponse(out, safe=False) - - except Exception as e: - import logging - logger = logging.getLogger(__name__) - logger.error(f"Error listing add requests for admin: {str(e)}", exc_info=True) - return JsonResponse({'error': 'An error occurred while fetching requests'}, status=500) - -# Processes multiple add requests in batch -@api_view(['POST']) -@permission_classes([IsAuthenticated]) -@transaction.atomic -@role_required(['acadadmin']) -def approve_add_requests(request): - import json - import logging - logger = logging.getLogger(__name__) - - try: - body = json.loads(request.body) - request_ids = body.get('request_ids', []) - action = body.get('action', 'approve').lower().strip() - except json.JSONDecodeError: - return JsonResponse({'error': 'Invalid JSON format'}, status=400) - except Exception as e: - logger.error(f"Error parsing request body: {str(e)}") - return JsonResponse({'error': 'Invalid request format'}, status=400) - - if not request_ids or not isinstance(request_ids, list): - return JsonResponse({'error': 'request_ids must be a non-empty array'}, status=400) - - if action not in ['approve', 'reject']: - return JsonResponse({'error': 'action must be either "approve" or "reject"'}, status=400) - - results = [] - success_count = 0 - error_count = 0 - - for req_id in request_ids: - try: - req_id = int(req_id) - - add_request = CourseAddRequest.objects.select_related( - 'student', 'course', 'course_slot', 'student__batch_id' - ).select_for_update(of=('self',)).get(id=req_id) - - if add_request.status != "Pending": - results.append({ - 'id': req_id, - 'status': 'already_processed', - 'current_status': add_request.status - }) - continue - - if action == 'approve': - existing = course_registration.objects.filter( - student_id=add_request.student, - course_id=add_request.course, - session=add_request.academic_year, - semester_type=add_request.semester_type - ).exists() - - if existing: - add_request.status = "Rejected" - add_request.processed_at = timezone.now() - add_request.save(update_fields=['status', 'processed_at']) - results.append({'id': req_id, 'status': 'error', 'detail': 'Already registered'}) - error_count += 1 - continue - - try: - semester = Semester.objects.get( - curriculum=add_request.student.batch_id.curriculum, - semester_no=add_request.student.curr_semester_no - ) - except Semester.DoesNotExist: - add_request.status = "Rejected" - add_request.processed_at = timezone.now() - add_request.save(update_fields=['status', 'processed_at']) - results.append({'id': req_id, 'status': 'error', 'detail': 'Semester not found'}) - error_count += 1 - continue - - registration_type = 'Regular' - try: - student_grade = Student_grades.objects.filter( - roll_no=add_request.student.id.user.username, - course_id=add_request.course - ).order_by('-year', '-semester').first() - - if student_grade and student_grade.grade: - backlog_grades = ['F', 'X', 'CD'] - improvement_grades = ['C', 'D+', 'D'] - - if student_grade.grade in backlog_grades: - registration_type = 'Backlog' - elif student_grade.grade in improvement_grades: - registration_type = 'Improvement' - except Exception as grade_error: - logger.warning(f"Could not determine registration type for request {req_id}: {str(grade_error)}") - - try: - reg = course_registration( - student_id=add_request.student, - course_id=add_request.course, - course_slot_id=add_request.course_slot, - semester_id=semester, - session=add_request.academic_year, - semester_type=add_request.semester_type, - working_year=datetime.datetime.now().year, - registration_type=registration_type - ) - reg.save() - - if add_request.old_course_registration: - course_replacement.objects.create( - old_course_registration=add_request.old_course_registration, - new_course_registration=reg - ) - logger.info(f"Created course_replacement for request {req_id}: {add_request.old_course_registration.id} -> {reg.id}") - - add_request.status = "Approved" - add_request.processed_at = timezone.now() - add_request.save(update_fields=['status', 'processed_at']) - results.append({'id': req_id, 'status': 'approved'}) - success_count += 1 - logger.info(f"Approved add request {req_id} for student {add_request.student.id}") - except Exception as reg_error: - logger.error(f"Error creating registration for request {req_id}: {str(reg_error)}", exc_info=True) - results.append({'id': req_id, 'status': 'error', 'detail': f'Registration failed: {str(reg_error)}'}) - error_count += 1 - else: # reject - add_request.status = "Rejected" - add_request.processed_at = timezone.now() - add_request.save(update_fields=['status', 'processed_at']) - results.append({'id': req_id, 'status': 'rejected'}) - success_count += 1 - logger.info(f"Rejected add request {req_id}") - - except (ValueError, TypeError): - results.append({'id': req_id, 'status': 'error', 'detail': 'Invalid ID format'}) - error_count += 1 - except CourseAddRequest.DoesNotExist: - results.append({'id': req_id, 'status': 'not_found'}) - error_count += 1 - except Exception as e: - logger.error(f"Error processing add request {req_id}: {str(e)}", exc_info=True) - results.append({'id': req_id, 'status': 'error', 'detail': f'Processing error: {str(e)}'}) - error_count += 1 - - return JsonResponse({ - 'results': results, - 'summary': { - 'total': len(request_ids), - 'success': success_count, - 'errors': error_count - } - }, safe=False) - - -# Delete add course requests -@api_view(['POST']) -@permission_classes([IsAuthenticated]) -@transaction.atomic -@role_required(['acadadmin']) -def delete_add_requests(request): - import json - import logging - logger = logging.getLogger(__name__) - - try: - body = json.loads(request.body) - request_ids = body.get('request_ids', []) - except json.JSONDecodeError: - return JsonResponse({'error': 'Invalid JSON format'}, status=400) - except Exception as e: - logger.error(f"Error parsing request body: {str(e)}") - return JsonResponse({'error': 'Invalid request format'}, status=400) - - if not request_ids or not isinstance(request_ids, list): - return JsonResponse({'error': 'request_ids must be a non-empty array'}, status=400) - - deleted_count = 0 - errors = [] - - for req_id in request_ids: - try: - req_id = int(req_id) - add_request = CourseAddRequest.objects.get(id=req_id) - add_request.delete() - deleted_count += 1 - except CourseAddRequest.DoesNotExist: - errors.append({'id': req_id, 'error': 'Request not found'}) - except Exception as e: - logger.error(f"Error deleting add request {req_id}: {str(e)}") - errors.append({'id': req_id, 'error': str(e)}) - - return JsonResponse({ - 'deleted': deleted_count, - 'total': len(request_ids), - 'errors': errors - }) - - -@api_view(['POST']) -@authentication_classes([TokenAuthentication]) -@permission_classes([IsAuthenticated]) -@role_required(['acadadmin']) -def student_search(request): - roll_no = request.data.get('rollno','').upper() - if not roll_no: - return JsonResponse({'error':'rollno is required'},status=400) - student = Student.objects.filter(id_id=roll_no).first() - if not student: - return JsonResponse({'error':'Student record not found'},status=400) - extra = student.id - user = extra.user - data = { - 'roll_no':roll_no, - 'full_name':f"{user.first_name} {user.last_name}".strip(), - 'date_of_birth':str(extra.date_of_birth), - 'user_status':extra.user_status, - 'address':extra.address, - 'phone_no':extra.phone_no, - 'department':extra.department.name if extra.department else None, - 'programme':student.programme, - 'batch':student.batch, - 'batch_name':student.batch_id.name if student.batch_id else None, - 'discipline':student.batch_id.discipline.name if student.batch_id and student.batch_id.discipline else None, - 'curriculum':student.batch_id.curriculum.name if student.batch_id and student.batch_id.curriculum else None, - 'cpi':student.cpi, - 'category':student.category, - 'father_name':student.father_name, - 'mother_name':student.mother_name, - 'hall_no':student.hall_no, - 'room_no':student.room_no, - 'specialization':student.specialization, - 'curr_semester_no':student.curr_semester_no, - } - return JsonResponse(data,status=200) - - -@api_view(['GET']) -@authentication_classes([TokenAuthentication]) -@permission_classes([IsAuthenticated]) -@role_required(['student']) -def student_registration_semesters_view(request): - """ - Return a list of distinct semesters in which the student has registrations. - For new students, also include their current semester even if no registrations exist. - """ - try: - roll_number = request.user.username - student = Student.objects.get(id_id=roll_number) - - # Pull distinct (semester_no, semester_type) from the student's course registrations - qs = (course_registration.objects - .filter(student_id=student) - .values_list('semester_id__semester_no', 'semester_type') - .distinct() - .order_by('semester_id__semester_no')) - - unique = OrderedDict() - for sem_no, sem_type in qs: - label = make_label(sem_no, sem_type or "") - unique[(sem_no, sem_type)] = label - - # For new students who haven't registered for any courses yet, - # include their current semester - if not unique and student.curr_semester_no: - # Determine semester type based on semester number (odd/even) - current_sem_type = "Odd Semester" if student.curr_semester_no % 2 == 1 else "Even Semester" - current_label = make_label(student.curr_semester_no, current_sem_type) - unique[(student.curr_semester_no, current_sem_type)] = current_label - - semesters = [ - {"semester_no": no, "semester_type": typ, "label": lbl} - for (no, typ), lbl in unique.items() - ] - - return JsonResponse({"success": True, "semesters": semesters}, status=200) - - except Student.DoesNotExist: - return JsonResponse({"success": False, "error": "Student not found."}, status=404) - except Exception as e: - return JsonResponse({"success": False, "error": str(e)}, status=500) - - -@api_view(["GET"]) -@permission_classes([IsAuthenticated]) -@role_required(['student']) -def student_filled(request): - roll_number = request.user.username - student = Student.objects.get(id_id=roll_number) - semester_no = student.curr_semester_no - done = FeedbackFilled.objects.filter(student=student, semester_no = semester_no).exists() - return Response({"filled": done}) - -@api_view(["GET"]) -@permission_classes([IsAuthenticated]) -@role_required(['student']) -def student_questions(request): - try: - roll_number = request.user.username - student = Student.objects.get(id_id=roll_number) - except Student.DoesNotExist: - return Response({"detail": "Student profile not found."}, status=status.HTTP_404_NOT_FOUND) - - semester_no = student.curr_semester_no - - filled = FeedbackFilled.objects.filter( - student=student, - semester_no=semester_no - ).exists() - - registrations = course_registration.objects.filter( - student_id=student, - semester_id__semester_no=semester_no, - ).select_related("course_id") - - courses = [] - for reg in registrations: - course = reg.course_id - academic_year, _ = parse_academic_year(reg.session, reg.semester_type) - instructor_entry = CourseInstructor.objects.filter( - course_id=course, - semester_type=reg.semester_type, - year = academic_year - - ).first() - - instructor_id = instructor_entry.id if instructor_entry else None - instructor_name = ( - f"{instructor_entry.instructor_id.id.user.first_name} {instructor_entry.instructor_id.id.user.last_name}" - if instructor_entry else "" - ) - - courses.append({ - "course_id": course.id, - "code": course.code, - "name": course.name, - "instructor_id": instructor_id, - "instructor_name": instructor_name, - }) - - questions = [ - { - "id": question.id, - "section": question.section, - "text": question.text, - "options": [{"id": option.id, "text": option.text} for option in question.options.all()], - } - for question in FeedbackQuestion.objects.all() - ] - - return Response({ - "filled": filled, - "courses": courses, - "questions": questions, - }) - -@api_view(["POST"]) -@permission_classes([IsAuthenticated]) -@role_required(['student']) -def student_submit(request): - try: - roll_number = request.user.username - student = Student.objects.get(id_id=roll_number) - except Student.DoesNotExist: - return Response({"detail": "Student profile not found."}, status=status.HTTP_404_NOT_FOUND) - - semester_no = student.curr_semester_no - data = request.data - if FeedbackFilled.objects.filter(student=student, semester_no = semester_no).exists(): - return Response({"detail":"Already filled."}, status=status.HTTP_409_CONFLICT) - - with transaction.atomic(): - for r in data["responses"]: - - reg = course_registration.objects.get(student_id =student, course_id_id = r["course_id"], semester_id__semester_no = student.curr_semester_no) - FeedbackResponse.objects.create( - question_id = r["question_id"], - option_id = r.get("option_id"), - text_answer = r.get("text_answer",""), - course_id = r["course_id"], - section = r["section"], - session = reg.session, - semester_type = reg.semester_type, - ) - FeedbackFilled.objects.create(student=student, semester_no = student.curr_semester_no) - - return Response({"detail":"Submitted"}, status=status.HTTP_201_CREATED) - -@api_view(["GET"]) -@permission_classes([IsAuthenticated]) -def inst_courses(request): - """ - GET /inst/courses/?session=&semester_type= - Returns the list of courses the logged-in instructor is teaching. - """ - fac = request.user.username - sess = request.query_params.get("session") - semt = request.query_params.get("semester_type") - if not sess or not semt: - return Response({"detail": "Provide 'session' and 'semester_type'."}, status=status.HTTP_400_BAD_REQUEST) - - academic_year, _ = parse_academic_year(sess, semt) - regs = CourseInstructor.objects.filter( - instructor_id_id=fac, - year=academic_year, - semester_type=semt, - ) - - return Response([{ - "course_id": cr.course_id.id, - "code": cr.course_id.code, - "name": cr.course_id.name, - } for cr in regs]) - - -@api_view(["GET"]) -@permission_classes([IsAuthenticated]) -def inst_all_stats(request): - """ - GET /inst/stats/all/?session=&semester_type=&course_id= - Returns per-question counts + comments for the 'Course Instructor' section. - If no responses yet, returns {"detail": "No responses found till now."}. - """ - sess = request.query_params.get("session") - semt = request.query_params.get("semester_type") - cid = request.query_params.get("course_id") - - academic_year, _ = parse_academic_year(sess, semt) - if not CourseInstructor.objects.filter( - course_id_id=cid, - instructor_id_id=request.user.username, - year=academic_year, - semester_type=semt, - ).exists(): - return Response( - {"error": "Access denied: you are not assigned as instructor for this course."}, - status=status.HTTP_403_FORBIDDEN - ) - - has_any = FeedbackResponse.objects.filter( - course_id=cid, - session=sess, - semester_type=semt, - question__section="instructor", - ).exists() - - if not has_any: - return Response( - {"detail": "No responses found till now."}, - status=status.HTTP_200_OK - ) - - out = [] - questions = FeedbackQuestion.objects.filter(section="instructor").order_by("order") - - for q in questions: - base = FeedbackResponse.objects.filter( - question=q, - course_id=cid, - session=sess, - semester_type=semt, - ) - counts = { - o.text: base.filter(option=o).count() - for o in FeedbackOption.objects.filter(question=q) - } - comments = list( - base.filter(option__isnull=True).values_list("text_answer", flat=True) - ) - out.append({ - "question_id": q.id, - "text": q.text, - "counts": counts, - "comments": comments, - }) - - return Response(out, status=status.HTTP_200_OK) - - -@api_view(["GET"]) -@permission_classes([IsAuthenticated]) -@role_required(['acadadmin']) -def admin_course_list(request): - """ - GET /admin/courses/?session=&semester_type= - """ - sess = request.query_params.get("session") - semt = request.query_params.get("semester_type") - if not sess or not semt: - return Response( - {"detail":"Provide 'session' & 'semester_type'."}, - status=status.HTTP_400_BAD_REQUEST - ) - - regs = FeedbackResponse.objects.filter( - session=sess, - semester_type=semt, - ).select_related("course").distinct() - - seen = set() - courses = [] - for reg in regs: - c = reg.course - if c.id in seen: - continue - seen.add(c.id) - courses.append({ - "course_id": c.id, - "code": c.code, - "name": c.name, - }) - - return Response(courses) - - -@api_view(["GET"]) -@permission_classes([IsAuthenticated]) -@role_required(['acadadmin']) -def admin_all_stats(request): - """ - GET /admin/stats/all/?session=&semester_type=&course_id= - Returns a JSON payload grouped by section: - { - sections: [ - { - section: "", - questions: [ - { - question_id, text, - counts: { option_text: count, ... }, - comments: [ ... ] - }, - ... - ] - }, - ... - ] - } - """ - sess = request.query_params.get("session") - semt = request.query_params.get("semester_type") - cid = request.query_params.get("course_id") - if not sess or not semt or not cid: - return Response( - {"detail":"Provide 'session', 'semester_type', and 'course_id'."}, - status=status.HTTP_400_BAD_REQUEST - ) - - raw = [] - for q in FeedbackQuestion.objects.all().order_by("order"): - base = FeedbackResponse.objects.filter( - question=q, - course_id=cid, - session=sess, - semester_type=semt, - ) - counts = { - o.text: base.filter(option=o).count() - for o in FeedbackOption.objects.filter(question=q) - } - comments = list( - base.filter(option__isnull=True) - .values_list("text_answer", flat=True) - ) - raw.append({ - "section": q.section, - "question_id": q.id, - "text": q.text, - "counts": counts, - "comments": comments, - }) - - grouped = {} - for item in raw: - sec = item["section"] - grouped.setdefault(sec, []).append({ - "question_id": item["question_id"], - "text": item["text"], - "counts": item["counts"], - "comments": item["comments"], - }) - - response = { - "sections": [ - {"section": sec, "questions": qs} - for sec, qs in grouped.items() - ] - } - - if not raw or all(len(v["questions"]) == 0 for v in response["sections"]): - return Response({"detail":"No responses found till now."}) - - return Response(response) - - - -@api_view(["GET"]) -@permission_classes([IsAuthenticated]) -@role_required(['acadadmin']) -def list_batches(request): - batches = Batch.objects.filter(running_batch=True).select_related("discipline").order_by("year", "name") - result = [] - for b in batches: - label = f"{b.name} {b.discipline.acronym} {b.year}" - result.append({"id": b.id, "label": label, "year": b.year}) - return Response(result) - -@api_view(["GET"]) -@permission_classes([IsAuthenticated]) -@role_required(['acadadmin']) -def list_students_in_batch(request): - batch_id = request.query_params.get("batch_id") - if not batch_id: - return Response({"detail": "batch_id required."}, status=status.HTTP_400_BAD_REQUEST) - students = Student.objects.filter(batch_id__id=batch_id) - result = [] - for st in students: - cb = st.batch_id - cb_label = f"{cb.name} {cb.discipline.acronym} {cb.year}" - result.append({ - "id": st.id_id, - "username": str(st.id_id), - "current_batch": cb_label, - "current_batch_id": cb.id, - "current_batch_year": st.batch, - }) - return Response(result) - -@api_view(["POST"]) -@permission_classes([IsAuthenticated]) -@role_required(['acadadmin']) -def apply_batch_changes(request): - data = request.data - user = request.user - errors = [] - - with transaction.atomic(): - for idx, pair in enumerate(data): - sid = pair.get("student_id") - nid = pair.get("new_batch_id") - nyear = pair.get("new_batch_year") - if not sid or not nid or nyear is None: - errors.append({"index": idx, "detail": "student_id, new_batch_id, new_batch_year required."}) - continue - try: - student = Student.objects.get(id=sid) - except Student.DoesNotExist: - errors.append({"index": idx, "detail": f"Student {sid} not found."}) - continue - - old_batch = student.batch_id - if old_batch and old_batch.id == nid and student.batch == nyear: - continue - try: - new_batch = Batch.objects.get(id=nid) - except Batch.DoesNotExist: - errors.append({"index": idx, "detail": f"Batch {nid} not found."}) - continue - - BatchChangeHistory.objects.create( - student=student, - old_batch=old_batch, - new_batch=new_batch, - ) - student.batch_id = new_batch - student.batch = nyear - student.save() - - # Sync branch, department, and specialization + return Response({'error':'Member 2 is required'}, status=status.HTTP_400_BAD_REQUEST) + if 'member3' in request.data: try: - from applications.programme_curriculum.models_student_management import StudentBatchUpload - from applications.globals.models import DepartmentInfo - student_upload = StudentBatchUpload.objects.filter(roll_number=student.id_id).first() - if student_upload: - student_upload.branch = new_batch.discipline.name - student_upload.save() - dept_name = new_batch.discipline.acronym - department = DepartmentInfo.objects.filter(name=dept_name).first() - if department: - student.id.department = department - student.id.save() - student.specialization = dept_name - student.save() + user3 = User.objects.get(username=request.data['member3']) + member3 = user3.extrainfo + request.data['member3'] = member3 except: - pass - - if errors: - return Response({"errors": errors}, status=status.HTTP_207_MULTI_STATUS) - return Response({"detail": "Batch changes applied."}, status=status.HTTP_200_OK) - -@api_view(["GET"]) -@permission_classes([IsAuthenticated]) -@role_required(['acadadmin']) -def list_students_in_batch_semester_promotion(request): - batch_id = request.query_params.get("batch_id") - if not batch_id: - return Response({"detail": "batch_id required."}, status=status.HTTP_400_BAD_REQUEST) - students = Student.objects.filter(batch_id__id=batch_id).order_by('id_id') - result = [] - for st in students: - result.append({ - "id": st.id_id, - "username": str(st.id_id), - "current_semester_no": st.curr_semester_no, - }) - return Response(result) - -@api_view(["POST"]) -@permission_classes([IsAuthenticated]) -@role_required(['acadadmin']) -def apply_promotion(request): - data = request.data # list of student IDs - user = request.user - errors = [] - with transaction.atomic(): - for idx, sid in enumerate(data): - try: - student = Student.objects.get(id=sid) - except Student.DoesNotExist: - errors.append({"index": idx, "detail": f"Student {sid} not found."}) - continue - old_sem = student.curr_semester_no - new_sem = old_sem + 1 - try: - semester_obj = Semester.objects.get(curriculum=student.batch_id.curriculum,semester_no=new_sem) - except Semester.DoesNotExist: - errors.append({"index": idx, "detail": f"Semester {new_sem} not defined."}) - continue - student.curr_semester_no = new_sem - student.save() - frs = FinalRegistration.objects.filter(student_id=student, verified=False, semester_id = semester_obj) - for fr in frs: - course = fr.course_id - exists = course_registration.objects.filter( - student_id=student, - course_id=course, - semester_id=semester_obj - ).exists() - session, semester_type = generate_next_session(date_time.year, new_sem) - if not exists: - new_cr = course_registration.objects.create( - student_id=student, - working_year=None, - semester_id=semester_obj, - course_id=course, - course_slot_id=fr.course_slot_id, - registration_type=fr.registration_type, - session=session, - semester_type=semester_type - ) - if fr.old_course_registration: - course_replacement.objects.create( - old_course_registration=fr.old_course_registration, - new_course_registration=new_cr - ) - fr.verified = True - fr.save() - if errors: - return Response({"errors": errors}, status=status.HTTP_207_MULTI_STATUS) - return Response({"detail": "Promotion applied."}, status=status.HTTP_200_OK) - - -# @api_view(["GET"]) -# @permission_classes([IsAuthenticated]) -# def download_user_template(request): -# columns = [ -# "username", "first_name", "last_name", "email", "gender", "date_of_birth", -# "user_status", "address", "phone_no", "user_type", "department", -# "title", "about_me", -# "programme", "batch", "batch_id", "category", -# "father_name", "mother_name", "hall_no", "room_no", "specialization", "curr_semester_no" -# ] -# df = pd.DataFrame(columns=columns) -# buffer = io.BytesIO() -# df.to_excel(buffer, index=False) -# buffer.seek(0) -# resp = HttpResponse(buffer, content_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") -# resp["Content-Disposition"] = "attachment; filename=student_upload_template.xlsx" -# return resp - -# @api_view(["POST"]) -# @permission_classes([IsAuthenticated]) -# def upload_users(request): -# f = request.FILES.get("file") -# if not f: -# return JsonResponse({"detail": "No file uploaded."}, status=status.HTTP_400_BAD_REQUEST) -# try: -# df = pd.read_excel(f) -# except Exception: -# return JsonResponse({"detail": "Invalid Excel file."}, status=status.HTTP_400_BAD_REQUEST) -# required = ["username", "first_name", "last_name", "email", "gender", "date_of_birth", "user_type", "programme", "batch", "category"] -# errors = [] -# created = [] -# with transaction.atomic(): -# for idx, row in df.iterrows(): -# rownum = idx + 2 -# for field in required: -# if pd.isna(row.get(field)): -# errors.append({"row": rownum, "detail": f"{field} is required."}) -# break -# else: -# uname = str(row["username"]).strip() -# if User.objects.filter(username=uname).exists(): -# errors.append({"row": rownum, "detail": "Username already exists."}) -# continue -# email = str(row["email"]).strip() -# gender = str(row["gender"]).strip().upper()[0] -# dob = row["date_of_birth"] -# if isinstance(dob, datetime.date) is False: -# errors.append({"row": rownum, "detail": "Invalid date_of_birth."}) -# continue -# user = User.objects.create_user(username=uname, email=email, password="user@123") -# user.first_name = str(row["first_name"]).strip() -# user.last_name = str(row["last_name"]).strip() -# user.save() -# eid = uname # using username as ExtraInfo.id -# dept_name = str(row.get("department", "")).strip() -# dept = None -# if dept_name: -# dept, _ = DepartmentInfo.objects.get_or_create(name=dept_name) -# ei = ExtraInfo.objects.create( -# id=eid, -# user=user, -# title=str(row.get("title", "")).strip() or None, -# sex=gender, -# date_of_birth=dob, -# user_status=str(row.get("user_status", "")).strip() or None, -# address=str(row.get("address", "")).strip() or None, -# phone_no=int(row.get("phone_no")) if not pd.isna(row.get("phone_no")) else None, -# user_type=str(row["user_type"]).strip(), -# department=dept, -# about_me=str(row.get("about_me", "")).strip() or None, -# ) -# batch_year = int(row["batch"]) -# prog = str(row["programme"]).strip() -# cat = str(row["category"]).strip() -# batch_id_val = row.get("batch_id") -# batch_obj = None -# if not pd.isna(batch_id_val): -# try: -# batch_obj = Batch.objects.get(id=int(batch_id_val)) -# except Batch.DoesNotExist: -# errors.append({"row": rownum, "detail": "Invalid batch_id."}) -# continue -# student = Student.objects.create( -# id=ei, -# programme=prog, -# batch=batch_year, -# batch_id=batch_obj, -# cpi=float(row.get("cpi", 0)) if not pd.isna(row.get("cpi")) else 0, -# category=cat, -# father_name=str(row.get("father_name", "")).strip() or None, -# mother_name=str(row.get("mother_name", "")).strip() or None, -# hall_no=int(row.get("hall_no")) if not pd.isna(row.get("hall_no")) else 0, -# room_no=str(row.get("room_no", "")).strip() or None, -# specialization=str(row.get("specialization", "")).strip() or None, -# curr_semester_no=int(row.get("curr_semester_no")) if not pd.isna(row.get("curr_semester_no")) else 1 -# ) -# created.append(uname) -# status_code = status.HTTP_207_MULTI_STATUS if errors else status.HTTP_201_CREATED -# return JsonResponse({"created": created, "errors": errors}, status=status_code) \ No newline at end of file + return Response({'error':'Wrong username of member 3. User does not exist.'}, status=status.HTTP_400_BAD_REQUEST) + else: + member3 = None + if not 'approval' in request.data: + return Response({'error':'Approval value is required.'}, status=status.HTTP_400_BAD_REQUEST) + elif request.data['approval'] != 'yes' and request.data['approval'] != 'no': + return Response({'error':'Wrong approval value provided. Approval value should be yes or no'}, status=status.HTTP_400_BAD_REQUEST) + if request.data['approval'] == 'yes': + request.data.pop('approval', None) + request.data['pending_supervisor'] = False + request.data['approval_supervisor'] = True + request.data['forwarded_to_hod'] = True + request.data['pending_hod'] = True + else: + request.data.pop('approval', None) + request.data['pending_supervisor'] = False + request.data['approval_supervisor'] = False + request.data['forwarded_to_hod'] = False + request.data['pending_hod'] = False + serializer = serializers.ThesisTopicProcessSerializer(thesis, data=request.data, partial=True) + if serializer.is_valid(): + serializer.save() + return Response(serializer.data, status=status.HTTP_200_OK) + else: + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + else: + return Response({'error':'Cannot approve thesis'}, status=status.HTTP_400_BAD_REQUEST) diff --git a/FusionIIIT/applications/academic_procedures/migrations/0001_initial.py b/FusionIIIT/applications/academic_procedures/migrations/0001_initial.py index 71f23d8c2..b873390df 100644 --- a/FusionIIIT/applications/academic_procedures/migrations/0001_initial.py +++ b/FusionIIIT/applications/academic_procedures/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 3.1.5 on 2024-07-16 15:44 +# Generated by Django 3.1.5 on 2023-03-15 18:53 import datetime from django.db import migrations, models @@ -11,9 +11,9 @@ class Migration(migrations.Migration): initial = True dependencies = [ + ('academic_information', '0001_initial'), ('programme_curriculum', '0001_initial'), ('globals', '0001_initial'), - ('academic_information', '0001_initial'), ] operations = [ @@ -188,7 +188,7 @@ class Migration(migrations.Migration): fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('month', models.CharField(choices=[('Jan', 'January'), ('Feb', 'Febuary'), ('Mar', 'March'), ('Apr', 'April'), ('May', 'May'), ('Jun', 'June'), ('Jul', 'July'), ('Aug', 'August'), ('Sep', 'September'), ('Oct', 'October'), ('Nov', 'November'), ('Dec', 'December')], max_length=10)), - ('year', models.IntegerField(choices=[(2024, 2024), (2023, 2023)])), + ('year', models.IntegerField(choices=[(2023, 2023), (2022, 2022)])), ('description', models.CharField(choices=[('Stu_paid', 'Paid'), ('Stu_due', 'Due')], max_length=15)), ('amount', models.IntegerField()), ('remaining_amount', models.IntegerField()), @@ -243,7 +243,7 @@ class Migration(migrations.Migration): fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('semester', models.IntegerField()), - ('batch', models.IntegerField(default=2024)), + ('batch', models.IntegerField(default=2023)), ('verified', models.BooleanField(default=False)), ('curr_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='academic_information.curriculum')), ('student_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='academic_information.student')), @@ -334,7 +334,7 @@ class Migration(migrations.Migration): name='course_registration', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('working_year', models.IntegerField(blank=True, choices=[(2024, 2024), (2023, 2023)], null=True)), + ('working_year', models.IntegerField(blank=True, choices=[(2023, 2023), (2022, 2022)], null=True)), ('course_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='programme_curriculum.course')), ('course_slot_id', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='programme_curriculum.courseslot')), ('semester_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='programme_curriculum.semester')), @@ -368,23 +368,13 @@ class Migration(migrations.Migration): 'db_table': 'Bonafide', }, ), - migrations.CreateModel( - name='backlog_course', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('is_summer_course', models.BooleanField(default=False)), - ('course_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='academic_information.course')), - ('semester_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='programme_curriculum.semester')), - ('student_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='academic_information.student')), - ], - ), migrations.CreateModel( name='AssistantshipClaim', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('date', models.DateTimeField(auto_now_add=True)), ('month', models.CharField(choices=[('Jan', 'January'), ('Feb', 'Febuary'), ('Mar', 'March'), ('Apr', 'April'), ('May', 'May'), ('Jun', 'June'), ('Jul', 'July'), ('Aug', 'August'), ('Sep', 'September'), ('Oct', 'October'), ('Nov', 'November'), ('Dec', 'December')], max_length=10)), - ('year', models.IntegerField(choices=[(2024, 2024), (2023, 2023)])), + ('year', models.IntegerField(choices=[(2023, 2023), (2022, 2022)])), ('bank_account', models.CharField(max_length=11)), ('applicability', models.CharField(choices=[('GATE', 'GATE'), ('NET', 'NET'), ('CEED', 'CEED')], max_length=5)), ('ta_supervisor_remark', models.BooleanField(default=False)), @@ -402,7 +392,7 @@ class Migration(migrations.Migration): name='Register', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('year', models.IntegerField(default=2024)), + ('year', models.IntegerField(default=2023)), ('semester', models.IntegerField()), ('curr_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='academic_information.curriculum')), ('student_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='academic_information.student')), diff --git a/FusionIIIT/applications/academic_procedures/migrations/0002_initialregistration_registration_type.py b/FusionIIIT/applications/academic_procedures/migrations/0002_initialregistration_registration_type.py deleted file mode 100644 index 2bbd7e0c5..000000000 --- a/FusionIIIT/applications/academic_procedures/migrations/0002_initialregistration_registration_type.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.1.5 on 2024-11-16 23:49 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('academic_procedures', '0001_initial'), - ] - - operations = [ - migrations.AddField( - model_name='initialregistration', - name='registration_type', - field=models.CharField(choices=[('Audit', 'Audit'), ('Improvement', 'Improvement'), ('Backlog', 'Backlog'), ('Regular', 'Regular')], default='Regular', max_length=20), - ), - ] diff --git a/FusionIIIT/applications/academic_procedures/migrations/0003_finalregistration_registration_type.py b/FusionIIIT/applications/academic_procedures/migrations/0003_finalregistration_registration_type.py deleted file mode 100644 index 376c82446..000000000 --- a/FusionIIIT/applications/academic_procedures/migrations/0003_finalregistration_registration_type.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.1.5 on 2024-11-17 00:34 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('academic_procedures', '0002_initialregistration_registration_type'), - ] - - operations = [ - migrations.AddField( - model_name='finalregistration', - name='registration_type', - field=models.CharField(choices=[('Audit', 'Audit'), ('Improvement', 'Improvement'), ('Backlog', 'Backlog'), ('Regular', 'Regular')], default='Regular', max_length=20), - ), - ] diff --git a/FusionIIIT/applications/academic_procedures/migrations/0004_course_registration_registration_type.py b/FusionIIIT/applications/academic_procedures/migrations/0004_course_registration_registration_type.py deleted file mode 100644 index 1731e55f7..000000000 --- a/FusionIIIT/applications/academic_procedures/migrations/0004_course_registration_registration_type.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.1.5 on 2024-12-01 16:20 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('academic_procedures', '0003_finalregistration_registration_type'), - ] - - operations = [ - migrations.AddField( - model_name='course_registration', - name='registration_type', - field=models.CharField(choices=[('Audit', 'Audit'), ('Improvement', 'Improvement'), ('Backlog', 'Backlog'), ('Regular', 'Regular')], default='Regular', max_length=20), - ), - ] diff --git a/FusionIIIT/applications/academic_procedures/migrations/0005_auto_20241202_1439.py b/FusionIIIT/applications/academic_procedures/migrations/0005_auto_20241202_1439.py deleted file mode 100644 index d29992845..000000000 --- a/FusionIIIT/applications/academic_procedures/migrations/0005_auto_20241202_1439.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 3.1.5 on 2024-12-02 14:39 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('programme_curriculum', '0004_auto_20241117_0137'), - ('academic_information', '0001_initial'), - ('academic_procedures', '0004_course_registration_registration_type'), - ] - - operations = [ - migrations.AlterUniqueTogether( - name='course_registration', - unique_together={('course_id', 'student_id', 'semester_id', 'registration_type')}, - ), - ] diff --git a/FusionIIIT/applications/academic_procedures/migrations/0006_auto_20250114_0326.py b/FusionIIIT/applications/academic_procedures/migrations/0006_auto_20250114_0326.py deleted file mode 100644 index b7434719e..000000000 --- a/FusionIIIT/applications/academic_procedures/migrations/0006_auto_20250114_0326.py +++ /dev/null @@ -1,43 +0,0 @@ -# Generated by Django 3.1.5 on 2025-01-14 03:26 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('academic_procedures', '0005_auto_20241202_1439'), - ] - - operations = [ - migrations.AlterField( - model_name='assistantshipclaim', - name='year', - field=models.IntegerField(choices=[(2025, 2025), (2024, 2024)]), - ), - migrations.AlterField( - model_name='course_registration', - name='registration_type', - field=models.CharField(choices=[('Audit', 'Audit'), ('Improvement', 'Improvement'), ('Backlog', 'Backlog'), ('Regular', 'Regular'), ('Extra Credits', 'Extra Credits'), ('Replacement', 'Replacement')], default='Regular', max_length=20), - ), - migrations.AlterField( - model_name='course_registration', - name='working_year', - field=models.IntegerField(blank=True, choices=[(2025, 2025), (2024, 2024)], null=True), - ), - migrations.AlterField( - model_name='finalregistrations', - name='batch', - field=models.IntegerField(default=2025), - ), - migrations.AlterField( - model_name='messdue', - name='year', - field=models.IntegerField(choices=[(2025, 2025), (2024, 2024)]), - ), - migrations.AlterField( - model_name='register', - name='year', - field=models.IntegerField(default=2025), - ), - ] diff --git a/FusionIIIT/applications/academic_procedures/migrations/0007_auto_20250203_1619.py b/FusionIIIT/applications/academic_procedures/migrations/0007_auto_20250203_1619.py deleted file mode 100644 index cb479a10b..000000000 --- a/FusionIIIT/applications/academic_procedures/migrations/0007_auto_20250203_1619.py +++ /dev/null @@ -1,22 +0,0 @@ -# Generated by Django 3.1.5 on 2025-02-03 16:19 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('academic_information', '0001_initial'), - ('academic_procedures', '0006_auto_20250114_0326'), - ] - - operations = [ - migrations.AlterUniqueTogether( - name='finalregistration', - unique_together={('course_id', 'student_id', 'semester_id', 'registration_type')}, - ), - migrations.AlterUniqueTogether( - name='initialregistration', - unique_together={('course_id', 'student_id', 'semester_id', 'registration_type')}, - ), - ] diff --git a/FusionIIIT/applications/academic_procedures/migrations/0008_course_replacement.py b/FusionIIIT/applications/academic_procedures/migrations/0008_course_replacement.py deleted file mode 100644 index b4f08f705..000000000 --- a/FusionIIIT/applications/academic_procedures/migrations/0008_course_replacement.py +++ /dev/null @@ -1,26 +0,0 @@ -# Generated by Django 3.1.5 on 2025-02-04 22:46 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('academic_procedures', '0007_auto_20250203_1619'), - ] - - operations = [ - migrations.CreateModel( - name='course_replacement', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('new_course_registration', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='replaces', to='academic_procedures.course_registration')), - ('old_course_registration', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='replaced', to='academic_procedures.course_registration')), - ], - options={ - 'db_table': 'course_replacement', - 'unique_together': {('old_course_registration', 'new_course_registration')}, - }, - ), - ] diff --git a/FusionIIIT/applications/academic_procedures/migrations/0009_auto_20250409_2128.py b/FusionIIIT/applications/academic_procedures/migrations/0009_auto_20250409_2128.py deleted file mode 100644 index 98f416297..000000000 --- a/FusionIIIT/applications/academic_procedures/migrations/0009_auto_20250409_2128.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 3.1.5 on 2025-04-09 21:28 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('academic_procedures', '0008_course_replacement'), - ] - - operations = [ - migrations.AddField( - model_name='course_registration', - name='semester_type', - field=models.CharField(choices=[('Odd Semester', 'Odd Semester'), ('Even Semester', 'Even Semester'), ('Summer Semester', 'Summer Semester')], max_length=20, null=True), - ), - migrations.AddField( - model_name='course_registration', - name='session', - field=models.CharField(max_length=9, null=True), - ), - ] diff --git a/FusionIIIT/applications/academic_procedures/migrations/0009_auto_20250420_1221.py b/FusionIIIT/applications/academic_procedures/migrations/0009_auto_20250420_1221.py deleted file mode 100644 index a6a187849..000000000 --- a/FusionIIIT/applications/academic_procedures/migrations/0009_auto_20250420_1221.py +++ /dev/null @@ -1,24 +0,0 @@ -# Generated by Django 3.1.5 on 2025-04-20 12:21 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('academic_procedures', '0008_course_replacement'), - ] - - operations = [ - migrations.AddField( - model_name='finalregistration', - name='old_course_registration', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='academic_procedures.course_registration'), - ), - migrations.AddField( - model_name='initialregistration', - name='old_course_registration', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='academic_procedures.course_registration'), - ), - ] diff --git a/FusionIIIT/applications/academic_procedures/migrations/0010_populate_session_and_type.py b/FusionIIIT/applications/academic_procedures/migrations/0010_populate_session_and_type.py deleted file mode 100644 index 8a327f987..000000000 --- a/FusionIIIT/applications/academic_procedures/migrations/0010_populate_session_and_type.py +++ /dev/null @@ -1,39 +0,0 @@ -# Generated by Django 3.1.5 on 2025-04-09 21:29 - -from django.db import migrations - -def populate_session_and_semester_type(apps, schema_editor): - course_registration = apps.get_model('academic_procedures', 'course_registration') - Semester = apps.get_model('programme_curriculum', 'Semester') - - for obj in course_registration.objects.select_related('semester_id').all(): - sem_no = obj.semester_id.semester_no - working_year = obj.working_year - - # session logic - if sem_no % 2 == 1: - session = f"{working_year}-{str(working_year + 1)[-2:]}" - else: - session = f"{working_year - 1}-{str(working_year)[-2:]}" - - # semester_type logic - if sem_no == 0: - semester_type = "Summer Semester" - elif sem_no % 2 == 0: - semester_type = "Even Semester" - else: - semester_type = "Odd Semester" - - obj.session = session - obj.semester_type = semester_type - obj.save(update_fields=['session', 'semester_type']) - -class Migration(migrations.Migration): - - dependencies = [ - ('academic_procedures', '0009_auto_20250409_2128'), - ] - - operations = [ - migrations.RunPython(populate_session_and_semester_type), - ] diff --git a/FusionIIIT/applications/academic_procedures/migrations/0011_merge_20250420_1513.py b/FusionIIIT/applications/academic_procedures/migrations/0011_merge_20250420_1513.py deleted file mode 100644 index dc0c09335..000000000 --- a/FusionIIIT/applications/academic_procedures/migrations/0011_merge_20250420_1513.py +++ /dev/null @@ -1,14 +0,0 @@ -# Generated by Django 3.1.5 on 2025-04-20 15:13 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('academic_procedures', '0009_auto_20250420_1221'), - ('academic_procedures', '0010_populate_session_and_type'), - ] - - operations = [ - ] \ No newline at end of file diff --git a/FusionIIIT/applications/academic_procedures/migrations/0012_auto_20250422_0230.py b/FusionIIIT/applications/academic_procedures/migrations/0012_auto_20250422_0230.py deleted file mode 100644 index 608fb05a6..000000000 --- a/FusionIIIT/applications/academic_procedures/migrations/0012_auto_20250422_0230.py +++ /dev/null @@ -1,21 +0,0 @@ -# Generated by Django 3.1.5 on 2025-04-22 02:30 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('academic_procedures', '0011_merge_20250420_1513'), - ] - - operations = [ - migrations.RemoveField( - model_name='course_registration', - name='semester_type', - ), - migrations.RemoveField( - model_name='course_registration', - name='session', - ), - ] \ No newline at end of file diff --git a/FusionIIIT/applications/academic_procedures/migrations/0013_auto_20250423_1401.py b/FusionIIIT/applications/academic_procedures/migrations/0013_auto_20250423_1401.py deleted file mode 100644 index dc20cdda2..000000000 --- a/FusionIIIT/applications/academic_procedures/migrations/0013_auto_20250423_1401.py +++ /dev/null @@ -1,49 +0,0 @@ -# Generated by Django 3.1.5 on 2025-04-23 14:01 - -from django.db import migrations, models - -def populate_session_and_semester_type(apps, schema_editor): - course_registration = apps.get_model('academic_procedures', 'course_registration') - Semester = apps.get_model('programme_curriculum', 'Semester') - - for obj in course_registration.objects.select_related('semester_id').all(): - sem_no = obj.semester_id.semester_no - working_year = obj.working_year - - # session logic - if sem_no % 2 == 1: - session = f"{working_year}-{str(working_year + 1)[-2:]}" - else: - session = f"{working_year - 1}-{str(working_year)[-2:]}" - - # semester_type logic - if sem_no == 0: - semester_type = "Summer Semester" - elif sem_no % 2 == 0: - semester_type = "Even Semester" - else: - semester_type = "Odd Semester" - - obj.session = session - obj.semester_type = semester_type - obj.save(update_fields=['session', 'semester_type']) - -class Migration(migrations.Migration): - - dependencies = [ - ('academic_procedures', '0012_auto_20250422_0230'), - ] - - operations = [ - migrations.AddField( - model_name='course_registration', - name='semester_type', - field=models.CharField(choices=[('Odd Semester', 'Odd Semester'), ('Even Semester', 'Even Semester'), ('Summer Semester', 'Summer Semester')], max_length=20, null=True), - ), - migrations.AddField( - model_name='course_registration', - name='session', - field=models.CharField(max_length=9, null=True), - ), - migrations.RunPython(populate_session_and_semester_type), - ] diff --git a/FusionIIIT/applications/academic_procedures/migrations/0014_assignment_coursereplacementrequest_stipendrequest.py b/FusionIIIT/applications/academic_procedures/migrations/0014_assignment_coursereplacementrequest_stipendrequest.py deleted file mode 100644 index 1024089df..000000000 --- a/FusionIIIT/applications/academic_procedures/migrations/0014_assignment_coursereplacementrequest_stipendrequest.py +++ /dev/null @@ -1,64 +0,0 @@ -# Generated by Django 3.1.5 on 2025-05-03 01:47 - -from django.db import migrations, models -import django.db.models.deletion -import django.utils.timezone - - -class Migration(migrations.Migration): - - dependencies = [ - ('globals', '0004_extrainfo_last_selected_role'), - ('academic_information', '0001_initial'), - ('programme_curriculum', '0008_populate_semester_type'), - ('academic_procedures', '0013_auto_20250423_1401'), - ] - - operations = [ - migrations.CreateModel( - name='Assignment', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('start_year', models.IntegerField()), - ('start_month', models.IntegerField()), - ('end_year', models.IntegerField()), - ('end_month', models.IntegerField()), - ('faculty', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='globals.faculty')), - ('ta', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='academic_information.student')), - ], - ), - migrations.CreateModel( - name='StipendRequest', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('year', models.IntegerField()), - ('month', models.IntegerField()), - ('status', models.CharField(choices=[('pending', 'Pending'), ('approved_by_faculty', 'Approved by Faculty'), ('approved_by_hod', 'Approved by HOD')], default='pending', max_length=20)), - ('faculty_remark', models.TextField(blank=True, null=True)), - ('hod_remark', models.TextField(blank=True, null=True)), - ('assignment', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='stipends', to='academic_procedures.assignment')), - ], - options={ - 'ordering': ['year', 'month'], - 'unique_together': {('assignment', 'year', 'month')}, - }, - ), - migrations.CreateModel( - name='CourseReplacementRequest', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('academic_year', models.CharField(max_length=9)), - ('semester_type', models.CharField(choices=[('Odd Semester', 'Odd Semester'), ('Even Semester', 'Even Semester'), ('Summer Semester', 'Summer Semester')], max_length=20)), - ('status', models.CharField(choices=[('Pending', 'Pending'), ('Approved', 'Approved'), ('Rejected', 'Rejected')], default='Pending', max_length=20)), - ('created_at', models.DateTimeField(default=django.utils.timezone.now)), - ('processed_at', models.DateTimeField(blank=True, null=True)), - ('course_slot', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='programme_curriculum.courseslot')), - ('new_course', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='new_course_reqs', to='programme_curriculum.course')), - ('old_course', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='old_course_reqs', to='programme_curriculum.course')), - ('student', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='academic_information.student')), - ], - options={ - 'unique_together': {('student', 'course_slot', 'academic_year', 'semester_type')}, - }, - ), - ] diff --git a/FusionIIIT/applications/academic_procedures/migrations/0015_auto_20250709_1240.py b/FusionIIIT/applications/academic_procedures/migrations/0015_auto_20250709_1240.py deleted file mode 100644 index a948e7a7a..000000000 --- a/FusionIIIT/applications/academic_procedures/migrations/0015_auto_20250709_1240.py +++ /dev/null @@ -1,174 +0,0 @@ -# Generated by Django 3.2.25 on 2025-07-09 12:40 - -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion - -def create_questions(apps, schema_editor): - Q = apps.get_model("academic_procedures", "FeedbackQuestion") - O = apps.get_model("academic_procedures", "FeedbackOption") - - - attendance_texts = [ - "Number of classes you missed", - ] - for idx, txt in enumerate(attendance_texts, start=1): - q = Q.objects.create(section="attendance", order=idx, text=txt) - for j, opt in enumerate(["None", "Less than 5", "Between 5 - 10", "Between 10 - 15", "More than 15"], start=1): - O.objects.create(question=q, text=opt, order=j) - - texts = [ - "Amount of materials covered in this course was just right", - "Texts/Reference material/Class notes were easily available", - "Learning material was relevant and well organized", - "All the contents were covered", - ] - for idx, txt in enumerate(texts, start=1): - q = Q.objects.create(section="contents", order=idx, text=txt) - for j, opt in enumerate(["Excellent","Good","Average","Poor"], start=1): - O.objects.create(question=q, text=opt, order=j) - Q.objects.create( - section="contents", order=5, - text="Any Suggestion Regarding Course Contents [Overall]" - ) - - instr_texts_1 = [ - "Number of classes actually engaged by the instructor", - "Instructor was punctual in coming to the classes", - "Duration of most of the classes was as per schedule", - "All the allocated classes were held on scheduled time and duration", - ] - percents = ["< 80%","80% - 90%","90% - 95%","95% - 100%"] - for idx, txt in enumerate(instr_texts_1, start=1): - q = Q.objects.create(section="instructor", order=idx, text=txt) - for j, opt in enumerate(percents, start=1): - O.objects.create(question=q, text=opt, order=j) - - instr_texts_2 = [ - "Topics were covered in a logical sequence with even pace.", - "Effective use of teaching aids (Board work/ Presentation/ Audio-Visual aids)", - "Evaluation schema (Quizzes/ exams/ Assignments) Covered the entire course material.", - "The evaluation Assignments/ Quizzes etc. was done fairly and on time.", - "The questions raised in the class were satisfactorily answered.", - "The Overall effectiveness of the instructor", - - ] - for idx, txt in enumerate(instr_texts_2, start=5): - q = Q.objects.create(section="instructor", order=idx, text=txt) - for j, opt in enumerate(["Excellent","Good","Average","Poor"], start=1): - O.objects.create(question=q, text=opt, order=j) - Q.objects.create( - section="instructor", order=11, - text="Strengths and Weaknesses of the Instructor" - ) - - tut_texts = [ - "The Tutorials helped in better understanding of the course.", - "All allocated tutorials were conducted.", - "Doubts were clarified effectively.", - "Questions were encouraged.", - "The evaluation of Assignment/ Quizzes etc was done fairly and on time.", - "Overall the tutor was effective.", - ] - for idx, txt in enumerate(tut_texts, start=1): - q = Q.objects.create(section="tutorial", order=idx, text=txt) - for j, opt in enumerate(["Excellent","Good","Average","Poor"], start=1): - O.objects.create(question=q, text=opt, order=j) - Q.objects.create( - section="tutorial", order=7, - text="Any Constructive Suggestion for Improving the Tutorials" - ) - - lab1 = Q.objects.create(section="lab", order=1, - text="Course Instructor present in the lab") - for j, opt in enumerate(["Less than 5 lab","5 to 10 lab","More than 10 lab","Not present any lab"], start=1): - O.objects.create(question=lab1, text=opt, order=j) - lab_texts = [ - "Equipment's available in the laboratory were functioning.", - "Lab Instructor ensured the smooth conduct of experiments.", - "Laboratory reports were corrected weekly and in time", - ] - for idx, txt in enumerate(lab_texts, start=2): - q = Q.objects.create(section="lab", order=idx, text=txt) - for j, opt in enumerate(["Excellent","Good","Average","Poor"], start=1): - O.objects.create(question=q, text=opt, order=j) - - -class Migration(migrations.Migration): - - dependencies = [ - ('programme_curriculum', '0009_auto_20250503_1355'), - ('academic_information', '0001_initial'), - ('academic_procedures', '0014_assignment_coursereplacementrequest_stipendrequest'), - ] - - operations = [ - migrations.CreateModel( - name='FeedbackOption', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('text', models.CharField(max_length=50)), - ('order', models.PositiveIntegerField()), - ], - options={ - 'ordering': ['order'], - }, - ), - migrations.CreateModel( - name='FeedbackQuestion', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('section', models.CharField(choices=[('contents', 'Course Contents'), ('instructor', 'Course Instructor'), ('tutorial', 'Tutorial'), ('lab', 'Lab Instructor'), ('attendance', 'Attendance')], max_length=20)), - ('text', models.TextField()), - ('order', models.PositiveIntegerField()), - ], - options={ - 'ordering': ['section', 'order'], - }, - ), - migrations.CreateModel( - name='FeedbackResponse', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('text_answer', models.TextField(blank=True)), - ('section', models.CharField(choices=[('contents', 'Course Contents'), ('instructor', 'Course Instructor'), ('tutorial', 'Tutorial'), ('lab', 'Lab Instructor'), ('attendance', 'Attendance')], max_length=20)), - ('session', models.CharField(max_length=9)), - ('semester_type', models.CharField(choices=[('Odd Semester', 'Odd Semester'), ('Even Semester', 'Even Semester'), ('Summer Semester', 'Summer Semester')], max_length=20)), - ('submitted_at', models.DateTimeField(auto_now_add=True)), - ('course', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='programme_curriculum.course')), - ('option', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='academic_procedures.feedbackoption')), - ('question', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='academic_procedures.feedbackquestion')), - ], - ), - migrations.AddField( - model_name='feedbackoption', - name='question', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='options', to='academic_procedures.feedbackquestion'), - ), - migrations.CreateModel( - name='BatchChangeHistory', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('changed_at', models.DateTimeField(auto_now_add=True)), - ('new_batch', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='history_new', to='programme_curriculum.batch')), - ('old_batch', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='history_old', to='programme_curriculum.batch')), - ('student', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='academic_information.student')), - ], - options={ - 'ordering': ['-changed_at'], - }, - ), - migrations.CreateModel( - name='FeedbackFilled', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('semester_no', models.PositiveIntegerField()), - ('filled_at', models.DateTimeField(auto_now_add=True)), - ('student', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='academic_information.student')), - ], - options={ - 'unique_together': {('student', 'semester_no')}, - }, - ), - migrations.RunPython(create_questions) - ] diff --git a/FusionIIIT/applications/academic_procedures/migrations/0016_coursedroprequest.py b/FusionIIIT/applications/academic_procedures/migrations/0016_coursedroprequest.py deleted file mode 100644 index 68b2e89f2..000000000 --- a/FusionIIIT/applications/academic_procedures/migrations/0016_coursedroprequest.py +++ /dev/null @@ -1,57 +0,0 @@ -from django.db import migrations, models -import django.db.models.deletion -import django.utils.timezone - - -class Migration(migrations.Migration): - - dependencies = [ - ('academic_information', '0001_initial'), - ('programme_curriculum', '0031_add_curriculum_options_to_batch'), - ('academic_procedures', '0015_auto_20250709_1240'), - ] - - operations = [ - migrations.CreateModel( - name='CourseDropRequest', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('academic_year', models.CharField(max_length=9)), - ('semester_type', models.CharField( - choices=[ - ('Odd Semester', 'Odd Semester'), - ('Even Semester', 'Even Semester'), - ('Summer Semester', 'Summer Semester') - ], - max_length=20 - )), - ('status', models.CharField( - choices=[ - ('Pending', 'Pending'), - ('Approved', 'Approved'), - ('Rejected', 'Rejected') - ], - default='Pending', - max_length=20 - )), - ('created_at', models.DateTimeField(default=django.utils.timezone.now)), - ('processed_at', models.DateTimeField(blank=True, null=True)), - ('course', models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name='drop_course_reqs', - to='programme_curriculum.course' - )), - ('course_slot', models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - to='programme_curriculum.courseslot' - )), - ('student', models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - to='academic_information.student' - )), - ], - options={ - 'unique_together': {('student', 'course_slot', 'academic_year', 'semester_type')}, - }, - ), - ] diff --git a/FusionIIIT/applications/academic_procedures/migrations/0017_courseaddrequest.py b/FusionIIIT/applications/academic_procedures/migrations/0017_courseaddrequest.py deleted file mode 100644 index 8acefa675..000000000 --- a/FusionIIIT/applications/academic_procedures/migrations/0017_courseaddrequest.py +++ /dev/null @@ -1,32 +0,0 @@ -from django.db import migrations, models -import django.db.models.deletion -import django.utils.timezone - - -class Migration(migrations.Migration): - - dependencies = [ - ('academic_procedures', '0016_coursedroprequest'), - ('programme_curriculum', '0031_add_curriculum_options_to_batch'), - ] - - operations = [ - migrations.CreateModel( - name='CourseAddRequest', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('academic_year', models.CharField(max_length=9)), - ('semester_type', models.CharField(choices=[('Odd Semester', 'Odd Semester'), ('Even Semester', 'Even Semester'), ('Summer Semester', 'Summer Semester')], max_length=20)), - ('status', models.CharField(choices=[('Pending', 'Pending'), ('Approved', 'Approved'), ('Rejected', 'Rejected')], default='Pending', max_length=20)), - ('created_at', models.DateTimeField(default=django.utils.timezone.now)), - ('processed_at', models.DateTimeField(blank=True, null=True)), - ('course', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='add_course_reqs', to='programme_curriculum.Course')), - ('course_slot', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='programme_curriculum.CourseSlot')), - ('student', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='academic_information.Student')), - ], - ), - migrations.AlterUniqueTogether( - name='courseaddrequest', - unique_together={('student', 'course_slot', 'academic_year', 'semester_type')}, - ), - ] diff --git a/FusionIIIT/applications/academic_procedures/migrations/0018_auto_20260106_1355.py b/FusionIIIT/applications/academic_procedures/migrations/0018_auto_20260106_1355.py deleted file mode 100644 index 9f14028b8..000000000 --- a/FusionIIIT/applications/academic_procedures/migrations/0018_auto_20260106_1355.py +++ /dev/null @@ -1,44 +0,0 @@ -# Generated by Django 3.1.5 on 2026-01-06 13:55 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('academic_procedures', '0017_courseaddrequest'), - ] - - operations = [ - migrations.AddField( - model_name='courseaddrequest', - name='old_course_registration', - field=models.ForeignKey(blank=True, help_text='Reference to the previous course registration being replaced (for backlog/improvement)', null=True, on_delete=django.db.models.deletion.SET_NULL, to='academic_procedures.course_registration'), - ), - migrations.AlterField( - model_name='assistantshipclaim', - name='year', - field=models.IntegerField(choices=[(2026, 2026), (2025, 2025)]), - ), - migrations.AlterField( - model_name='course_registration', - name='working_year', - field=models.IntegerField(blank=True, choices=[(2026, 2026), (2025, 2025)], null=True), - ), - migrations.AlterField( - model_name='finalregistrations', - name='batch', - field=models.IntegerField(default=2026), - ), - migrations.AlterField( - model_name='messdue', - name='year', - field=models.IntegerField(choices=[(2026, 2026), (2025, 2025)]), - ), - migrations.AlterField( - model_name='register', - name='year', - field=models.IntegerField(default=2026), - ), - ] diff --git a/FusionIIIT/applications/academic_procedures/models.py b/FusionIIIT/applications/academic_procedures/models.py index 3b615fb18..37b339378 100644 --- a/FusionIIIT/applications/academic_procedures/models.py +++ b/FusionIIIT/applications/academic_procedures/models.py @@ -2,14 +2,12 @@ from django.db import models from django.contrib.postgres.fields import ArrayField -from django.contrib.auth import get_user_model from applications.academic_information.models import Course, Student, Curriculum -from applications.programme_curriculum.models import Course as Courses, Semester, CourseSlot, Batch +from applications.programme_curriculum.models import Course as Courses, Semester, CourseSlot from applications.globals.models import DepartmentInfo, ExtraInfo, Faculty from django.utils import timezone - class Constants: SEM_CHOICES = ( ('1', '1'), @@ -285,9 +283,6 @@ class SemesterMarks(models.Model): # curr_id = models.ForeignKey(Curriculum, on_delete=models.CASCADE) curr_id = models.ForeignKey(Courses, on_delete=models.CASCADE) #course_id = models.ForeignKey(Courses, on_delete=models.CASCADE, null=True) - - # def __str__(self): - # return self.student_id class Meta: db_table = 'SemesterMarks' @@ -570,6 +565,8 @@ class StudentRegistrationChecks(models.Model): final_registration_flag(boolean) - to denote whether the final registration is complete semester_id(programme_curriculum.Semester) - reference to the semester for which the registration will be considered + + ''' @@ -582,57 +579,6 @@ class Meta: db_table = 'StudentRegistrationChecks' -class course_registration(models.Model): - ''' - Current Purpose : stores information regarding the process of registration of a student for a course - - ATTRIBUTES - course_id(programme_curriculum.Course) - reference to the course details for which the registration is being done - semester_id(programme_curriculum.Semester) - reference to the semester for which the course registration is done - student_id(academic_information.Student) - reference to the student - course_slot_id(programme_curriculum.CourseSlot) - details about under which course slot the course is offered(Optional/Core other details) - - ''' - - - student_id = models.ForeignKey(Student, on_delete=models.CASCADE) - working_year=models.IntegerField(null=True,blank=True,choices=Year_Choices) - semester_id = models.ForeignKey(Semester, on_delete=models.CASCADE) - course_id = models.ForeignKey(Courses, on_delete=models.CASCADE) - course_slot_id = models.ForeignKey(CourseSlot, null=True, blank=True, on_delete=models.SET_NULL) - REGISTRATION_TYPE_CHOICES = [ - ('Audit', 'Audit'), - ('Improvement', 'Improvement'), - ('Backlog', 'Backlog'), - ('Regular', 'Regular'), - ('Extra Credits', 'Extra Credits'), - ('Replacement', 'Replacement'), - ] - registration_type = models.CharField( - max_length=20, - choices=REGISTRATION_TYPE_CHOICES, - default='Regular', - ) - session = models.CharField(max_length=9, null=True) - SEMESTER_TYPE_CHOICES = [ - ("Odd Semester", "Odd Semester"), - ("Even Semester", "Even Semester"), - ("Summer Semester", "Summer Semester"), - ] - semester_type = models.CharField( - max_length=20, - choices=SEMESTER_TYPE_CHOICES, - null=True - ) - # grade = models.CharField(max_length=10) - #course_registration_year = models.IntegerField() - def __str__(self): - return str(self.semester_id.semester_no) - class Meta: - db_table = 'course_registration' - unique_together = ('course_id', 'student_id', 'semester_id', 'registration_type') - - class InitialRegistration(models.Model): ''' Current Purpose : stores information regarding the process of registration of a student for a course @@ -645,7 +591,6 @@ class InitialRegistration(models.Model): course_slot_id(programme_curriculum.CourseSlot) - details about under which course slot the course is offered(Optional/Core other details) timestamp - the time this entry was generated priority - priority of the selected course from the list of courses for the corresponding course_slot_it - registration_type - Type of registration for the course @@ -656,23 +601,11 @@ class InitialRegistration(models.Model): course_slot_id = models.ForeignKey(CourseSlot, null=True, blank=True,on_delete=models.SET_NULL) timestamp = models.DateTimeField(default=timezone.now) priority = models.IntegerField(blank=True,null=True) - old_course_registration=models.ForeignKey(course_registration, null=True, on_delete=models.CASCADE) - REGISTRATION_TYPE_CHOICES = [ - ('Audit', 'Audit'), - ('Improvement', 'Improvement'), - ('Backlog', 'Backlog'), - ('Regular', 'Regular'), - ] - registration_type = models.CharField( - max_length=20, - choices=REGISTRATION_TYPE_CHOICES, - default='Regular', - ) class Meta: db_table = 'InitialRegistration' - unique_together = ('course_id', 'student_id', 'semester_id', 'registration_type') - + + class FinalRegistration(models.Model): ''' Current Purpose : stores information regarding the process of final(complete) registration of a student for a course @@ -685,6 +618,9 @@ class FinalRegistration(models.Model): verified(Boolean) - denotes whether the registration is verified by academic department and complete course_slot_id(programme_curriculum.CourseSlot) - details about under which course slot the course is offered(Optional/Core other details) + + + ''' @@ -693,22 +629,9 @@ class FinalRegistration(models.Model): student_id = models.ForeignKey(Student, on_delete=models.CASCADE) verified = models.BooleanField(default=False) course_slot_id = models.ForeignKey(CourseSlot, null=True, blank=True,on_delete=models.SET_NULL) - old_course_registration=models.ForeignKey(course_registration, null=True, on_delete=models.CASCADE) - REGISTRATION_TYPE_CHOICES = [ - ('Audit', 'Audit'), - ('Improvement', 'Improvement'), - ('Backlog', 'Backlog'), - ('Regular', 'Regular'), - ] - registration_type = models.CharField( - max_length=20, - choices=REGISTRATION_TYPE_CHOICES, - default='Regular', - ) class Meta: db_table = 'FinalRegistration' - unique_together = ('course_id', 'student_id', 'semester_id', 'registration_type') class CourseRequested(models.Model): @@ -744,275 +667,30 @@ class Meta: db_table = 'FeePayments' -class course_replacement(models.Model): - old_course_registration=models.ForeignKey(course_registration, on_delete=models.CASCADE,related_name="replaced") - new_course_registration=models.ForeignKey(course_registration, on_delete=models.CASCADE, related_name="replaces") - class Meta: - db_table = 'course_replacement' - unique_together = ('old_course_registration', 'new_course_registration') - -class backlog_course(models.Model): +class course_registration(models.Model): ''' - Current Purpose : stores information regarding the backlog courses of a student (purpose is unclear and is open to interpretations) + Current Purpose : stores information regarding the process of registration of a student for a course + ATTRIBUTES course_id(programme_curriculum.Course) - reference to the course details for which the registration is being done semester_id(programme_curriculum.Semester) - reference to the semester for which the course registration is done student_id(academic_information.Student) - reference to the student - is_summer_course(Boolean) - details about whether this course is available as summer_course or not - ''' - student_id = models.ForeignKey(Student, on_delete=models.CASCADE) - semester_id = models.ForeignKey(Semester, on_delete=models.CASCADE) - course_id = models.ForeignKey(Course, on_delete=models.CASCADE) - is_summer_course = models.BooleanField(default= False) - - -from django.db.models.signals import post_save -from django.dispatch import receiver - - -class Assignment(models.Model): - ta = models.ForeignKey(Student, on_delete=models.CASCADE) - faculty = models.ForeignKey(Faculty, on_delete=models.CASCADE) - start_year = models.IntegerField() - start_month = models.IntegerField() # 1–12 - end_year = models.IntegerField() - end_month = models.IntegerField() - - def __str__(self): - return f"{self.ta}→{self.faculty} ({self.start_month}/{self.start_year}–{self.end_month}/{self.end_year})" - -class StipendRequest(models.Model): - PENDING = 'pending' - FAC_APPROVED = 'approved_by_faculty' - HOD_APPROVED = 'approved_by_hod' - STATUS_CHOICES = [ - (PENDING, 'Pending'), - (FAC_APPROVED, 'Approved by Faculty'), - (HOD_APPROVED, 'Approved by HOD'), - ] - - assignment = models.ForeignKey(Assignment, on_delete=models.CASCADE, related_name='stipends') - year = models.IntegerField() - month = models.IntegerField() - status = models.CharField(max_length=20, choices=STATUS_CHOICES, default=PENDING) - faculty_remark = models.TextField(blank=True, null=True) - hod_remark = models.TextField(blank=True, null=True) - - class Meta: - unique_together = ('assignment','year','month') - ordering = ['year','month'] - - def __str__(self): - return f"{self.assignment.ta} - {self.month}/{self.year} [{self.get_status_display()}]" - -@receiver(post_save, sender=Assignment) -def create_monthly_stipends(sender, instance, created, **kwargs): - if not created: return - sy, sm = instance.start_year, instance.start_month - ey, em = instance.end_year, instance.end_month - year, month = sy, sm - while (year 2): - swayam_courses_count = 2 - if(next_sem_student == 6 or next_sem_student == 7 or next_sem_student == 8): - swayam_courses_count = 3 try: pre_registered_courses = InitialRegistration.objects.all().filter(student_id = user_details.id,semester_id = next_sem_id) @@ -456,16 +408,15 @@ def academic_procedures_student(request): final_registered_courses = FinalRegistration.objects.all().filter(student_id = user_details.id,semester_id = next_sem_id) final_registered_course_show=[] for final_registered_course in final_registered_courses: - final_registered_course_show.append({"course_code":final_registered_course.course_id.code,"course_name":final_registered_course.course_id.name,"course_credit":final_registered_course.course_id.credit, "registration_type": final_registered_course.registration_type}) + final_registered_course_show.append({"course_code":final_registered_course.course_id.code,"course_name":final_registered_course.course_id.name,"course_credit":final_registered_course.course_id.credit}) add_courses_options = get_add_course_options(current_sem_branch_course, currently_registered_course, batch.year) - drop_courses_options = get_drop_course_options(currently_registered_course) - replace_courses_options = get_replace_course_options(currently_registered_course, batch.year) + #drop_courses_options = get_drop_course_options(currently_registered_course) + except Exception as e: final_registered_courses = None final_registered_course_show = None - drop_courses_options = None + #drop_courses_options = None add_courses_options = None - replace_courses_options = None fee_payment_mode_list = dict(Constants.PaymentMode) @@ -540,30 +491,10 @@ def academic_procedures_student(request): attendence.append((i,pr,pr+ab)) cur_spi='Sem results not available' # To be fetched from db if result uploaded - backlogCourseList = [] - auto_backlog_courses = list(Student_grades.objects.filter(roll_no = obj , grade = 'F')) - auto_backlog_courses_list = [] - for i in auto_backlog_courses: - if not i.course_id.courseslots.filter(type__contains="Optional").exists(): - auto_backlog_courses_list.append([i.course_id.name, i.course_id.code, i.course_id.version, i.course_id.credit , i.grade]) - - backlogCourses = backlog_course.objects.select_related('course_id' , 'student_id' , 'semester_id' ).filter(student_id=obj) - for i in backlogCourses: - summer_course = "Yes" if i.is_summer_course else "No" - course_details = i.course_id.course_details if i.course_id.course_details else "N/A" - - backlogCourseList.append([i.course_id.course_name, course_details , i.semester_id.semester_no , summer_course]) - # Mess_bill = Monthly_bill.objects.filter(student_id = obj) - # Mess_pay = Payments.objects.filter(student_id = obj) - swayam_course_slots = get_current_semester_swayam_course_slots(curr_sem_id=curr_sem_id) - swayam_courses = [] - try: - swayam_courses = swayam_course_slots[0].courses.all() - except: - pass - Mess_bill = [] - Mess_pay = [] + Mess_bill = Monthly_bill.objects.filter(student_id = obj) + Mess_pay = Payments.objects.filter(student_id = obj) + # Branch Change Form save if request.method=='POST': if True: @@ -575,9 +506,8 @@ def academic_procedures_student(request): return render( request, '../templates/academic_procedures/academic.html', {'details': details, - # 'calendar': calendar, + # 'calendar': calendar, 'currently_registered': currently_registered_course, - 'current_courseregistrations': current_courseregistrations, 'pre_registered_course' : pre_registered_courses, 'pre_registered_course_show' : pre_registered_course_show, 'final_registered_course' : final_registered_courses, @@ -587,7 +517,6 @@ def academic_procedures_student(request): 'courses_list': next_sem_branch_course, 'fee_payment_mode_list' : fee_payment_mode_list, 'next_sem_registration_courses': next_sem_registration_courses, - 'current_sem_registration_courses': current_sem_branch_course, 'final_registration_choice' : final_registration_choice, 'unavailable_courses_nextsem' : unavailable_courses_nextsem, 'performance_list' : performance_list, @@ -610,20 +539,15 @@ def academic_procedures_student(request): # 'change_branch': change_branch, # 'add_course': add_course, 'add_courses_options': add_courses_options, - 'drop_courses_options' : drop_courses_options, - 'replace_courses_options' : replace_courses_options, + #'drop_courses_options' : drop_courses_options, # 'pre_register': pre_register, 'pre_registration_timestamp': pre_registration_timestamp, 'prd': pre_registration_date_flag, 'prd_start_date': prd_start_date, 'frd': final_registration_date_flag, 'adc_date_flag': add_or_drop_course_date_flag, - 'drop_date_flag' : drop_date_flag, 'pre_registration_flag' : pre_registration_flag, 'final_registration_flag': final_registration_flag, - 'swayam_registration_flag': swayam_registration_flag, - 'swayam_courses_count':swayam_courses_count, - 'swayam_courseslots':swayam_course_slots, # 'final_r': final_register_1, 'teaching_credit_registration_course' : teaching_credit_registration_course, @@ -639,20 +563,16 @@ def academic_procedures_student(request): 'hos_d':hos_d, 'tot_d':tot_d, 'attendence':attendence, - # 'backlogCourseList' : backlogCourseList, - # 'auto_backlog_courses_list' : auto_backlog_courses_list, 'BranchChangeForm': BranchChangeForm(), 'BranchFlag':branchchange_flag, 'assistantship_flag' : student_status, - 'notifications': notifs, - 'swayam_courses' : swayam_courses, } ) - elif request.session.get('currentDesignationSelected') == "Associate Professor" : + elif str(des.designation) == "Associate Professor" : return HttpResponseRedirect('/academic-procedures/main/') - elif request.session.get('currentDesignationSelected') in ["acadadmin", "studentacadadmin"]: + elif str(request.user) == "acadadmin" : return HttpResponseRedirect('/academic-procedures/main/') else: @@ -894,55 +814,22 @@ def approve_branch_change(request): choices.append(values[i]) else: continue - - from applications.programme_curriculum.models import Discipline, Batch - changed_branch = [] - changed_students = [] - for i in range(len(branches)): - get_student = ExtraInfo.objects.select_related('user','department').filter(id=choices[i][:7]).first() - if not get_student: - continue - - branch = DepartmentInfo.objects.filter(name=branches[i]).first() - if not branch: - continue - - get_student.department = branch + get_student = ExtraInfo.objects.all().select_related('user','department').filter(id=choices[i][:7]) + get_student = get_student[0] + branch = DepartmentInfo.objects.all().filter(name=branches[i]) + get_student.department = branch[0] changed_branch.append(get_student) - - # Update Student batch_id to match new discipline - student = Student.objects.select_related('id','id__user','id__department','batch_id').filter(id=choices[i][:7]).first() - if student and student.batch_id: - try: - new_discipline = Discipline.objects.filter(name=branches[i]).first() - if new_discipline: - new_batch = Batch.objects.filter( - year=student.batch_id.year, - name=student.batch_id.name, - discipline=new_discipline - ).first() - if new_batch: - student.batch_id = new_batch - changed_students.append(student) - except Exception as e: - pass - - if student: - change = BranchChange.objects.filter(user=student).first() - if change: - change.delete() - + student = Student.objects.all().select_related('id','id__user','id__department').filter(id=choices[i][:7]).first() + change = BranchChange.objects.select_related('branches','user','user__id','user__id__user','user__id__department').all().filter(user=student) + change = change[0] + change.delete() try: - if changed_branch: - ExtraInfo.objects.bulk_update(changed_branch, ['department']) - if changed_students: - Student.objects.bulk_update(changed_students, ['batch_id']) + ExtraInfo.objects.bulk_update(changed_branch,['department']) messages.info(request, 'Apply for branch change successfull') - except Exception as e: + except: messages.info(request, 'Unable to proceed, we will get back to you very soon') - return HttpResponseRedirect('/academic-procedures/main') else: @@ -999,21 +886,14 @@ def dropcourseadmin(request): rid - Registration ID of Registers table response_data - data to be responded. ''' - if user_check(request): - return HttpResponseRedirect('/academic-procedures/main') data = request.GET.get('id') - # data = data.split(" - ") - reg_id = int(data) - # student_id = data[0] - # course_code = data[1] - # course = Courses.objects.get(code=course_code , version = 1.0) + data = data.split(" - ") + course_code = data[1] # need to add batch and programme - # curriculum_object = Curriculum.objects.all().filter(course_code = course_code) + curriculum_object = Curriculum.objects.all().filter(course_code = course_code) try: - # Register.objects.filter(curr_id = curriculum_object.first(),student_id=int(data[0])).delete() - course_registration.objects.filter(id=reg_id).delete() - except Exception as e: - print(str(e)) + Register.objects.filter(curr_id = curriculum_object.first(),student_id=int(data[0])).delete() + except: pass # print("hello ") response_data = {} @@ -1027,7 +907,7 @@ def gen_course_list(request): course_id = request.POST['course'] course = Courses.objects.get(id = course_id) #obj = course_registration.objects.all().filter(course_id = course) - obj=course_registration.objects.filter(course_id__id=course_id, working_year=batch).select_related( + obj=course_registration.objects.filter(course_id__id=course_id, student_id__batch=batch).select_related( 'student_id__id__user','student_id__id__department').only('student_id__batch', 'student_id__id__user__first_name', 'student_id__id__user__last_name', 'student_id__id__department__name','student_id__id__user__username') @@ -1035,23 +915,13 @@ def gen_course_list(request): batch="" course="" obj="" - verified_students = [] - for registration in obj: - final_registration = FinalRegistration.objects.filter( - course_id=course, - semester_id=registration.semester_id, - student_id=registration.student_id, - verified=True - ).exists() - if final_registration: - verified_students.append({ - "rollno": registration.student_id.id.user.username, - "name": registration.student_id.id.user.first_name + " " + registration.student_id.id.user.last_name, - "department": registration.student_id.id.department.name - }) - verified_students = sorted(verified_students, key=lambda x: x['rollno']) + students = [] + for i in obj: + students.append({"rollno":i.student_id.id.user.username, + "name":i.student_id.id.user.first_name+" "+i.student_id.id.user.last_name, + "department":i.student_id.id.department.name}) html = render_to_string('academic_procedures/gen_course_list.html', - {'students': verified_students, 'batch':batch, 'course':course_id}, request) + {'students': students, 'batch':batch, 'course':course_id}, request) maindict = {'html': html} obj = json.dumps(maindict) return HttpResponse(obj, content_type='application/json') @@ -1080,12 +950,14 @@ def verify_course(request): current_user = get_object_or_404(User, username=request.user.username) user_details = ExtraInfo.objects.all().select_related( 'user', 'department').filter(user=current_user).first() - desig_id = Designation.objects.all().filter(Q(name='acadadmin') | Q(name='studentacadadmin')).first() - temp = HoldsDesignation.objects.all().select_related().filter(designation=desig_id).first() + desig_id = Designation.objects.all().filter(name='adminstrator').first() + temp = HoldsDesignation.objects.all().select_related().filter( + designation=desig_id).first() acadadmin = temp.working k = str(user_details).split() final_user = k[2] - if (request.session.get('currentDesignationSelected') not in ['acadadmin', 'studentacadadmin']) : + + if (str(acadadmin) != str(final_user)): return HttpResponseRedirect('/academic-procedures/') roll_no = request.POST["rollNo"] obj = ExtraInfo.objects.all().select_related( @@ -1096,42 +968,31 @@ def verify_course(request): 'firstname': firstname, 'lastname': lastname} obj2 = Student.objects.all().select_related( 'id', 'id__user', 'id__department').filter(id=roll_no).first() - # obj = Register.objects.all().select_related('curr_id', 'student_id', 'curr_id__course_id', - # 'student_id__id', 'student_id__id__user', 'student_id__id__department').filter(student_id=obj2) - batch = obj2.batch_id - curr_id = batch.curriculum - curr_sem_id = Semester.objects.get(curriculum = curr_id, semester_no = obj2.curr_semester_no) - # curr_sem_id = obj2.curr_semester_no + obj = Register.objects.all().select_related('curr_id', 'student_id', 'curr_id__course_id', + 'student_id__id', 'student_id__id__user', 'student_id__id__department').filter(student_id=obj2) + curr_sem_id = obj2.curr_semester_no details = [] current_sem_courses = get_currently_registered_course( - roll_no, curr_sem_id, True) + roll_no, curr_sem_id) idd = obj2 for z in current_sem_courses: - # print(z) - # z = z[1] - print(z) - course_code = z.course_id.code - course_name = z.course_id.name - replaced_by = course_replacement.objects.all().filter(old_course_registration=z).first() - # course_code, course_name = str(z).split(" - ") + z = z[1] + course_code, course_name = str(z).split(" - ") k = {} # reg_ig has course registration id appended with the the roll number # so that when we have removed the registration we can be redirected to this view - k['reg_id'] = z.id + k['reg_id'] = roll_no+" - "+course_code k['rid'] = roll_no+" - "+course_code # Name ID Confusion here , be carefull - courseobj2 = Courses.objects.all().filter(id=z.course_id.id) + courseobj2 = Courses.objects.all().filter(code=course_code) # if(str(z.student_id) == str(idd)): for p in courseobj2: k['course_id'] = course_code k['course_name'] = course_name - k['course_version'] = z.course_id.version - k['sem'] = z.semester_id.semester_no + k['sem'] = curr_sem_id k['credits'] = p.credit - k['registration_type'] = z.registration_type - k['replaced_by'] = replaced_by details.append(k) year = demo_date.year @@ -1145,16 +1006,11 @@ def verify_course(request): # TO DO Bdes date = {'year': yearr, 'semflag': semflag} course_list = Courses.objects.all() - semester_list = Semester.objects.filter(curriculum=curr_id) - courseslot_list = CourseSlot.objects.filter(semester__in=semester_list) - semester_no_list=[] - for i in semester_list: - semester_no_list.append(int(i.semester_no)) + semester_list = Semester.objects.all() html = render_to_string('academic_procedures/studentCourses.html', {'details': details, 'dict2': dict2, 'course_list': course_list, - 'courseslot_list': courseslot_list, 'semester_list': semester_list, 'date': date}, request) @@ -1168,32 +1024,16 @@ def verify_course(request): # view to add Course for a student def acad_add_course(request): if(request.method == "POST"): - if user_check(request): - return HttpResponseRedirect('/academic-procedures/main') course_id = request.POST["course_id"] - courseslot_id = request.POST["courseslot_id"] course = Courses.objects.get(id=course_id) - courseslot = CourseSlot.objects.get(id=courseslot_id) roll_no = request.POST['roll_no'] student = Student.objects.all().select_related( 'id', 'id__user', 'id__department').filter(id=roll_no).first() sem_id = request.POST['semester_id'] semester = Semester.objects.get(id=sem_id) - registration_type = request.POST["registration_type"] - working_year = request.POST.get("working_year", datetime.datetime.now().year) - old_course_id = request.POST.get("old_course") - with transaction.atomic(): - cr = course_registration( - course_slot_id=courseslot, course_id=course, student_id=student, semester_id=semester , working_year = working_year, registration_type=registration_type) - cr.save() - if old_course_id: - old_course_obj = course_registration.objects.filter(id=old_course_id).first() - if old_course_obj: - course_replacement.objects.create( - old_course_registration=old_course_obj, - new_course_registration=cr - ) - + cr = course_registration( + course_id=course, student_id=student, semester_id=semester) + cr.save() return HttpResponseRedirect('/academic-procedures/') @@ -1230,7 +1070,7 @@ def acad_branch_change(request): k = str(user_details).split() final_user = k[2] - if ( request.session.get('currentDesignationSelected') not in ['acadadmin', 'studentacadadmin']): + if (str(acadadmin) != str(final_user)): return HttpResponseRedirect('/academic-procedures/') # year = datetime.datetime.now().year @@ -1485,30 +1325,6 @@ def get_course_verification_date_eligibilty(current_date): return False except Exception as e: return False - -def get_swayam_registration_eligibility(current_date, user_sem, year): - try: - swayam_registration_date = Calendar.objects.all().filter(description=f"Swayam Registration {user_sem} {year}").first() - swayam_start_date = swayam_registration_date.from_date - swayam_end_date = swayam_registration_date.to_date - if current_date>=swayam_start_date and current_date<=swayam_end_date: - return True - else : - return False - except Exception as e: - return False - -def get_drop_course_date_eligibility(current_date, user_sem, year): - try: - drop_course_date = Calendar.objects.all().filter(description=f"Drop course {user_sem} {year}").first() - drop_start_date = drop_course_date.from_date - drop_end_date = drop_course_date.to_date - if current_date>=drop_start_date and current_date<=drop_end_date: - return True - else : - return False - except Exception as e: - return False def get_user_branch(user_details): return user_details.department.name @@ -1588,79 +1404,7 @@ def pre_registration(request): else: return HttpResponseRedirect('/academic-procedures/main') -@login_required(login_url='/accounts/login') -@transaction.atomic -def auto_pre_registration(request): - if request.method == 'POST': - try: - current_user = get_object_or_404(User, username=request.POST.get('user')) - current_user = ExtraInfo.objects.all().select_related('user','department').filter(user=current_user).first() - current_user = Student.objects.all().filter(id=current_user.id).first() - sem_id = Semester.objects.get(id = request.POST.get('semester')) - - course_slots=request.POST.getlist("course_slot") - try: - student_registeration_check=get_student_registrtion_check(current_user,sem_id) - if(student_registeration_check and student_registeration_check.pre_registration_flag==True): - messages.error(request,"You have already registered for next semester") - return HttpResponseRedirect('/academic-procedures/main') - except Exception as e: - print(e) - - reg_curr = [] - final_reg_curr = [] - existing_entries = set() - for course_slot in course_slots : - course_priorities = request.POST.getlist("course_priority-"+course_slot) - registration_type = request.POST.get('registration_type-'+course_slot, 'Regular') - if(course_priorities[0] == 'NULL'): - continue - course_slot_id_for_model = CourseSlot.objects.get(id = int(course_slot)) - print("=----> course_priorities ----- ",course_priorities) - print("------------>course slot id ",course_slot_id_for_model) - for course_priority in course_priorities: - if(course_priority == 'NULL'): - continue - priority_of_current_course,course_id = map(int,course_priority.split("-")) - - course_id_for_model = Courses.objects.get(id = course_id) - current_combination = (course_slot_id_for_model.id, course_id_for_model.id) - if current_combination not in existing_entries: - p = InitialRegistration( - course_id = course_id_for_model, - semester_id = sem_id, - student_id = current_user, - course_slot_id = course_slot_id_for_model, - priority = priority_of_current_course, - registration_type = registration_type - ) - # f =FinalRegistration(student_id=current_user ,course_slot_id=course_slot_id_for_model , course_id=course_id_for_model ,semester_id=sem_id) - # final_reg_curr.append(f) - reg_curr.append(p) - existing_entries.add(current_combination) - try: - InitialRegistration.objects.bulk_create(reg_curr) - # FinalRegistration.objects.bulk_create(final_reg_curr) - registration_check = StudentRegistrationChecks( - student_id = current_user, - pre_registration_flag = True, - final_registration_flag = False, - semester_id = sem_id - ) - registration_check.save() - messages.info(request, 'Successfully Registered.') - messages.success(request, "Successfully Registered.") - return HttpResponseRedirect('/academic-procedures/stu') - except Exception as e: - messages.error(request, "Error in Registration.") - return HttpResponseRedirect('/academic-procedures/stu') - except Exception as e: - messages.error(request, "Error in Registration.") - return HttpResponseRedirect('/academic-procedures/main') - else: - return HttpResponseRedirect('/academic-procedures/main') - def get_student_registrtion_check(obj, sem): return StudentRegistrationChecks.objects.all().filter(student_id = obj, semester_id = sem).first() @@ -1710,7 +1454,6 @@ def final_registration(request): else: return HttpResponseRedirect('/academic-procedures/main') - def allot_courses(request): if user_check(request): return HttpResponseRedirect('/academic-procedures/main') @@ -1721,65 +1464,30 @@ def allot_courses(request): profiles=request.FILES['allotedCourses'] batch_id=request.POST['batch'] sem_no=int(request.POST['semester']) - working_year =int(request.POST['working_year']) - + batch=Batch.objects.get(id=batch_id) sem_id=Semester.objects.get(curriculum=batch.curriculum,semester_no=sem_no) - print(batch , sem_id) - # format of excel sheet being uploaded should be xls only , otherwise error + excel = xlrd.open_workbook(file_contents=profiles.read()) sheet=excel.sheet_by_index(0) - course_registrations=[] final_registrations=[] - pre_registrations=[] - student_checks=[] - # print('>>>>>>>>>>>>>>>>>>>' , sheet.nrows) - currroll=set() for i in range(1,sheet.nrows): roll_no = str(sheet.cell(i,0).value).split(".")[0] - # print("Roll No from Excel:", roll_no) course_slot_name = sheet.cell_value(i,1) course_code = sheet.cell_value(i,2) course_name = sheet.cell_value(i,3) - try: - - user=User.objects.get(username=roll_no) - user_info = ExtraInfo.objects.get(user=user) - student = Student.objects.get(id=user_info) - course_slot=CourseSlot.objects.get(name=course_slot_name.strip(),semester=sem_id) - slot_courses = course_slot.courses.filter() - # for i in slot_courses: - # print("slot course ", i) - # print(course_slot) - print(course_code.strip() , course_name.strip(),student) - course = slot_courses.get(code=course_code.strip()) - # print(course_code.strip() , course_name.strip(),student) - # course = Courses.objects.get(code=course_code.strip(),name=course_name.strip()) - if(roll_no not in currroll): - student_check=StudentRegistrationChecks(student_id = student, semester_id = sem_id, pre_registration_flag = True,final_registration_flag = True) - student_checks.append(student_check) - currroll.add(roll_no) - # print(">>>>>",roll_no,course_slot_name,course_code,course_name) - except Exception as e: - print('----------------------' , e) - pre_registration=InitialRegistration(student_id=student,course_slot_id=course_slot, - course_id=course,semester_id=sem_id,priority=1) - pre_registrations.append(pre_registration) + user=User.objects.get(username=roll_no) + user_info = ExtraInfo.objects.get(user=user) + student = Student.objects.get(id=user_info) + course_slot=CourseSlot.objects.get(name=course_slot_name.strip(),semester=sem_id) + course = Courses.objects.get(code=course_code.strip(),name=course_name.strip()) + #print(">>>>>",roll_no,course_slot_name,course_code,course_name) final_registration=FinalRegistration(student_id=student,course_slot_id=course_slot, - course_id=course,semester_id=sem_id, verified=True ) + course_id=course,semester_id=sem_id) final_registrations.append(final_registration) - - courseregistration=course_registration(working_year=working_year,course_id=course,semester_id=sem_id,student_id=student,course_slot_id=course_slot) - course_registrations.append(courseregistration) - - try: - with transaction.atomic(): - InitialRegistration.objects.bulk_create(pre_registrations) - StudentRegistrationChecks.objects.bulk_create(student_checks) - FinalRegistration.objects.bulk_create(final_registrations) - course_registration.objects.bulk_create(course_registrations) + FinalRegistration.objects.bulk_create(final_registrations) messages.success(request, 'Successfully uploaded!') return HttpResponseRedirect('/academic-procedures/main') # return HttpResponse("Success") @@ -1788,78 +1496,12 @@ def allot_courses(request): return HttpResponseRedirect('/academic-procedures/main') # return HttpResponse("Success") except Exception as e: - print(e) messages.error(request, 'Error: Query does not match. Please check if all the data input is in the correct format.') return HttpResponseRedirect('/academic-procedures/main') # return HttpResponseRedirect(request.META.get('HTTP_REFERER')) # return HttpResponse("Fail") -@login_required(login_url='/accounts/login') -def allot_courses_after_add_and_drop(request): - if user_check(request): - return HttpResponseRedirect('/academic-procedures/main') - - - try: - if request.method == 'POST' and request.FILES: - profiles=request.FILES['allotedCourses'] - working_year =int(request.POST['working_year']) - excel = xlrd.open_workbook(file_contents=profiles.read()) - sheet=excel.sheet_by_index(0) - course_registrations=[] - course_registrations=[] - final_registrations=[] - pre_registrations=[] - student_checks=[] - currroll=set() - print(sheet.nrows) - for i in range(1,sheet.nrows): - roll_no = str(sheet.cell(i,0).value).split(".")[0] - course_slot_name = sheet.cell_value(i,1) - course_code = sheet.cell_value(i,2) - roll_no =roll_no.strip() - try: - user=User.objects.get(username=roll_no) - user_info = ExtraInfo.objects.get(user=user) - student = Student.objects.get(id=user_info) - batch=student.batch_id - sem_id=Semester.objects.get(curriculum=batch.curriculum,semester_no=student.curr_semester_no) - course_slot=CourseSlot.objects.get(name=course_slot_name.strip(),semester=sem_id) - course = course_slot.courses.get(code=course_code.strip()) - - if(roll_no not in currroll): - student_check=StudentRegistrationChecks(student_id = student, semester_id = sem_id, pre_registration_flag = True,final_registration_flag = True) - student_checks.append(student_check) - currroll.add(roll_no) - except Exception as e: - print(e, '---', roll_no, course_slot_name, course_code) - - - pre_registration=InitialRegistration(student_id=student,course_slot_id=course_slot, - course_id=course,semester_id=sem_id,priority=1) - pre_registrations.append(pre_registration) - final_registration=FinalRegistration(student_id=student,course_slot_id=course_slot, - course_id=course,semester_id=sem_id, verified=True ) - final_registrations.append(final_registration) - courseregistration=course_registration(working_year=working_year,course_id=course,semester_id=sem_id,student_id=student,course_slot_id=course_slot) - course_registrations.append(courseregistration) - - - try: - with transaction.atomic(): - InitialRegistration.objects.bulk_create(pre_registrations) - StudentRegistrationChecks.objects.bulk_create(student_checks) - FinalRegistration.objects.bulk_create(final_registrations) - course_registration.objects.bulk_create(course_registrations) - messages.success(request, 'Successfully uploaded!') - return HttpResponseRedirect('/academic-procedures/main') - except Exception as e: - messages.error(request, 'Error: '+str(e)) - return HttpResponseRedirect('/academic-procedures/main') - except Exception as e: - messages.error(request, 'Error: Query does not match. Please check if all the data input is in the correct format.') - return HttpResponseRedirect('/academic-procedures/main') @@ -1883,7 +1525,7 @@ def user_check(request): try: current_user = get_object_or_404(User, username=request.user.username) user_details = ExtraInfo.objects.all().select_related('user','department').filter(user=current_user).first() - desig_id = Designation.objects.all().filter(Q(name='acadadmin') | Q(name='studentacadadmin')) + desig_id = Designation.objects.all().filter(name='acadadmin') temp = HoldsDesignation.objects.all().select_related().filter(designation = desig_id).first() acadadmin = temp.working k = str(user_details).split() @@ -1893,7 +1535,7 @@ def user_check(request): final_user="" pass - if (request.session.get('currentDesignationSelected') not in ['acadadmin', 'studentacadadmin']): + if (str(acadadmin) != str(final_user)): return True else: return False @@ -1981,8 +1623,7 @@ def add_courses(request): course_id = course_id, student_id=current_user, course_slot_id = courseslot_id, - semester_id=sem_id, - working_year = datetime.datetime.now().year, + semester_id=sem_id ) if p not in reg_curr: reg_curr.append(p) @@ -2022,52 +1663,6 @@ def drop_course(request): else: return HttpResponseRedirect('/academic-procedures/main') -def replace_courses(request): - """ - This function is used to replace elective courses which have been registered - @param: - request - contains metadata about the requested page - @variables: - current_user - contains current logged in user - sem_id - contains current semester id - count - no of courses to be replaced - course_id - contains course id for a particular course - course_slot_id - contains course slot id for a particular course - choice - contains choice of a particular course - slot - contains slot of a particular course - """ - - if request.method == 'POST' : - try: - current_user = get_object_or_404(User, username=request.POST.get('user')) - current_user = ExtraInfo.objects.all().filter(user=current_user).first() - current_user = Student.objects.all().filter(id=current_user.id).first() - - # sem_id = Semester.objects.get(id = request.POST.get('semester')) - count = request.POST.get('ct') - count = int(count) - - for i in range(1, count+1): - choice = "choice["+str(i)+"]" - slot = "slot["+str(i)+"]" - try : - - course_id = Courses.objects.get(id = request.POST.get(choice)) - courseslot_id = CourseSlot.objects.get(id = request.POST.get(slot)) - - registered_course = course_registration.objects.filter(student_id=current_user, course_slot_id = courseslot_id).first() - - if registered_course: - registered_course.course_id = course_id - registered_course.save() - except Exception as e: - continue - return HttpResponseRedirect('/academic-procedures/main') - except Exception as e: - return HttpResponseRedirect('/academic-procedures/main') - else : - return HttpResponseRedirect('/academic-procedures/main') - @@ -2167,7 +1762,7 @@ def get_final_registration_choices(branch_courses,batch): max_limit = courseslot.max_registration_limit lis = [] for course in courseslot.courses.all(): - if FinalRegistration.objects.filter(student_id__batch_id__year = batch, course_id = course).count() < max_limit: + if FinalRegistration .objects.filter(student_id__batch_id__year = batch, course_id = course).count() < max_limit: lis.append(course) else: unavailable_courses.append(course) @@ -2182,13 +1777,10 @@ def get_add_course_options(branch_courses, current_register, batch): for c in current_register: slots.append(c[0]) for courseslot in branch_courses: - if courseslot.type == "Swayam": - continue max_limit = courseslot.max_registration_limit if courseslot not in slots: lis = [] for course in courseslot.courses.all(): - # print(course) if course_registration.objects.filter(student_id__batch_id__year = batch, course_id = course).count() < max_limit: lis.append(course) course_option.append((courseslot, lis)) @@ -2201,24 +1793,7 @@ def get_drop_course_options(current_register): courses.append(item[1]) return courses -def get_replace_course_options( current_register, batch): - replace_options = [] - - for registered_course in current_register: - courseslot_id = registered_course[0] - course_id = registered_course[1] - - courseslot = courseslot_id - coursename = course_id.name - lis = [] - - if 'Elective' in courseslot.type: - for course in courseslot.courses.all(): - if course != course_id: - lis.append(course) - replace_options.append((courseslot, coursename, lis)) - return replace_options @@ -2250,11 +1825,7 @@ def get_user_semester(roll_no, ug_flag, masters_flag, phd_flag): def get_branch_courses(roll_no, user_sem, branch): roll = str(roll_no) - try: - year = int(roll[:4]) - except: - year = int(roll[:2]) - year = 2000 + year + year = int(roll[:4]) courses = Curriculum.objects.all().select_related().filter(batch=(year)) courses = courses.filter(sem = user_sem) courses = courses.filter(floated = True) @@ -2283,17 +1854,11 @@ def get_currently_registered_courses(id, user_sem): ans.append(course) return ans -def get_currently_registered_course(id, sem_id, courseregobj=False): - if (type(sem_id) == int): - obj = course_registration.objects.all().filter(student_id = id, semester_id__semester_no=sem_id) - else: - obj = course_registration.objects.all().filter(student_id = id).order_by('-semester_id__semester_no') +def get_currently_registered_course(id, sem_id): + obj = course_registration.objects.all().filter(student_id = id, semester_id=sem_id) courses = [] for i in obj: - if (courseregobj): - courses.append(i) - else: - courses.append((i.course_slot_id,i.course_id)) + courses.append((i.course_slot_id,i.course_id)) return courses @@ -2335,13 +1900,13 @@ def acad_person(request): des = HoldsDesignation.objects.all().select_related().filter(user = request.user).first() - if request.session.get('currentDesignationSelected') == "student": + if str(des.designation) == "student": return HttpResponseRedirect('/academic-procedures/main/') - elif request.session.get('currentDesignationSelected') == "Associate Professor" : + elif str(des.designation) == "Associate Professor" : return HttpResponseRedirect('/academic-procedures/main/') - elif request.session.get('currentDesignationSelected') in ['acadadmin', 'studentacadadmin']: + elif str(request.user) == "acadadmin" : # year = datetime.datetime.now().year @@ -2417,7 +1982,7 @@ def acad_person(request): def acad_proced_global_context(): year = demo_date.year month = demo_date.month - yearr = get_acad_year(user_sem,year) + yearr = str(year) + "-" + str(year+1) semflag = 0 queryflag = 0 query_option1 = get_batch_query_detail(month, year) @@ -2694,13 +2259,12 @@ def student_list(request): batch_id = Batch.objects.get(id = batch) student_obj = FeePayments.objects.all().select_related('student_id').filter(student_id__batch_id = batch_id) if (student_obj): - reg_table = student_obj.prefetch_related('student_id__studentregistrationchecks').filter(semester_id = student_obj[0].semester_id, student_id__studentregistrationchecks__final_registration_flag = True , student_id__finalregistration__verified=False , student_id__finalregistration__semester_id= student_obj[0].semester_id ).select_related( + reg_table = student_obj.prefetch_related('student_id__studentregistrationchecks').filter(semester_id = student_obj[0].semester_id, student_id__studentregistrationchecks__final_registration_flag = True).select_related( 'student_id','student_id__id','student_id__id__user','student_id__id__department').values( 'student_id__id','student_id__id__user__first_name','student_id__id__user__last_name','student_id__batch','student_id__id__department__name', 'student_id__programme','student_id__curr_semester_no','student_id__id__sex','student_id__id__phone_no','student_id__category', 'student_id__specialization','mode','transaction_id','deposit_date','fee_paid','utr_number','reason','fee_receipt','actual_fee', - 'student_id__id__user__username').order_by('student_id__id__user').distinct() - # print('------------------------------------------------------------------------------------------------------------------------------------------',reg_table) + 'student_id__id__user__username').order_by('student_id__id__user') else : reg_table = [] @@ -2760,7 +2324,7 @@ def student_list(request): 'specialization','gender','category', 'pwd_status','phone_no','actual_fee', 'fee_paid','reason','date_deposited', - 'mode','utr_number','fee_receipt')).distinct() + 'mode','utr_number','fee_receipt')) excel_response = BytesIO() @@ -2787,9 +2351,8 @@ def course_list(request): student_id = request_body['student_id'] semester_id = request_body['semester_id'] - # final_registration_table = FinalRegistration.objects.all().filter(semester_id = semester_id, verified = False) - # final = final_registration_table.filter(student_id = student_id, semester_id = semester_id) - final = FinalRegistration.objects.all().filter(semester_id__semester_no = semester_id, student_id__id=student_id, verified = False) + final_registration_table = FinalRegistration.objects.all().filter(semester_id = semester_id, verified = False) + final = final_registration_table.filter(student_id = student_id, semester_id = semester_id) html = render_to_string('academic_procedures/student_course_list.html',{"course_list":final}, request) return HttpResponse(json.dumps({'html': html}),content_type="application/json") @@ -2799,12 +2362,6 @@ def process_verification_request(request): return verify_registration(request) return JsonResponse({'status': 'Failed'}, status=400) - -def auto_process_verification_request(request): - if request.is_ajax(): - return auto_verify_registration(request) - return JsonResponse({'status': 'Failed'}, status=400) - @transaction.atomic def verify_registration(request): @@ -2814,17 +2371,12 @@ def verify_registration(request): batch = student.batch_id curr_id = batch.curriculum - - if(student.curr_semester_no+1 >= 9): - sem_no = 4 - else: - sem_no = student.curr_semester_no+1 - - sem_id = Semester.objects.get(curriculum = curr_id, semester_no = sem_no) + sem_id = Semester.objects.get(curriculum = curr_id, semester_no = student.curr_semester_no+1) final_register_list = FinalRegistration.objects.all().filter(student_id = student, verified = False, semester_id = sem_id) - + sem_no = student.curr_semester_no + 1 + with transaction.atomic(): ver_reg = [] for obj in final_register_list: @@ -2832,13 +2384,11 @@ def verify_registration(request): course_id=obj.course_id, student_id=student, semester_id=obj.semester_id, - course_slot_id = obj.course_slot_id, - working_year = datetime.datetime.now().year, + course_slot_id = obj.course_slot_id ) ver_reg.append(p) o = FinalRegistration.objects.filter(id= obj.id).update(verified = True) course_registration.objects.bulk_create(ver_reg) - # StudentRegistrationChecks.objects.filter(student_id = student_id, semester_id = sem_id).update(final_registration_flag = True) academics_module_notif(request.user, student.id.user, 'registration_approved') Student.objects.filter(id = student_id).update(curr_semester_no = sem_no) return JsonResponse({'status': 'success', 'message': 'Successfully Accepted'}) @@ -2849,74 +2399,18 @@ def verify_registration(request): batch = student_id.batch_id curr_id = batch.curriculum - if(student.curr_semester_no+1 >= 9): - sem_no = 4 - else: - sem_no = student.curr_semester_no+1 - sem_id = Semester.objects.get(curriculum = curr_id, semester_no = sem_no) + sem_id = Semester.objects.get(curriculum = curr_id, semester_no = student_id.curr_semester_no + 1) + with transaction.atomic(): - academicadmin = get_object_or_404(User, username = request.user.username) + academicadmin = get_object_or_404(User, username = "acadadmin") FinalRegistration.objects.filter(student_id = student_id, verified = False, semester_id = sem_id).delete() StudentRegistrationChecks.objects.filter(student_id = student_id, semester_id = sem_id).update(final_registration_flag = False) FeePayments.objects.filter(student_id = student_id, semester_id = sem_id).delete() academics_module_notif(academicadmin, student_id.id.user, 'Registration Declined - '+reject_reason) return JsonResponse({'status': 'success', 'message': 'Successfully Rejected'}) -@transaction.atomic -def auto_verify_registration(request): - if request.POST.get('status_req') == "accept" : - student_id = request.POST.get('student_id') - student = Student.objects.get(id = student_id) - batch = student.batch_id - curr_id = batch.curriculum - - if(student.curr_semester_no+1 >= 9): - # print('----------------------------------------------------------------' , student.curr_semester_no) - sem_no = 8 - else: - # print('----------------------------------------------------------------' , student.curr_semester_no) - sem_no = student.curr_semester_no+1 - sem_id = Semester.objects.get(curriculum = curr_id, semester_no = sem_no) - # print('----------------------------------------------------------------' , student.curr_semester_no) - - final_register_list = FinalRegistration.objects.all().filter(student_id = student, verified = False, semester_id = sem_id) - - # final_register_list = FinalRegistration.objects.all().filter(student_id = student, verified = False) - - with transaction.atomic(): - for obj in final_register_list: - o = FinalRegistration.objects.filter(id= obj.id).update(verified = True) - course_registration.objects.create( - student_id=obj.student_id, - course_id=obj.course_id, - semester_id=obj.semester_id, - course_slot_id=obj.course_slot_id, - registration_type=obj.registration_type, - working_year=demo_date.year # Set the current year - ) - academics_module_notif(request.user, student.id.user, 'registration_approved') - - Student.objects.filter(id = student_id).update(curr_semester_no = sem_no) - return JsonResponse({'status': 'success', 'message': 'Successfully Accepted'}) - - elif request.POST.get('status_req') == "reject" : - reject_reason = request.POST.get('reason') - student_id = request.POST.get('student_id') - student_id = Student.objects.get(id = student_id) - batch = student_id.batch_id - curr_id = batch.curriculum - if(student_id.curr_semester_no+1 >= 9): - sem_no = 8 - else: - sem_no = student_id.curr_semester_no+1 - sem_id = Semester.objects.get(curriculum = curr_id, semester_no = sem_no) - with transaction.atomic(): - academicadmin = get_object_or_404(User, username = request.user.username) - # FinalRegistration.objects.filter(student_id = student_id, verified = False, semester_id = sem_id).delete() - StudentRegistrationChecks.objects.filter(student_id = student_id, semester_id = sem_id).update(final_registration_flag = False) - FeePayments.objects.filter(student_id = student_id, semester_id = sem_id).delete() - academics_module_notif(academicadmin, student_id.id.user, 'Registration Declined - '+reject_reason) - return JsonResponse({'status': 'success', 'message': 'Successfully Rejected'}) + + def get_registration_courses(courses): x = [[]] @@ -2981,8 +2475,8 @@ def course_marks_data(request): try: course_id = request.POST.get('course_id') course = Courses.objects.select_related().get(id = course_id) - # print(course) - # print(course_id) + print(course) + print(course_id) student_list = course_registration.objects.filter(course_id__id=course_id).select_related( 'student_id__id__user','student_id__id__department').only('student_id__batch', 'student_id__id__user__first_name', 'student_id__id__user__last_name', @@ -3385,39 +2879,6 @@ def generate_grade_sheet_pdf(request): return response return HttpResponse("PDF could not be generated") -@api_view(['GET']) -@permission_classes([IsAuthenticated]) -def generate_course_registration_receipt(request): - try: - current_user = request.user - current_user = ExtraInfo.objects.all().filter(user=current_user).first() - current_user = Student.objects.all().filter(id = current_user.id).first() - - batch = current_user.batch_id - curr_id = batch.curriculum - curr_sem_id = Semester.objects.get(curriculum = curr_id, semester_no = current_user.curr_semester_no) - courses = course_registration.objects.filter(student_id = current_user, semester_id = curr_sem_id) - - course_list = [] - for course in courses: - course_list.append({ - 'course_code': course.course_id.code, - 'course_name': course.course_id.name, - 'credits': course.course_id.credit - }) - - context = { - 'current_courseregistrations': course_list, - 'semester_no': current_user.curr_semester_no, - 'name': request.user.first_name + request.user.last_name, - 'roll_no' : current_user.id_id, - 'batch': batch.name+' '+str(batch.year), - 'branch': curr_id.name.split(' ')[0] - } - return JsonResponse(context) - - except Exception as e: - return JsonResponse({'error': 'Unable to retrieve data', 'details': str(e)}) def get_spi(course_list,grade_list): spi = 0.0 @@ -4037,386 +3498,7 @@ def mdue(request): content = json.dumps("success") return HttpResponse(content) - - - -def get_detailed_sem_courses(sem_id): - course_slots = CourseSlot.objects.filter(semester_id=sem_id) - # Serialize queryset of course slots into JSON - course_slots_json = serialize('json', course_slots) - # Convert JSON string into Python object - course_slots_data = json.loads(course_slots_json) - - # Iterate over each course slot data and include associated course data - for slot_data in course_slots_data: - # Retrieve associated courses for the current course slot - slot = CourseSlot.objects.get(id=slot_data['pk']) - courses = list(slot.courses.all().values()) - # Add courses data to the course slot data - slot_data['courses'] = courses - - return course_slots_data - - -def get_next_sem_courses(request): - if request.method == 'POST': - data = json.loads(request.body) - next_sem = data.get('next_sem') - branch = data.get('branch') - programme = data.get('programme') - batch = data.get('batch') - # we go to student table and apply filters and get batch_id of the students with these filter - batch_id = Student.objects.filter(programme = programme , batch = batch , specialization = branch)[0].batch_id - - curr_id = batch_id.curriculum - # print('-----------------------------------------------------------------------------------------', curr_id) - # curr_id = 1 - next_sem_id = Semester.objects.get(curriculum = curr_id, semester_no = next_sem) - - if next_sem_id: - next_sem_registration_courses = get_detailed_sem_courses(next_sem_id ) - # print(next_sem_registration_courses) - return JsonResponse(next_sem_registration_courses, safe=False) - return JsonResponse({'error': 'Invalid request'}) - - -def add_course_to_slot(request): - if request.method == 'POST': - data = json.loads(request.body) - course_slot_name = data.get('course_slot_name') - course_code = data.get('course_name') - # print('-----------------------------------------------------------------------------------------' , course_slot_name , course_code) - try: - course_slot = CourseSlot.objects.filter(name=course_slot_name).first() - course = Courses.objects.filter(code=course_code).first() - course_slot.courses.add(course) - - return JsonResponse({'message': f'Course {course_code} added to slot {course_slot_name} successfully.'}, status=200) - except CourseSlot.DoesNotExist: - return JsonResponse({'error': 'Course slot does not exist.'}, status=400) - except Course.DoesNotExist: - return JsonResponse({'error': 'Course does not exist.'}, status=400) - - return JsonResponse({'error': 'Invalid request method.'}, status=405) - - -def remove_course_from_slot(request): - if request.method == 'POST': - data = json.loads(request.body) - course_slot_name = data.get('course_slot_name') - course_code = data.get('course_name') - # print('-----------------------------------------------------------------------------------------' , course_slot_name , course_code) - try: - course_slot = CourseSlot.objects.filter(name=course_slot_name).first() - course = Courses.objects.filter(code=course_code).first() - course_slot.courses.remove(course) - return JsonResponse({'message': f'Course {course_code} removed from slot {course_slot_name} successfully.'}, status=200) - except CourseSlot.DoesNotExist: - return JsonResponse({'error': 'Course slot does not exist.'}, status=400) - except Course.DoesNotExist: - return JsonResponse({'error': 'Course does not exist.'}, status=400) - - return JsonResponse({'error': 'Invalid request method.'}, status=405) - - -def add_one_course(request): - if request.method == 'POST': - try: - # print(request.POST) - current_user = get_object_or_404(User, username=request.POST.get('user')) - current_user = ExtraInfo.objects.all().filter(user=current_user).first() - current_user = Student.objects.all().filter(id=current_user.id).first() - - sem_id = Semester.objects.get(id=request.POST.get('semester')) - choice = request.POST.get('choice') - slot = request.POST.get('slot') - - try: - course_id = Courses.objects.get(id=choice) - courseslot_id = CourseSlot.objects.get(id=slot) - print(courseslot_id) - print(courseslot_id.type) - if course_registration.objects.filter(course_slot_id_id=courseslot_id, student_id=current_user).count() == 1 and courseslot_id.type != "Swayam": - already_registered_course_id = course_registration.objects.filter(course_slot_id_id=courseslot_id, student_id=current_user)[0].course_id - # print(already_registered_course_id) - msg = 'Already Registered in the course : ' +already_registered_course_id.code + '-'+ already_registered_course_id.name - return JsonResponse({'message' : msg}) - if((course_registration.objects.filter(course_id=course_id, student_id=current_user).count() >= 1)): - return JsonResponse({'message': 'Already registered in this course!'}, status=200) - # Check if maximum course registration limit has not been reached - if course_registration.objects.filter(student_id__batch_id__year=current_user.batch_id.year, course_id=course_id).count() < courseslot_id.max_registration_limit and \ - (course_registration.objects.filter(course_id=course_id, student_id=current_user).count() == 0): - p = course_registration( - course_id=course_id, - student_id=current_user, - course_slot_id=courseslot_id, - semester_id=sem_id, - working_year = datetime.datetime.now().year, - ) - p.save() - return JsonResponse({'message': 'Course added successfully'}) - else: - return JsonResponse({'message': 'Course not added because seats are full!'}, status=200) - except Exception as e: - return JsonResponse({'message': 'Error adding course'}, status=500) - except Exception as e: - return JsonResponse({'message': 'Error adding course'}, status=500) - else: - return JsonResponse({'message': 'Invalid request method'}, status=405) - -def replace_one_course(request): - if request.method == 'POST' : - try: - current_user = get_object_or_404(User, username=request.POST.get('user')) - current_user = ExtraInfo.objects.all().filter(user=current_user).first() - current_user = Student.objects.all().filter(id=current_user.id).first() - - - course_id = Courses.objects.get(id = request.POST.get('choice')) - courseslot_id = CourseSlot.objects.get(id = request.POST.get('slot')) - if course_registration.objects.filter(student_id__batch_id__year=current_user.batch_id.year, course_id=course_id).count() < courseslot_id.max_registration_limit and \ - (course_registration.objects.filter(course_id=course_id, student_id=current_user).count() == 0): - # print('---------------------------------------------------------------------------------' , course_registration.objects.filter(student_id__batch_id__year=current_user.batch_id.year, course_id=course_id).count() , courseslot_id.max_registration_limit ) - registered_course = course_registration.objects.filter(student_id=current_user, course_slot_id = courseslot_id).first() - - if registered_course: - registered_course.course_id = course_id - registered_course.save() - else: - return JsonResponse({'message': 'Cannot Replace to this course seats are full!'}, status=200) - - return JsonResponse({'message': 'Course Replaced Successfully'}, status=200) - except Exception as e: - return JsonResponse({'message': 'Error Replacing course'}, status=500) - else : - return JsonResponse({'message': 'Invalid request method'}, status=405) - -def get_sem_swayam(sem_id, batch): - courses = [] - course_slots = CourseSlot.objects.all().filter(type='Swayam') - - for slot in course_slots: - courses.append(slot) - - return courses - -def replaceSwayam(request): - if(request.POST): - # print(f"++++++++++++++++++++++++++++++++++++++++++++++++{request.POST}") - - current_user = get_object_or_404(User, username=request.user.username) - user_details = ExtraInfo.objects.all().select_related( - 'user', 'department').filter(user=current_user).first() - desig_id = Designation.objects.all().filter(Q(name='acadadmin') | Q(name='studentacadadmin')).first() - temp = HoldsDesignation.objects.all().select_related().filter( - designation=desig_id).first() - acadadmin = temp.working - k = str(user_details).split() - final_user = k[2] - - if (request.session.get('currentDesignationSelected') not in ['acadadmin', 'studentacadadmin']): - return HttpResponseRedirect('/academic-procedures/') - roll_no = request.POST["rollNo"] - obj = ExtraInfo.objects.all().select_related( - 'user', 'department').filter(id=roll_no).first() - firstname = obj.user.first_name - lastname = obj.user.last_name - dict2 = {'roll_no': roll_no, - 'firstname': firstname, 'lastname': lastname} - - details = [] - obj2 = Student.objects.all().select_related( - 'id', 'id__user', 'id__department').filter(id=roll_no).first() - # obj = Register.objects.all().select_related('curr_id', 'student_id', 'curr_id__course_id', - # 'student_id__id', 'student_id__id__user', 'student_id__id__department').filter(student_id=obj2) - batch = obj2.batch_id - curr_id = batch.curriculum - curr_sem_id = Semester.objects.get(curriculum = curr_id, semester_no = obj2.curr_semester_no) - current_sem_courses = get_currently_registered_elective( - roll_no, curr_sem_id) - current_sem_swayam = get_sem_swayam(curr_sem_id,2025) - idd = obj2 - for z in current_sem_courses: - eletive_id=z[2] - z = z[1] - - course_code = z.code - course_name = z.name - - k = {} - # reg_ig has course registration id appended with the the roll number - # so that when we have removed the registration we can be redirected to this view - k['reg_id'] = roll_no+" - "+course_code - k['rid'] = roll_no+" - "+course_code - # Name ID Confusion here , be carefull - courseobj2 = Courses.objects.all().filter(code=course_code) - # if(str(z.student_id) == str(idd)): - for p in courseobj2: - k['course_id'] = course_code - k['course_name'] = course_name - k['sem'] = curr_sem_id.semester_no - k['credits'] = p.credit - k['eletive_id'] = eletive_id - details.append(k) - - year = demo_date.year - month = demo_date.month - yearr = str(year) + "-" + str(year+1) - semflag = 0 - if(month >= 7): - semflag = 1 - else: - semflag = 2 - # TO DO Bdes - date = {'year': yearr, 'semflag': semflag} - course_list = Courses.objects.all() - semester_list = Semester.objects.all() - html = render_to_string('academic_procedures/studentSwayam.html', - {'details': details, - 'dict2': dict2, - 'course_list': course_list, - 'current_sem_swayam':current_sem_swayam, - 'roll_no':roll_no, - 'semester_list': semester_list, - # 'csrf_token' : csrf_token, - 'date': date}, request) - - maindict = {'html': html} - obj = json.dumps(maindict) - return HttpResponse(obj, content_type='application/json') - -def get_currently_registered_elective(student_id, semester_id): - registrations = course_registration.objects.filter(student_id=student_id, semester_id=semester_id) - courses = [] - for registration in registrations: - if registration.course_slot_id.type == "Optional Elective": - courses.append((registration.course_slot_id, registration.course_id, registration.id)) - return courses - - - -def swayam_replace(request): - if request.method == 'POST': - csrf_token = request.POST.get('csrfmiddlewaretoken', None) - - # print(f"---------------------------------{csrf_token}") - try: - - # print(f"djfhajjfsjfhajfhjdsfsdfj{request.POST}") - csrf_token = request.POST.get('csrfmiddlewaretoken', None) - - # print(f"---------------------------------{csrf_token}") - # Accessing individual values by key - user_value = request.POST['user'] - course_id_value = request.POST['course_id'] - - # print(user_value) 20BCS074 - # print(course_id_value) 8955 - - elective_to_delete = course_registration.objects.get(id=course_id_value) - sem = elective_to_delete.semester_id - # print(elective_to_delete) - # print(sem) cse ug curri v1 - - - swayam_course_id_value = request.POST['swayam_course_id'] - swayam_course_id_value_array = [int(id_str) for id_str in swayam_course_id_value.split(',')[:-1]] - # print(swayam_course_id_value_array) - # print(swayam_course_id_value) - - - - swayam_course_slot_id_value = request.POST['swayam_course_slot_id'] - swayam_course_slot_id_value_array = [int(slot_str) for slot_str in swayam_course_slot_id_value.split(',')[:-1]] - # print(swayam_course_slot_id_value_array) - # print(swayam_course_slot_id_value) - - - swayam_semester_id_value = request.POST['swayam_semester_id'] - swayam_semester_id_value_array = [int(semester_str) for semester_str in swayam_semester_id_value.split(',')[:-1]] - - - - - n = len(swayam_course_id_value_array) - # print(n) - # new_row_data = [] - for i in range(n): - course_id_model = Courses.objects.get(id=swayam_course_id_value_array[i]) - # print(course_id_model) - semester_id_model = Semester.objects.get(id=swayam_semester_id_value_array[i]) - student_id_model = Student.objects.get(id=user_value) - course_slot_id_model = CourseSlot.objects.get(id=swayam_course_slot_id_value_array[i]) - obj = course_registration( - course_id = course_id_model, - semester_id = semester_id_model, - student_id = student_id_model, - course_slot_id = course_slot_id_model, - working_year = datetime.datetime.now().year, - ) - obj.save() - - # for j in range(n): - # SwayamCourses.objects.filter(course_id = swayam_course_id_value_array[j], student_id=user_value).update(course_used = True) - - elective_to_delete.delete() - - - messages.success(request, "Your Courses have been replaced.") - return HttpResponseRedirect('/academic-procedures/main') - - - - - except Exception as e: - error_message = str(e) - print("Error:", error_message) - messages.error(request, f"Error in Registration: {error_message}") - return HttpResponseRedirect('/academic-procedures/main') - else: - return HttpResponseRedirect('/academic-procedures/main') - -def register_backlog_course(request): - if request.method == 'POST': - try: - current_user = request.user - current_user = ExtraInfo.objects.all().filter(user=request.user).first() - current_user = Student.objects.all().filter(id=current_user.id).first() - sem_id = Semester.objects.filter(id = request.POST.get('semester')).first() - course_id = Courses.objects.get(code = request.POST.get('courseCode') , version = request.POST.get('Version')) - course_slots = course_id.courseslots.all() - course_slot_id = '' - if course_slots: - course_slot_id = CourseSlot.objects.filter(id = course_slots[0].id).first() - if (sem_id.semester_no - course_slot_id.semester.semester_no)%2 != 0 : - return JsonResponse({'message':'Wait for Next Semester !'}, status=200) - # print('_____________________________________________________________________________________________' , course_id ,current_user , course_slot_id , sem_id) - try: - if course_registration.objects.filter(course_id=course_id, student_id=current_user , semester_id = sem_id).count() == 0: - p = course_registration( - course_id=course_id, - student_id=current_user, - course_slot_id=course_slot_id, - semester_id=sem_id, - working_year = datetime.datetime.now().year, - ) - p.save() - return JsonResponse({'message': 'Successfully Registered Backlog course' }, status=200) - else: - return JsonResponse({'message': 'Already Registered Backlog course' }, status=200) - except Exception as e: - print(str(e)) - return JsonResponse({'message': 'Error Registering course ' + str(e)}, status=500) - except Exception as e: - print(str(e)) - return JsonResponse({'message': 'Adding Backlog Failed ' +str(e)}, status=500) - - -def get_current_semester_swayam_course_slots(curr_sem_id): - courseslot_list = CourseSlot.objects.filter(semester = curr_sem_id, name__startswith='SW') - return courseslot_list - \ No newline at end of file diff --git a/FusionIIIT/applications/central_mess/admin.py b/FusionIIIT/applications/central_mess/admin.py index cbeb94d1c..cbb2fcb6c 100755 --- a/FusionIIIT/applications/central_mess/admin.py +++ b/FusionIIIT/applications/central_mess/admin.py @@ -2,8 +2,8 @@ from .models import (Feedback, Menu, Menu_change_request, Mess_meeting, Mess_minutes, Mess_reg, Messinfo, Monthly_bill, - Payments, Rebate, - Special_request, Vacation_food,MessBillBase, Registration_Request, Reg_records, Reg_main) + Nonveg_data, Nonveg_menu, Payments, Rebate, + Special_request, Vacation_food,MessBillBase) # Register your models here. @@ -45,7 +45,7 @@ class Mess_regAdmin(admin.ModelAdmin): ('start_reg', {'fields': ['start_reg']}), ('end_reg', {'fields': ['end_reg']}), ] - list_display = ('sem','start_reg', 'end_reg') + list_display = ('start_reg', 'end_reg') @@ -56,14 +56,14 @@ class Monthly_billAdmin(admin.ModelAdmin): ('month', {'fields': ['month']}), ('year', {'fields': ['year']}), ('amount', {'fields': ['amount']}), - # ('nonveg_total_bill', {'fields': ['nonveg_total_bill']}), + ('nonveg_total_bill', {'fields': ['nonveg_total_bill']}), ('rebate_count', {'fields': ['rebate_count']}), ('rebate_amount', {'fields': ['rebate_amount']}), ('total_bill', {'fields': ['total_bill']}), ] list_display = ('student_id', 'month', 'year', 'amount', - 'rebate_count', 'rebate_amount', 'total_bill') + 'nonveg_total_bill', 'rebate_count', 'rebate_amount', 'total_bill') @@ -71,9 +71,11 @@ class PaymentsAdmin(admin.ModelAdmin): model = Payments fieldsets = [ ('student_id', {'fields': ['student_id']}), + ('sem', {'fields': ['sem']}), + ('year', {'fields': ['year']}), ('amount_paid', {'fields': ['amount_paid']}), ] - list_display = ('student_id', 'amount_paid') + list_display = ('student_id', 'sem', 'year', 'amount_paid') class RebateAdmin(admin.ModelAdmin): @@ -104,26 +106,26 @@ class Vacation_foodAdmin(admin.ModelAdmin): -# class Nonveg_menuAdmin(admin.ModelAdmin): -# model = Nonveg_menu -# fieldsets = [ -# ('dish', {'fields': ['dish']}), -# ('price', {'fields': ['price']}), -# ('order_interval', {'fields': ['order_interval']}), -# ] -# list_display = ('dish', 'price', 'order_interval') +class Nonveg_menuAdmin(admin.ModelAdmin): + model = Nonveg_menu + fieldsets = [ + ('dish', {'fields': ['dish']}), + ('price', {'fields': ['price']}), + ('order_interval', {'fields': ['order_interval']}), + ] + list_display = ('dish', 'price', 'order_interval') -# class Nonveg_dataAdmin(admin.ModelAdmin): -# model = Nonveg_data -# fieldsets = [ -# ('student_id', {'fields': ['student_id']}), -# ('order_date', {'fields': ['order_date']}), -# ('dish', {'fields': ['dish']}), -# ('order_interval', {'fields': ['order_interval']}), -# ] -# list_display = ('student_id', 'order_date', 'dish', 'order_interval') +class Nonveg_dataAdmin(admin.ModelAdmin): + model = Nonveg_data + fieldsets = [ + ('student_id', {'fields': ['student_id']}), + ('order_date', {'fields': ['order_date']}), + ('dish', {'fields': ['dish']}), + ('order_interval', {'fields': ['order_interval']}), + ] + list_display = ('student_id', 'order_date', 'dish', 'order_interval') @@ -170,10 +172,9 @@ class FeedbackAdmin(admin.ModelAdmin): ('student_id', {'fields': ['student_id']}), ('fdate', {'fields': ['fdate']}), ('description', {'fields': ['description']}), - ('feedback_type', {'fields': ['feedback_type']}), - ('feedback_remark', {'fields':['feedback_remark']}) + ('feedback_type', {'fields': ['feedback_type']}) ] - list_display = ('student_id', 'fdate', 'description', 'feedback_type', 'feedback_remark') + list_display = ('student_id', 'fdate', 'description', 'feedback_type') class MessBillBaseAdmin(admin.ModelAdmin): @@ -183,19 +184,6 @@ class MessBillBaseAdmin(admin.ModelAdmin): ] list_display = ('bill_amount', 'timestamp') -class Reg_mainAdmin(admin.ModelAdmin): - model = Reg_main - list_display = ('student_id','program','current_mess_status','balance','mess_option') - -class Reg_recordsAdmin(admin.ModelAdmin): - model=Reg_records - list_display = ('student_id','start_date','end_date') - - -class Registration_RequestAdmin(admin.ModelAdmin): - model=Registration_Request - list_display = ('student_id','Txn_no','img','amount','status','registration_remark') - admin.site.register(Mess_minutes, Mess_minutesAdmin), admin.site.register(Messinfo, MessinfoAdmin), @@ -206,12 +194,9 @@ class Registration_RequestAdmin(admin.ModelAdmin): admin.site.register(Rebate, RebateAdmin), admin.site.register(Vacation_food, Vacation_foodAdmin), admin.site.register(Special_request, Special_requestAdmin), -# admin.site.register(Nonveg_menu, Nonveg_menuAdmin), -# admin.site.register(Nonveg_data, Nonveg_dataAdmin), +admin.site.register(Nonveg_menu, Nonveg_menuAdmin), +admin.site.register(Nonveg_data, Nonveg_dataAdmin), admin.site.register(Mess_meeting, Mess_meetingAdmin), admin.site.register(Feedback, FeedbackAdmin), admin.site.register(MessBillBase,MessBillBaseAdmin), admin.site.register(Menu_change_request, Menu_change_requestAdmin) -admin.site.register(Reg_main,Reg_mainAdmin) -admin.site.register(Reg_records,Reg_recordsAdmin) -admin.site.register(Registration_Request,Registration_RequestAdmin) diff --git a/FusionIIIT/applications/central_mess/api/serializers.py b/FusionIIIT/applications/central_mess/api/serializers.py deleted file mode 100644 index 1e077d6d4..000000000 --- a/FusionIIIT/applications/central_mess/api/serializers.py +++ /dev/null @@ -1,105 +0,0 @@ -from rest_framework import serializers -from applications.central_mess.models import * - -class MessinfoSerializer(serializers.ModelSerializer): - - class Meta: - model=Messinfo - fields=('__all__') - -class Mess_regSerializer(serializers.ModelSerializer): - class Meta: - model = Mess_reg - fields=('__all__') - -class MessBillBaseSerializer(serializers.ModelSerializer): - class Meta: - model = MessBillBase - fields=('__all__') - -class Monthly_billSerializer(serializers.ModelSerializer): - class Meta: - model=Monthly_bill - fields=('__all__') - -class PaymentsSerializer(serializers.ModelSerializer): - class Meta: - model=Payments - fields=('__all__') - -class MenuSerializer(serializers.ModelSerializer): - class Meta: - model=Menu - fields=('__all__') - -class RebateSerializer(serializers.ModelSerializer): - - class Meta: - model=Rebate - fields=('__all__') - -class Vacation_foodSerializer(serializers.ModelSerializer): - class Meta: - model = Vacation_food - fields=('__all__') - -# class Nonveg_menuSerializer(serializers.ModelSerializer): -# class Meta: -# model = Nonveg_menu -# fields=('__all__') - -# class Nonveg_dataSerializer(serializers.ModelSerializer): -# class Meta: -# model=Nonveg_data -# fields=('__all__') - -class Special_requestSerializer(serializers.ModelSerializer): - class Meta: - model=Special_request - fields=('__all__') - -class Mess_meetingSerializer(serializers.ModelSerializer): - class Meta: - model=Mess_meeting - fields=('__all__') - -class Mess_minutesSerializer(serializers.ModelSerializer): - class Meta: - model = Mess_minutes - fields=('__all__') - -class Menu_change_requestSerializer(serializers.ModelSerializer): - class Meta: - model = Menu_change_request - fields=('__all__') - -class FeedbackSerializer(serializers.ModelSerializer): - class Meta: - model=Feedback - fields=('__all__') - -class GetFilteredSerialzer(serializers.ModelSerializer): - first_name = serializers.CharField(source='student_id.id.user.first_name') - last_name = serializers.CharField(source='student_id.id.user.last_name') - class Meta: - model=Reg_main - fields=('__all__') - -class reg_recordSerialzer(serializers.ModelSerializer): - class Meta: - model = Reg_records - fields=('__all__') - -class RegistrationRequestSerializer(serializers.ModelSerializer): - class Meta: - model = Registration_Request - fields = ('__all__') - -class DeregistrationRequestSerializer(serializers.ModelSerializer): - class Meta: - model = Deregistration_Request - fields = ('__all__') -class UpdatePaymentRequestSerializer(serializers.ModelSerializer): - class Meta: - model = Update_Payment - fields = ('__all__') \ No newline at end of file diff --git a/FusionIIIT/applications/central_mess/api/urls.py b/FusionIIIT/applications/central_mess/api/urls.py deleted file mode 100644 index e0a86b9d7..000000000 --- a/FusionIIIT/applications/central_mess/api/urls.py +++ /dev/null @@ -1,31 +0,0 @@ -from django.conf.urls import url -from . import views -from .views import * - -urlpatterns = [ - url("feedbackApi", views.FeedbackApi.as_view(), name="feedbackApi"), - url("menuChangeRequestApi", views.Menu_change_requestApi.as_view(), name="menuChangeRequestApi"), - url("messMinutesApi", views.Mess_minutesApi.as_view(), name="messMinutesApi"), - # url("nonvegDataApi", views.Nonveg_dataApi.as_view(), name="nonvegDataApi"), - url("specialRequestApi", views.Special_requestApi.as_view(), name="specialRequestApi"), - url("messMeetingApi", views.Mess_meetingApi.as_view(), name="messMeetingApi"), - # url("nonvegMenuApi", views.Nonveg_menuApi.as_view(), name="nonvegMenuApi"), - url("vacationFoodApi", views.Vacation_foodApi.as_view(), name="vacationFoodApi"), - url("messInfoApi", views.MessinfoApi.as_view(), name="messInfoApi"), - url("rebateApi", views.RebateApi.as_view(), name="rebateApi"), - url("menuApi", views.MenuApi.as_view(), name="menuApi"), - url("paymentsApi", views.PaymentsApi.as_view(), name="paymentsApi"), - url("monthlyBillApi", views.Monthly_billApi.as_view(), name="monthlyBillApi"), - url("messBillBaseApi", views.MessBillBaseApi.as_view(), name="messBillBaseApi"), - url("messRegApi", views.Mess_regApi.as_view(), name="messRegApi"), - url("get_mess_students", views.Get_Filtered_Students.as_view(), name="get_mess_students"), - url("get_reg_records",views.Get_Reg_Records.as_view(),name="reg_record_API"), - # url("billDashboard", views.Bill_dashboard.as_view(), name="billDashboard"), - url("get_student_bill",views.Get_Student_bill.as_view(),name="student_bill_API"), - url("get_student_payment",views.Get_Student_Payments.as_view(),name="student_payment_API"), - url("get_student_all_details",views.Get_Student_Details.as_view(),name="get_student_details_API"), - url('registrationRequestApi', views.RegistrationRequestApi.as_view(), name='registrationRequestApi'), - url('deRegistrationRequestApi', views.DeregistrationRequestApi.as_view(), name='deRegistrationRequestApi'), - url('deRegistrationApi', views.DeregistrationApi.as_view(), name='deRegistrationApi'), - url('updatePaymentRequestApi', views.UpdatePaymentRequestApi.as_view(), name='updatePaymentRequestApi'), -] \ No newline at end of file diff --git a/FusionIIIT/applications/central_mess/api/views.py b/FusionIIIT/applications/central_mess/api/views.py deleted file mode 100644 index 4a6076ec4..000000000 --- a/FusionIIIT/applications/central_mess/api/views.py +++ /dev/null @@ -1,795 +0,0 @@ - #APIs -from django.db.models import F -from rest_framework.views import APIView -from rest_framework.response import Response -from .serializers import * -from django.shortcuts import get_object_or_404 -from applications.central_mess.models import * -from django.contrib.auth.models import User -from applications.globals.models import ExtraInfo, HoldsDesignation, Designation -from django.http import JsonResponse - -class FeedbackApi(APIView): - - def get(self, request): - feedback_obj = Feedback.objects.all(); - serialized_obj = FeedbackSerializer(feedback_obj, many=True) - return Response({'status':200, 'payload':serialized_obj.data}) - - def post(self, request): - data = request.data - - mess = data['mess'] - feedback_type = data['feedback_type'] - description = data['description'] - username = get_object_or_404(User,username=request.user.username) - idd = ExtraInfo.objects.get(user=username) - student = Student.objects.get(id=idd.id) - obj = Feedback( - student_id = student, - mess =mess, - feedback_type=feedback_type, - description=description - ) - obj.save() - return Response({'status':200}) - - def put(self, request): - data = request.data - - print(data) - - student_id = data['student_id'] - mess = data['mess'] - feedback_type = data['feedback_type'] - description = data['description'] - fdate = data['fdate'] - new_remark = data['feedback_remark'] - - # username = get_object_or_404(User,username=request.user.username) - # idd = ExtraInfo.objects.get(user=username) - # student = Student.objects.get(id=idd.id) - - feedback_request = get_object_or_404(Feedback, - student_id = student_id, - mess = mess, - feedback_type = feedback_type, - description = description, - fdate = fdate, - ) - feedback_request.feedback_remark = new_remark - feedback_request.save() - - return Response({'status':200}) - - -class MessinfoApi(APIView): - def get(self, request): - messinfo_obj = Messinfo.objects.all(); - serialized_obj = MessinfoSerializer(messinfo_obj, many=True) - return Response({'status':200, 'payload':serialized_obj.data}) - - def post(self, request): - data = request.data - - mess_option = data['mess_option'] - - username = get_object_or_404(User,username=request.user.username) - idd = ExtraInfo.objects.get(user=username) - student = Student.objects.get(id=idd.id) - obj = Messinfo( - student_id = student, - mess_option =mess_option, - ) - obj.save() - return Response({'status':200}) - -class Mess_regApi(APIView): - def get(self, request): - mess_reg_obj = Mess_reg.objects.all(); - serialized_obj = Mess_regSerializer(mess_reg_obj, many=True) - return Response({'status':200, 'payload':serialized_obj.data}) - - def post(self, request): - data = request.data - - sem = data['sem'] - start_reg = data['start_reg'] - end_reg= data['end_reg'] - - obj = Mess_reg( - sem = sem, - start_reg = start_reg, - end_reg = end_reg - ) - obj.save() - return Response({'status':200}) - -class MessBillBaseApi(APIView): - def get(self, request): - messBillBase_obj = MessBillBase.objects.all(); - serialized_obj = MessBillBaseSerializer(messBillBase_obj, many=True) - return Response({'status':200, 'payload':serialized_obj.data}) - - def post(self, request): - data = request.data - - bill_amount = data['bill_amount'] - # timestamp = data['timestamp'] - - obj = MessBillBase( - bill_amount = bill_amount, - # timestamp = timestamp, - ) - obj.save() - return Response({'status':200}) - -class Monthly_billApi(APIView): - def get(self, request): - monthly_bill_obj = Monthly_bill.objects.all(); - serialized_obj = Monthly_billSerializer(monthly_bill_obj, many=True) - return Response({'status':200, 'payload':serialized_obj.data}) - - def post(self, request): - data = request.data - - student_id = data['student_id'] - month = data['month'] - year = data['year'] - amount = data['amount'] - rebate_count = data['rebate_count'] - rebate_amount = data['rebate_amount'] - total_bill = data['amount']-(data['rebate_count']*data['rebate_amount']) - paid = data['paid'] - - username = get_object_or_404(User,username=student_id) - idd = ExtraInfo.objects.get(user=username) - student = Student.objects.get(id=idd.id) - - try: - reg_main = Monthly_bill.objects.get(student_id=student, year = year, month = month) - reg_main.amount = amount - reg_main.rebate_count = rebate_count - reg_main.rebate_amount = rebate_amount - reg_main.total_bill = total_bill - except Monthly_bill.DoesNotExist: - reg_main = Monthly_bill.objects.create( - student_id=student, - month = month, - year = year, - amount = amount, - rebate_amount = rebate_amount, - rebate_count = rebate_count, - total_bill = total_bill, - paid = paid - ) - reg_main.save() - return Response({'status':200}) - -class PaymentsApi(APIView): - def get(self, request): - payments_obj = Payments.objects.all(); - serialized_obj = PaymentsSerializer(payments_obj, many=True) - return Response({'status':200, 'payload':serialized_obj.data}) - - def post(self, request): - data = request.data - - # sem = data['sem'] - # year = data['year'] - amount_paid = data['amount_paid'] - - - username = get_object_or_404(User,username=request.user.username) - idd = ExtraInfo.objects.get(user=username) - student = Student.objects.get(id=idd.id) - - - obj = Payments( - student_id = student, - # sem = sem, - # year = year, - amount_paid = amount_paid, - ) - obj.save() - return Response({'status':200}) -class MenuApi(APIView): - def get(self, request): - menu_obj = Menu.objects.all(); - serialized_obj = MenuSerializer(menu_obj, many=True) - return Response({'status':200, 'payload':serialized_obj.data}) - - def post(self, request): - data = request.data - - mess_option = data['mess_option'] - meal_time = data['meal_time'] - dish = data['dish'] - - - obj = Menu( - mess_option = mess_option, - meal_time = meal_time, - dish = dish, - ) - obj.save() - return Response({'status':200}) -class RebateApi(APIView): - def get(self, request): - rebate_obj = Rebate.objects.all(); - serialized_obj = RebateSerializer(rebate_obj, many=True) - return Response({'status':200, 'payload':serialized_obj.data}) - - def post(self, request): - data = request.data - - # student_id = data['mess_option'] - start_date = data['start_date'] - end_date = data['end_date'] - purpose = data['purpose'] - status = data['status'] - app_date = data['app_date'] - leave_type = data['leave_type'] - - username = get_object_or_404(User,username=request.user.username) - idd = ExtraInfo.objects.get(user=username) - student = Student.objects.get(id=idd.id) - - - obj = Rebate( - student_id = student, - leave_type = leave_type, - app_date = app_date, - status = status, - purpose = purpose, - end_date= end_date, - start_date = start_date - ) - obj.save() - return Response({'status':200}) - - def put(self, request): - data = request.data - - student_id = data['student_id'] - start_date = data['start_date'] - end_date = data['end_date'] - purpose = data['purpose'] - new_status = data['status'] - app_date = data['app_date'] - leave_type = data['leave_type'] - rebate_remark = data['rebate_remark'] - - # username = get_object_or_404(User,username=student_id) - # idd = ExtraInfo.objects.get(user=username) - # student = Student.objects.get(id=idd.id) - - rebate_request = get_object_or_404(Rebate, student_id=student_id, end_date=end_date, start_date=start_date, app_date=app_date, purpose=purpose, leave_type=leave_type) - - # Update the status - rebate_request.status = new_status - rebate_request.rebate_remark = rebate_remark - rebate_request.save() - - return Response({'status': 200}) - -class Vacation_foodApi(APIView): - def get(self, request): - vacation_food_obj = Vacation_food.objects.all(); - serialized_obj = Vacation_foodSerializer(vacation_food_obj, many=True) - return Response({'status':200, 'payload':serialized_obj.data}) - - def post(self, request): - data = request.data - - start_date = data['start_date'] - end_date = data['end_date'] - purpose = data['purpose'] - status = data['status'] - app_date = data['app_date'] - - - username = get_object_or_404(User,username=request.user.username) - idd = ExtraInfo.objects.get(user=username) - student = Student.objects.get(id=idd.id) - - - obj = Vacation_food( - student_id = student, - app_date = app_date, - status = status, - purpose = purpose, - end_date= end_date, - start_date = start_date - ) - obj.save() - return Response({'status':200}) - - def put(self, request): - print(request.data) - data = request.data - - student_id = data['student_id'] - start_date = data['start_date'] - end_date = data['end_date'] - purpose = data['purpose'] - new_status = data['status'] - app_date = data['app_date'] - - - # username = get_object_or_404(User,username=request.user.username) - # idd = ExtraInfo.objects.get(user=username) - # student = Student.objects.get(id=idd.id) - - try: - vacation_food_request = get_object_or_404(Vacation_food, - student_id = student_id, - app_date = app_date, - purpose = purpose, - end_date= end_date, - start_date = start_date - ) - vacation_food_request.status = new_status - vacation_food_request.save() - return Response({'status':200}) - except: - vacation_food_request = Vacation_food.objects.filter(student_id = student_id, - app_date = app_date, - purpose = purpose, - end_date= end_date, - start_date = start_date - ).latest('app_date') - vacation_food_request.status = new_status - vacation_food_request.save() - return Response({'status':200}) - -class Nonveg_menuApi(APIView): - def get(self, request): - nonveg_menu_obj = Nonveg_menu.objects.all(); - serialized_obj = Nonveg_menuSerializer(nonveg_menu_obj, many=True) - return Response({'status':200, 'payload':serialized_obj.data}) - - def post(self, request): - data = request.data - - dish= data['dish'] - price = data['price'] - order_interval = data['order_interval'] - - - obj = Nonveg_menu( - dish = dish, - price = price, - order_interval = order_interval, - ) - obj.save() - return Response({'status':200}) - -class Nonveg_dataApi(APIView): - def get(self, request): - nonveg_data_obj = Nonveg_data.objects.all(); - serialized_obj = Nonveg_dataSerializer(nonveg_data_obj, many=True) - return Response({'status':200, 'payload':serialized_obj.data}) - - def post(self, request): - data = request.data - - dish= data['dish'] - order_date = data['order_date'] - app_date = data['app_date'] - order_interval = data['order_interval'] - - username = get_object_or_404(User,username=request.user.username) - idd = ExtraInfo.objects.get(user=username) - student = Student.objects.get(id=idd.id) - - dish_obj = Nonveg_menu.objects.get(dish=dish) - - obj = Nonveg_data( - student_id = student, - order_date = order_date, - app_date = app_date, - dish = dish_obj, - order_interval = order_interval, - ) - obj.save() - return Response({'status':200}) - -class Special_requestApi(APIView): - def get(self, request): - special_request_obj = Special_request.objects.all(); - serialized_obj = Special_requestSerializer(special_request_obj, many=True) - return Response({'status':200, 'payload':serialized_obj.data}) - - - def post(self, request): - data = request.data - - - start_date = data['start_date'] - end_date = data['end_date'] - status = data['status'] - app_date = data['app_date'] - request_= data['request'] - item1 = data['item1'] - item2 = data['item2'] - - - - username = get_object_or_404(User,username=request.user.username) - idd = ExtraInfo.objects.get(user=username) - student = Student.objects.get(id=idd.id) - - obj = Special_request( - student_id = student, - app_date = app_date, - status = status, - item1 = item1, - item2 = item2, - end_date= end_date, - start_date = start_date, - request = request_ - ) - obj.save() - return Response({'status':200}) - - def put(self, request): - print(request.data) - data = request.data - student_id = data['student_id'] - start_date = data['start_date'] - end_date = data['end_date'] - app_date = data['app_date'] - request_= data['request'] - item1 = data['item1'] - item2 = data['item2'] - new_status = data['status'] - - # Fetch the Special_request object you want to update - # username = get_object_or_404(User, username=request.user.username) - # idd = ExtraInfo.objects.get(user=username) - # student = Student.objects.get(id=idd.id) - - special_request = get_object_or_404(Special_request, student_id=student_id, app_date=app_date, item1=item1, item2=item2, end_date=end_date, start_date=start_date, request=request_) - - # Update the status - special_request.status = new_status - special_request.save() - - return Response({'status': 200}) - -class Mess_meetingApi(APIView): - def get(self, request): - mess_meeting_obj = Mess_meeting.objects.all(); - serialized_obj = Mess_meetingSerializer(mess_meeting_obj, many=True) - return Response({'status':200, 'payload':serialized_obj.data}) - - def post(self, request): - data = request.data - meet_date = data['meet_date'] - agenda = data['agenda'] - venue = data['venue'] - meeting_time = data['meeting_time'] - - obj = Mess_meeting( - meet_date = meet_date, - meeting_time = meeting_time, - agenda = agenda, - venue = venue, - ) - obj.save() - return Response({'status':200}) - -class Mess_minutesApi(APIView): - def get(self, request): - mess_minutes_obj = Mess_minutes.objects.all(); - serialized_obj = Mess_minutesSerializer(mess_minutes_obj, many=True) - return Response({'status':200, 'payload':serialized_obj.data}) - - def post(self, request): - data = request.data - - # meeting_date = data['meeting_date'] - mess_minutes = data['mess_minutes'] - meeting_date_obj = Mess_meeting.objects.get(meet_date=meeting_date) - - obj = Mess_minutes( - # meeting_date = meeting_date_obj, - mess_minutes = mess_minutes, - ) - obj.save() - return Response({'status':200}) - -class Menu_change_requestApi(APIView): - def get(self, request): - menu_change_request_obj = Menu_change_request.objects.all(); - serialized_obj = Menu_change_requestSerializer(menu_change_request_obj, many=True) - return Response({'status':200, 'payload':serialized_obj.data}) - - def post(self, request): - data = request.data - dish = data['dish'] - reason = data['reason'] - status = data['status'] - app_date = data['app_date'] - request_ = data['request'] - - - dish_obj = Menu.objects.get(dish=dish) - username = get_object_or_404(User,username=request.user.username) - idd = ExtraInfo.objects.get(user=username) - student = Student.objects.get(id=idd.id) - - obj = Menu_change_request( - student_id = student, - app_date = app_date, - status = status, - reason = reason, - request = request_, - dish = dish_obj - ) - obj.save() - return Response({'status':200}) - -class Get_Filtered_Students(APIView): - - def post(self,request): - type = request.data['type'] - if(type=='filter'): - reg_main = Reg_main.objects.select_related('student_id','student_id__id','student_id__id__user','student_id__id__department').all() - status=request.data['status'] - program=request.data['program'] - mess_option=request.data['mess_option'] - - if(status!='all'): - - reg_main=reg_main.filter(current_mess_status=status) - - if(program!='all'): - reg_main=reg_main.filter(program=program) - - if(mess_option!='all'): - - reg_main=reg_main.filter(mess_option=mess_option) - - serialized_obj = GetFilteredSerialzer(reg_main,many=True) - return Response({'payload':serialized_obj.data}) - - elif(type=='search'): - student = request.data['student_id'] - student = str(student).upper() - try: - reg_main = Reg_main.objects.select_related('student_id','student_id__id','student_id__id__user','student_id__id__department').get(student_id=student) - serialized_obj = GetFilteredSerialzer(reg_main) - return Response({'payload':serialized_obj.data}) - except: - response = JsonResponse({"error": "student does not exist"}) - response.status_code = 404 - return response - -class Get_Reg_Records(APIView): - - def post(self,request): - student = request.data['student_id'] - reg_record = Reg_records.objects.filter(student_id=student) - - serialized_obj = reg_recordSerialzer(reg_record,many=True) - return Response({'payload':serialized_obj.data}) - - -class Get_Student_bill(APIView): - - def post(self,request): - student = request.data['student_id'] - bill_details = Monthly_bill.objects.filter(student_id=student) - - serialized_obj = Monthly_billSerializer(bill_details,many=True) - return Response({'payload':serialized_obj.data}) - - -class Get_Student_Payments(APIView): - - def post(self,request): - student = request.data['student_id'] - payment_details = Payments.objects.filter(student_id=student) - - serialized_obj = PaymentsSerializer(payment_details,many=True) - return Response({'payload':serialized_obj.data}) - -class Get_Student_Details(APIView): - - def post(self,request): - student = request.data['student_id'] - bill_details = Monthly_bill.objects.filter(student_id=student) - payment_details = Payments.objects.filter(student_id=student) - reg_record = Reg_records.objects.filter(student_id=student) - payment_serialized_obj = PaymentsSerializer(payment_details,many=True) - bill_serialized_obj = Monthly_billSerializer(bill_details,many=True) - reg_record_serialized_obj = reg_recordSerialzer(reg_record,many=True) - reg_main = Reg_main.objects.select_related('student_id','student_id__id','student_id__id__user','student_id__id__department').get(student_id=student) - serialized_obj = GetFilteredSerialzer(reg_main) - data={'payment':payment_serialized_obj.data,'bill':bill_serialized_obj.data,'reg_records':reg_record_serialized_obj.data,'student_details':serialized_obj.data} - return Response({'payload':data}) - -class RegistrationRequestApi(APIView): - def get(self, request): - registration_requests = Registration_Request.objects.all() - serializer = RegistrationRequestSerializer(registration_requests, many=True) - return Response({'status': 200, 'payload': serializer.data}) - - def post(self, request): - serializer = RegistrationRequestSerializer(data=request.data) - if serializer.is_valid(): - serializer.save() - return Response({'status': 200}) - return Response(serializer.errors, status=400) - - def put(self, request): - try: - data = request.data - print(data) - student_id = data['student_id'] - start_date = data['start_date'] - payment_date = data['payment_date'] - amount = data['amount'] - Txn_no = data['Txn_no'] - img = data['img'] - new_status = data['status'] - new_remark = data['registration_remark'] - mess_option = data['mess_option'] - - username = get_object_or_404(User, username=student_id) - idd = ExtraInfo.objects.get(user=username) - student = Student.objects.get(id=idd.id) - - registration_request = get_object_or_404(Registration_Request, student_id = student_id, start_date = start_date, payment_date = payment_date, amount = amount, Txn_no = Txn_no) - - registration_request.status = new_status - registration_request.registration_remark = new_remark - registration_request.save() - - if (new_status == 'accept'): - new_payment_record = Payments(student_id = student, amount_paid = amount, payment_date=payment_date, payment_month=current_month(), payment_year=current_year()) - new_payment_record.save() - - try: - reg_main = Reg_main.objects.get(student_id=student) - reg_main.current_mess_status = "Registered" - reg_main.balance = F('balance') + amount - reg_main.mess_option = mess_option - except Reg_main.DoesNotExist: - reg_main = Reg_main.objects.create( - student_id=student, - program=student.programme, - current_mess_status="Registered", - balance=amount, - mess_option=mess_option - ) - reg_main.save() - - new_reg_record = Reg_records(student_id=student, start_date=start_date, end_date=None) - new_reg_record.save() - - - return Response({'status': 200}) - except Exception as e: - print({'error': str(e)}) - return Response({'error': str(e)}, status=400) - -class DeregistrationRequestApi(APIView): - def get(self, request): - deregistration_requests = Deregistration_Request.objects.all() - serializer = DeregistrationRequestSerializer(deregistration_requests, many=True) - return Response({'status': 200, 'payload': serializer.data}) - - def post(self, request): - serializer = DeregistrationRequestSerializer(data=request.data) - if serializer.is_valid(): - serializer.save() - return Response({'status': 200}) - return Response(serializer.errors, status=400) - - def put(self, request): - try: - data = request.data - print(data) - student_id = data['student_id'] - end_date = data['end_date'] - new_status = data['status'] - new_remark = data['deregistration_remark'] - - username = get_object_or_404(User, username=student_id) - idd = ExtraInfo.objects.get(user=username) - student = Student.objects.get(id=idd.id) - - deregistration_request = get_object_or_404(Deregistration_Request, student_id = student_id, end_date = end_date) - - deregistration_request.status = new_status - deregistration_request.deregistration_remark = new_remark - deregistration_request.save() - - if (new_status == 'accept'): - - reg_main = Reg_main.objects.get(student_id=student) - reg_main.current_mess_status = "Deregistered" - reg_main.save() - - reg_record = Reg_records.objects.filter(student_id=student).latest('start_date') - reg_record.end_date = end_date - reg_record.save() - return Response({'status': 200}) - except Exception as e: - print({'error': str(e)}) - return Response({'error': str(e)}, status=400) - -class DeregistrationApi(APIView): - def post(self, request): - try: - data = request.data - print(data) - student_id = data['student_id'] - end_date = data['end_date'] - - username = get_object_or_404(User, username=student_id) - idd = ExtraInfo.objects.get(user=username) - student = Student.objects.get(id=idd.id) - - reg_main = Reg_main.objects.get(student_id=student) - reg_main.current_mess_status = "Deregistered" - reg_main.save() - - reg_record = Reg_records.objects.filter(student_id=student).latest('start_date') - reg_record.end_date = end_date - reg_record.save() - return Response({'status': 200}) - except Exception as e: - print({'error': str(e)}) - return Response({'error': str(e)}, status=400) - -class UpdatePaymentRequestApi(APIView): - def get(self, request): - update_payment_requests = Update_Payment.objects.all() - serializer = UpdatePaymentRequestSerializer(update_payment_requests, many=True) - return Response({'status': 200, 'payload': serializer.data}) - - def post(self, request): - serializer = UpdatePaymentRequestSerializer(data=request.data) - if serializer.is_valid(): - serializer.save() - return Response({'status': 200}) - return Response(serializer.errors, status=400) - - def put(self, request): - try: - data = request.data - print(data) - student_id = data['student_id'] - payment_date = data['payment_date'] - amount = data['amount'] - Txn_no = data['Txn_no'] - img = data['img'] - new_status = data['status'] - new_remark = data['update_payment_remark'] - - username = get_object_or_404(User, username=student_id) - idd = ExtraInfo.objects.get(user=username) - student = Student.objects.get(id=idd.id) - - UpdatePayment_request = get_object_or_404(Update_Payment, student_id = student_id, payment_date = payment_date, amount = amount, Txn_no = Txn_no) - - UpdatePayment_request.status = new_status - UpdatePayment_request.update_payment_remark = new_remark - UpdatePayment_request.save() - - if (new_status == 'accept'): - new_payment_record = Payments(student_id = student, amount_paid = amount, payment_date=payment_date, payment_month=current_month(), payment_year=current_year()) - new_payment_record.save() - - reg_main = Reg_main.objects.get(student_id=student) - reg_main.balance = F('balance') + amount - reg_main.save() - - return Response({'status': 200}) - except Exception as e: - print({'error': str(e)}) - return Response({'error': str(e)}, status=400) \ No newline at end of file diff --git a/FusionIIIT/applications/central_mess/forms.py b/FusionIIIT/applications/central_mess/forms.py index 9325df23f..ac52d5d3a 100644 --- a/FusionIIIT/applications/central_mess/forms.py +++ b/FusionIIIT/applications/central_mess/forms.py @@ -1,8 +1,6 @@ from django import forms from .models import Mess_minutes -from .models import Registration_Request -from .models import Update_Payment class MinuteForm(forms.ModelForm): @@ -15,47 +13,4 @@ class MessInfoForm(forms.Form, ): ('mess1', 'Veg Mess'), ('mess2', 'Non Veg Mess'), ] - mess_option = forms.CharField(label='Mess Option', widget=forms.Select( - choices=MESS_CHOICES, attrs={'style': 'border-radius:1rem;padding:7px;'})) - - - -class RegistrationRequest(forms.ModelForm): - - class Meta: - model = Registration_Request - fields = ('Txn_no','amount','img','start_date','payment_date') - - widgets={ - 'Txn_no':forms.TextInput(attrs={'class':'ui big input','style':'border-radius:4px', 'initial':'590'}), - 'amount':forms.TextInput(attrs={'class':'ui big input'}), - 'img':forms.FileInput(attrs={'class':'ui big input'}), - 'start_date':forms.widgets.DateInput(attrs={'type':'date'}), - 'payment_date':forms.widgets.DateInput(attrs={'type':'date'}), - } - - -class UpdatePaymentRequest(forms.ModelForm): - - class Meta: - model = Update_Payment - fields = ('Txn_no','amount','img','payment_date') - - widgets={ - 'Txn_no':forms.TextInput(attrs={'class':'ui big input','style':'border-radius:4px', 'initial':'590'}), - 'amount':forms.TextInput(attrs={'class':'ui big input'}), - 'img':forms.FileInput(attrs={'class':'ui big input'}), - 'payment_date':forms.widgets.DateInput(attrs={'type':'date'}), - } - -class UpdateBalanceRequest(forms.ModelForm): - - class Meta: - model = Registration_Request - fields = ('Txn_no','amount','img') - - widgets={ - 'Txn_no':forms.TextInput(attrs={'class':'ui big input','style':'border-radius:4px'}), - 'amount':forms.TextInput(attrs={'class':'ui big input'}), - 'img':forms.FileInput(attrs={'class':'ui big input'}), - } \ No newline at end of file + mess_option = forms.CharField(label='Mess Option', widget=forms.Select(choices=MESS_CHOICES)) \ No newline at end of file diff --git a/FusionIIIT/applications/central_mess/handlers.py b/FusionIIIT/applications/central_mess/handlers.py index 882cbe4e4..e469e1286 100644 --- a/FusionIIIT/applications/central_mess/handlers.py +++ b/FusionIIIT/applications/central_mess/handlers.py @@ -15,8 +15,9 @@ from applications.academic_information.models import Student from applications.globals.models import ExtraInfo, HoldsDesignation, Designation from .models import (Feedback, Menu, Menu_change_request, Mess_meeting, - Mess_minutes, Mess_reg, Messinfo, Monthly_bill, Update_Payment, - Payments, Rebate,Special_request, Vacation_food, MessBillBase,Registration_Request, Reg_main, Reg_records ,Deregistration_Request, Semdates) + Mess_minutes, Mess_reg, Messinfo, Monthly_bill, + Nonveg_data, Nonveg_menu, Payments, Rebate, + Special_request, Vacation_food, MessBillBase) from notification.views import central_mess_notif @@ -24,49 +25,40 @@ year_g = today_g.year tomorrow_g = today_g + timedelta(days=1) first_day_of_this_month = date.today().replace(day=1) -this_month = first_day_of_this_month.strftime('%B') -this_year = first_day_of_this_month.year last_day_prev_month = first_day_of_this_month - timedelta(days=1) previous_month = last_day_prev_month.strftime('%B') previous_month_year = last_day_prev_month.year first_day_of_next_month = (date.today().replace(day=28) + timedelta(days=4)).replace(day=1) last_day_of_this_month = first_day_of_next_month - timedelta(days=1) next_month = first_day_of_next_month.month -first_day_prev_month = last_day_prev_month.replace(day=1) - -def current_month(): - return date.today().strftime("%B") - - -def current_year(): - return date.today().strftime("%Y") - -# def add_nonveg_order(request, student): -# """ -# This function is to place non veg orders -# :param request: -# user: Current user -# order_interval: Time of the day for which order is placed eg breakfast/lunch/dinner -# :param student: student placing the order -# :variables: -# extra_info: Extra information about the current user. From model ExtraInfo -# student: Student information about the current user -# student_mess: Mess choices of the student -# dish_request: Predefined dish available -# nonveg_object: Object of Nonveg_data -# :return: -# """ -# try: -# dish_request = Nonveg_menu.objects.get(dish=request.POST.get("dish")) -# order_interval = request.POST.get("interval") -# order_date = tomorrow_g -# nonveg_object = Nonveg_data(student_id=student, order_date=order_date, -# order_interval=order_interval, dish=dish_request) -# nonveg_object.save() -# # messages.success(request, 'Your request is forwarded !!', extra_tags='successmsg') - -# except ObjectDoesNotExist: -# return HttpResponse("Seems like object does not exist") + + +def add_nonveg_order(request, student): + """ + This function is to place non veg orders + :param request: + user: Current user + order_interval: Time of the day for which order is placed eg breakfast/lunch/dinner + :param student: student placing the order + :variables: + extra_info: Extra information about the current user. From model ExtraInfo + student: Student information about the current user + student_mess: Mess choices of the student + dish_request: Predefined dish available + nonveg_object: Object of Nonveg_data + :return: + """ + try: + dish_request = Nonveg_menu.objects.get(dish=request.POST.get("dish")) + order_interval = request.POST.get("interval") + order_date = tomorrow_g + nonveg_object = Nonveg_data(student_id=student, order_date=order_date, + order_interval=order_interval, dish=dish_request) + nonveg_object.save() + # messages.success(request, 'Your request is forwarded !!', extra_tags='successmsg') + + except ObjectDoesNotExist: + return HttpResponse("Seems like object does not exist") def add_mess_feedback(request, student): @@ -84,7 +76,7 @@ def add_mess_feedback(request, student): data: to record success or any errors """ date_today = datetime.now().date() - mess_optn = Reg_main.objects.get(student_id=student) + mess_optn = Messinfo.objects.get(student_id=student) description = request.POST.get('description') feedback_type = request.POST.get('feedback_type') feedback_object = Feedback(student_id=student, fdate=date_today, @@ -125,7 +117,7 @@ def add_vacation_food_request(request, student): } return data - vacation_check = Vacation_food.objects.filter(student_id=student).prefetch_related('student_id','student_id__id','student_id__id__user','student_id__id__department') + vacation_check = Vacation_food.objects.filter(student_id =student) date_format = "%Y-%m-%d" b = datetime.strptime(str(start_date), date_format) @@ -163,10 +155,11 @@ def add_menu_change_request(request, student): :return: """ try: - - dishID =request.POST['dish']; - dish=Menu.objects.get(id=dishID) + print("inside add_menu") + dish = Menu.objects.get(dish=request.POST.get("dish")) + print(dish, "-------------------------------------------------------------------") new_dish = request.POST.get("newdish") + print(new_dish) reason = request.POST.get("reason") # menu_object = Menu_change_request(dish=dish, request=new_dish, reason=reason) menu_object = Menu_change_request(dish=dish, student_id=student, request=new_dish, reason=reason) @@ -179,6 +172,7 @@ def add_menu_change_request(request, student): data = { 'status': 0 } + print(e) return data @@ -198,15 +192,16 @@ def handle_menu_change_response(request): ap_id = request.POST.get('idm') user = request.user stat = request.POST['status'] - application = Menu_change_request.objects.get(id=ap_id) + application = Menu_change_request.objects.get(pk=ap_id) # student = application.student_id # receiver = User.objects.get(username=student) if stat == '2': application.status = 2 - obj = Menu.objects.get(Q(meal_time=application.dish.meal_time) & Q(mess_option=application.dish.mess_option)) + meal = application.dish + obj = Menu.objects.get(dish=meal.dish) obj.dish = application.request obj.save() - data = { + data = { 'status': '2', } # central_mess_notif(user, receiver, 'menu_change_accepted') @@ -339,9 +334,9 @@ def add_leave_request(request, student): } return data - rebate_check = Rebate.objects.select_related('student_id','student_id__id','student_id__id__user','student_id__id__department').filter(student_id=student, status__in=['1', '2']) - - + rebates = Rebate.objects.filter(student_id=student) + rebate_check = rebates.filter(Q(status='1') | Q(status='2')) + for r in rebate_check: a = datetime.strptime(str(r.start_date), date_format) c = datetime.strptime(str(r.end_date), date_format) @@ -378,8 +373,8 @@ def add_mess_meeting_invitation(request): venue = request.POST['venue'] agenda = request.POST['agenda'] time = request.POST['time'] - members_mess = HoldsDesignation.objects.select_related().filter(Q(designation__name__contains='mess_convener') - | Q(designation__name__contains='mess_committee')|Q(designation__name='mess_manager') + members_mess = HoldsDesignation.objects.filter(Q(designation__name__contains='mess_convener') + | Q(designation__name__contains='mess_committee')|Q(designation__name='mess_manager') | Q(designation__name='mess_warden')) date_today = str(today_g.date()) if date <= date_today: @@ -403,49 +398,7 @@ def add_mess_meeting_invitation(request): } return data -def rebateCountFn(start_date, end_date, student_id): - ''' - This function is used to store the rebate_count in generte bill table and in what month that rebate has been issued. - ''' - start_date_month = start_date.strftime('%B') - start_date_year = start_date.year - end_date_month = end_date.strftime('%B') - date_format = "%Y-%m-%d" - begin_day = datetime.strptime(str(start_date), date_format) - end_day = datetime.strptime(str(end_date), date_format) - rebate_count_days = 0 - rebate_count_days_next_month = 0 - if start_date_month != end_date_month: - last_day_day = int(last_day_of_this_month.day) - begin_day_day = int(begin_day.day) - end_day_day = int(end_day.day) - first_day_day = int(first_day_of_next_month.day) - rebate_count_days = abs(last_day_day-begin_day_day) + 1 - rebate_count_days_next_month = abs(end_day_day-first_day_day)+1 - else: - rebate_count_days = abs((end_day - begin_day).days) + 1 - - #### Storing the rebate count days into the monthly bill table so it can be used while bill generation. - - try: - existing_student = Monthly_bill.objects.get(student_id=student_id, month=start_date_month, year=start_date_year) - new_rebate_count = existing_student.rebate_count + rebate_count_days - existing_student.rebate_count = new_rebate_count - existing_student.save() - if(rebate_count_days_next_month != 0): - new_student = Monthly_bill.objects.create(student_id=student_id, month=end_date_month, year=start_date_year, rebate_count=rebate_count_days_next_month) - new_student.save() - except: - new_student = Monthly_bill.objects.create(student_id=student_id, month=start_date_month, year=start_date_year, rebate_count=rebate_count_days) - new_student.save() - if(rebate_count_days_next_month != 0): - new_student = Monthly_bill.objects.create(student_id=student_id, month=end_date_month, year=start_date_year, rebate_count=rebate_count_days_next_month) - new_student.save() - - - - - + def handle_rebate_response(request): """ This function is to respond to rebate requests @@ -466,12 +419,9 @@ def handle_rebate_response(request): rebate_count = abs((d - b).days) + 1 receiver = leaves.student_id.id.user action = request.POST["status"] - remark = request.POST["remark"] - leaves.rebate_remark = remark leaves.status = action leaves.save() if action == '2': - rebateCountFn(leaves.start_date, leaves.end_date, leaves.student_id) message = 'Your leave request has been accepted between dates ' + str(b.date()) + ' and ' + str(d.date()) else: message = 'Your leave request has been rejected between dates ' + str(b.date()) + ' and ' + str(d.date()) @@ -520,8 +470,8 @@ def add_special_food_request(request, student): return data spfood_obj = Special_request(student_id=student, start_date=fr, end_date=to, item1=food1, item2=food2, request=purpose) - s_check = Special_request.objects.select_related('student_id','student_id__id','student_id__id__user','student_id__id__department').filter(student_id=student,status__in=['1', '2']).order_by('-app_date') - + requests_food = Special_request.objects.filter(student_id=student) + s_check = requests_food.filter(Q(status='1') | Q(status='2')) for r in s_check: a = datetime.strptime(str(r.start_date), date_format) @@ -586,335 +536,95 @@ def add_bill_base_amount(request): return data -def add_sem_dates(request): - """ - This function is to update the semester start and end date - :param request: - :return: - """ - start_date = request.POST.get("semstart_date") - end_date = request.POST.get("semend_date") - - if (end_date <= start_date): - data = { - 'status': 3, - 'message': "Please check the dates" - } - return data - - data = { - 'status': 1, - 'message': "Successfully updated" - } - semdate_object = Semdates(start_date=start_date, end_date=end_date) - semdate_object.save() - return data def add_mess_committee(request, roll_number): - studentHere = Student.objects.get(id=roll_number) - try: - mess = Messinfo.objects.get(student_id_id=studentHere) - if mess.mess_option == 'mess1': - designation = Designation.objects.get(name='mess_committee') - else: - designation = Designation.objects.get(name='mess_committee_mess2') - check_obj=HoldsDesignation.objects.select_related().filter(Q(user__username=studentHere) & + print("\n\n\\n\n\n\n\n") + print(roll_number) + mess = Messinfo.objects.get(student_id__id=roll_number) + print(mess) + if mess.mess_option == 'mess1': + designation = Designation.objects.get(name='mess_committee_mess1') + else: + designation = Designation.objects.get(name='mess_committee_mess2') + # designation = Designation.objects.get(name='mess_committee') + # add_obj = HoldsDesignation.objects.filter(Q(user__username=roll_number) & Q(designation=designation)) + check_obj = HoldsDesignation.objects.filter(Q(user__username=roll_number) & (Q(designation__name__contains='mess_committee') | Q(designation__name__contains='mess_convener'))) - if check_obj: - data = { - 'status': 2, - 'message': roll_number + " is already a part of mess committee" - } - return data - else: - add_user = User.objects.get(username=roll_number) - designation_object = HoldsDesignation(user=add_user, working=add_user, designation=designation) - designation_object.save() - central_mess_notif(request.user, add_user, 'added_committee', '') - data = { - 'status': 1, - 'message': roll_number + " is added to Mess Committee" - } + if check_obj: + data = { + 'status': 2, + 'message': roll_number + " is already a part of mess committee" + } return data - except: + else: + add_user = User.objects.get(username=roll_number) + designation_object = HoldsDesignation(user=add_user, working=add_user, designation=designation) + designation_object.save() + central_mess_notif(request.user, add_user, 'added_committee', '') data = { - 'status': 0, - 'message': roll_number + " is not registered for any Mess." + 'status': 1, + 'message': roll_number + " is added to Mess Committee" } + return data -def Calculate_rebate(id, month_previous, amount_per_day): - students = Rebate.objects.filter(student_id_id=id) - print(students) - no_of_days = 0 - for student in students: - start_date_month = student.start_date.month - end_date_month = student.end_date.month - if(start_date_month == month_previous): - if(end_date_month == month_previous): - no_of_days = no_of_days + abs((student.end_date - student.start_date).days) + 1 - elif(end_date_month == today_g.month): - no_of_days = no_of_days + abs((last_day_prev_month - student.start_date).days) + 1 - else: - if(end_date_month == month_previous): - no_of_days = no_of_days + abs((student.end_date - student.start_date).days) + 1 - elif(end_date_month == today_g.month): - no_of_days = no_of_days + abs((last_day_prev_month - first_day_prev_month).days) + 1 - print(no_of_days) - rebate_amount = no_of_days*amount_per_day - return rebate_amount - - def generate_bill(): - - per_day_cost_obj = MessBillBase.objects.latest('timestamp') - per_day_cost = per_day_cost_obj.bill_amount - print(per_day_cost) - amount = int(last_day_prev_month.day) * int(per_day_cost) - print(amount) - student_all = Reg_main.objects.filter(current_mess_status = "Registered") - print(student_all) + student_all = Student.objects.all() + month_t = datetime.now().month - 1 + month_g = last_day_prev_month.month + first_day_prev_month = last_day_prev_month.replace(day=1) + # previous_month = month_t.strftime("%B") + amount_c = MessBillBase.objects.latest('timestamp') for student in student_all: - student_id = student.student_id - rem_balance = student.balance - try: - monthly_bill_obj = Monthly_bill.objects.get(student_id=student_id, month=previous_month, year=previous_month_year) - rebate_count_count = monthly_bill_obj.rebate_count - rebate_amount = int(rebate_count_count)*int(per_day_cost) - monthly_bill_obj.rebate_amount = rebate_amount - total_bill = amount - rebate_amount - monthly_bill_obj.total_bill = total_bill - rem_balance = rem_balance - total_bill - student.balance = rem_balance - monthly_bill_obj.amount = amount - monthly_bill_obj.save() - except: - new_monthly_bill_obj = Monthly_bill(student_id=student_id, month=previous_month, year=previous_month_year, amount=amount, total_bill=amount) - rem_balance = rem_balance - amount - student.balance = rem_balance - new_monthly_bill_obj.save() - if(student.balance <= 0): - student.current_mess_status = 'Deregistered' - student.save() - - - - -def handle_reg_response(request): - """ - This function is to respond to registeration requests - @variables: - id: id of the registeration request - reg_req: Object corresponding to the id of the reg request - @return: - data: returns the status of the application - """ - - id = request.POST['id_reg'] - status = request.POST['status'] - remark = request.POST['remark'] - reg_req = Registration_Request.objects.get(pk=id) - start_date = reg_req.start_date - payment_date = reg_req.payment_date - student = reg_req.student_id - reg_req.status = status - reg_req.registration_remark=remark - try: - sem_end_date = Semdates.objects.latest('start_date').end_date - except: - sem_end_date= None - reg_req.save() - message='' - if(status=='accept'): - amount = reg_req.amount - mess = request.POST['mess_no'] - try : - reg_main = Reg_main.objects.get(student_id=student) - if(start_date == date.today()): - reg_main.current_mess_status="Registered" - else: - reg_main.current_mess_status = "Deregistered" - reg_main.mess_option=mess - reg_main.balance=reg_main.balance+amount - reg_main.save() - except: - program = student.programme - if(start_date == date.today()): - mess_status = "Registered" - else: - mess_status = "Deregistered" - new_reg = Reg_main(student_id=student,program=program,current_mess_status=mess_status,balance=amount,mess_option=mess) - new_reg.save() - new_reg_record = Reg_records(student_id=student, start_date=start_date, end_date=sem_end_date) - new_reg_record.save() - - new_payment_record = Payments(student_id = student, amount_paid = amount, payment_date=payment_date, payment_month=current_month(), payment_year=current_year()) - new_payment_record.save() - message="Your registeration request has been accepted" - else: - message="Your registeration request has been rejected" - - - - - receiver = reg_req.student_id.id.user - central_mess_notif(request.user, receiver, 'leave_request', message) - data = { - 'message': 'success' - } - return data - - -def handle_update_payment_response(request): - id = request.POST['id_reg'] - status = request.POST['status'] - remark = request.POST['remark'] - payment_req = Update_Payment.objects.get(pk=id) - payment_date = payment_req.payment_date - student = payment_req.student_id - payment_req.status = status - payment_req.update_remark=remark - - payment_req.save() - - if(status == 'accept'): - amount = payment_req.amount - reg_main_obj= Reg_main.objects.get(student_id=student) - new_balance = reg_main_obj.balance + amount - reg_main_obj.balance = new_balance - reg_main_obj.save() - new_payment_record = Payments(student_id=student, amount_paid = amount, payment_date=payment_date, payment_month=current_month(), payment_year= current_year()) - new_payment_record.save() - - message = 'Your update payment request has been accepted.' - - else: - message = 'Your update payment request has been rejected.' - - receiver = payment_req.student_id.id.user - central_mess_notif(request.user, receiver, 'leave_request', message) - data = { - 'message': 'success' - } - return data - - -def handle_dreg_response(request): - """ - This function is to respond to de registeration requests - @variables: - id: id of the registeration request - dreg_req: Object corresponding to the id of the de reg request - @return: - data: returns the status of the application - """ - - id = request.POST['id_reg'] - status = request.POST['status'] - remark = request.POST['remark'] - dreg_req = Deregistration_Request.objects.get(pk=id) - end_date = dreg_req.end_date - student = dreg_req.student_id - dreg_req.status = status - dreg_req.deregistration_remark=remark - dreg_req.save() - message='' - if(status=='accept'): - try : - reg_main = Reg_main.objects.get(student_id=student) - if(end_date == date.today()): - reg_main.current_mess_status="Deregistered" - reg_record_obj = Reg_records.objects.filter(student_id = student).latest('start_date') - reg_record_obj.end_date = end_date - reg_record_obj.save() - reg_main.save() - except: - data = {'message': 'Student does not exist in database'} - return data - - message="Your De-registeration request has been accepted" - else: - message="Your De-registeration request has been rejected" - - receiver = dreg_req.student_id.id.user - central_mess_notif(request.user, receiver, 'leave_request', message) - data = { - 'message': 'success' - } - return data + nonveg_total_bill=0 + rebate_count = 0 + total = 0 + nonveg_data = Nonveg_data.objects.filter(student_id=student) + rebates = Rebate.objects.filter(student_id=student) + for order in nonveg_data: + if order.order_date.strftime("%B") == previous_month: + nonveg_total_bill = nonveg_total_bill + order.dish.price + for r in rebates: + if r.status == '2': + if r.start_date.month == month_g: + if r.end_date.month == today_g: + rebate_count = rebate_count + abs((last_day_prev_month - r.start_date).days) + 1 + else: + rebate_count = rebate_count + abs((r.end_date - r.start_date).days) + 1 + elif r.end_date.month == month_g: + rebate_count = rebate_count + abs((r.end_date - first_day_prev_month).days) + 1 + else: + rebate_count = 0 + rebate_amount = rebate_count*amount_c.bill_amount/30 + total = amount_c.bill_amount + nonveg_total_bill - rebate_amount + bill_object = Monthly_bill(student_id=student, + month=previous_month, + year = previous_month_year, + amount=amount_c.bill_amount, + rebate_count=rebate_count, + rebate_amount=rebate_amount, + nonveg_total_bill=nonveg_total_bill, + total_bill=total) + if Monthly_bill.objects.filter(student_id=student, + month=previous_month, + year = previous_month_year, + total_bill=total): + print("exists") + elif Monthly_bill.objects.filter(student_id=student, + month=previous_month, + year = previous_month_year): + Monthly_bill.objects.filter(student_id=student, + month=previous_month, + year=previous_month_year).update(student_id=student, + month=previous_month, + amount=amount_c.bill_amount, + rebate_count=rebate_count, + rebate_amount=rebate_amount, + nonveg_total_bill=nonveg_total_bill, + total_bill=total) + # bill_object.update() + else: + bill_object.save() -def update_month_bill(request): - """ - This function is used to update the monthly bill of student by caretaker if any discrepancy arises. - """ - student = str(request.POST.get("rollNo")).upper() - studentHere = Student.objects.get(id = student) - new_amount = int(request.POST.get("new_amount")) - month = request.POST.get("Month") - year = int(request.POST.get("Year")) - try: - bill_base_amount = int(MessBillBase.objects.latest('timestamp').bill_amount) - except: - bill_base_amount = 150 - fixed_amount_per_month = int(bill_base_amount)*int(30) - reg_main_obj = Reg_main.objects.get(student_id=student) - curr_balance = reg_main_obj.balance - try: - existing_monthly_bill_object = Monthly_bill.objects.get(student_id = studentHere, month=month, year=year) - previous_total_bill = existing_monthly_bill_object.total_bill - curr_balance = curr_balance + previous_total_bill - existing_monthly_bill_object.total_bill = new_amount - curr_balance = curr_balance - int(new_amount) - reg_main_obj.balance = curr_balance - reg_main_obj.save() - existing_monthly_bill_object.save() - except: - new_monthly_bill_obj = Monthly_bill(student_id = studentHere, month=month, year= year, total_bill = new_amount, amount=fixed_amount_per_month) - curr_balance = curr_balance - new_amount - reg_main_obj.balance = curr_balance - reg_main_obj.save() - new_monthly_bill_obj.save() - data = { - 'message': 'success' - } - return data - -def handle_add_reg(request): - start_date = request.POST['start_date'] - amount = int(request.POST['amount']) - studentID = str(request.POST['input_roll']).upper() - student = Student.objects.select_related('id','id__user','id__department').get(id=studentID) - payment_date = request.POST['payment_date'] - try: - latest=Semdates.objects.latest('start_date') - latest_end_date = latest.end_date - except: - latest_end_date= None - - mess=request.POST['mess_option_form'] - try : - reg_main = Reg_main.objects.get(student_id=studentID) - if(start_date==str(date.today())): - reg_main.current_mess_status='Registered' - reg_main.mess_option=mess - reg_main.balance=reg_main.balance+amount - reg_main.save() - except: - program = student.programme - if(start_date==str(date.today())): - mess_status = "Registered" - else: - mess_status = "Deregistered" - new_reg = Reg_main(student_id=student,program=program,current_mess_status=mess_status,balance=amount,mess_option=mess) - new_reg.save() - - new_reg_record = Reg_records(student_id=student, start_date=start_date,end_date = latest_end_date) - new_reg_record.save() - - new_payment_record = Payments(student_id = student, amount_paid = amount, payment_date=payment_date, payment_month=current_month(), payment_year=current_year()) - new_payment_record.save() - message="Your registeration request has been accepted" - - diff --git a/FusionIIIT/applications/central_mess/migrations/0001_initial.py b/FusionIIIT/applications/central_mess/migrations/0001_initial.py index 7e80bedf5..2a5bce41c 100644 --- a/FusionIIIT/applications/central_mess/migrations/0001_initial.py +++ b/FusionIIIT/applications/central_mess/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 3.1.5 on 2024-07-16 15:44 +# Generated by Django 3.1.5 on 2023-03-15 18:53 import applications.central_mess.models import datetime @@ -19,7 +19,7 @@ class Migration(migrations.Migration): name='Menu', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('mess_option', models.CharField(choices=[('mess1', 'Mess1'), ('mess2', 'Mess2')], default='mess2', max_length=20)), + ('mess_option', models.CharField(choices=[('mess1', 'Veg_mess'), ('mess2', 'Non_veg_mess')], default='mess2', max_length=20)), ('meal_time', models.CharField(choices=[('MB', 'Monday Breakfast'), ('ML', 'Monday Lunch'), ('MD', 'Monday Dinner'), ('TB', 'Tuesday Breakfast'), ('TL', 'Tuesday Lunch'), ('TD', 'Tuesday Dinner'), ('WB', 'Wednesday Breakfast'), ('WL', 'Wednesday Lunch'), ('WD', 'Wednesday Dinner'), ('THB', 'Thursday Breakfast'), ('THL', 'Thursday Lunch'), ('THD', 'Thursday Dinner'), ('FB', 'Friday Breakfast'), ('FL', 'Friday Lunch'), ('FD', 'Friday Dinner'), ('SB', 'Saturday Breakfast'), ('SL', 'Saturday Lunch'), ('SD', 'Saturday Dinner'), ('SUB', 'Sunday Breakfast'), ('SUL', 'Sunday Lunch'), ('SUD', 'Sunday Dinner')], max_length=20)), ('dish', models.CharField(max_length=200)), ], @@ -51,6 +51,15 @@ class Migration(migrations.Migration): ('timestamp', models.DateTimeField(auto_now_add=True)), ], ), + migrations.CreateModel( + name='Nonveg_menu', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('dish', models.CharField(max_length=20)), + ('price', models.IntegerField()), + ('order_interval', models.CharField(choices=[('Breakfast', 'Breakfast'), ('Lunch', 'Lunch'), ('Dinner', 'Dinner')], default='Breakfast', max_length=20)), + ], + ), migrations.CreateModel( name='Vacation_food', fields=[ @@ -63,19 +72,6 @@ class Migration(migrations.Migration): ('student_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='academic_information.student')), ], ), - migrations.CreateModel( - name='Update_Payment', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('Txn_no', models.CharField(max_length=20)), - ('img', models.ImageField(default=None, upload_to='images/')), - ('amount', models.IntegerField(default=0)), - ('status', models.CharField(default='pending', max_length=10)), - ('update_remark', models.CharField(default='NA', max_length=50)), - ('payment_date', models.DateField(default=None, null=True)), - ('student_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='academic_information.student')), - ], - ), migrations.CreateModel( name='Special_request', fields=[ @@ -84,57 +80,12 @@ class Migration(migrations.Migration): ('end_date', models.DateField(default=datetime.date.today)), ('request', models.TextField()), ('status', models.CharField(choices=[('0', 'rejected'), ('1', 'pending'), ('2', 'accepted')], default='1', max_length=20)), - ('item1', models.CharField(choices=[('dal_chawal', 'Dal Chawal'), ('khicdi', 'Khicdi'), ('tomato_soup', 'Tomato Soup')], default='dal_chawal', max_length=50)), - ('item2', models.CharField(choices=[('breakfast', 'Breakfast'), ('lunch', 'Lunch'), ('dinner', 'Dinner')], default='breakfast', max_length=50)), + ('item1', models.CharField(max_length=50)), + ('item2', models.CharField(max_length=50)), ('app_date', models.DateField(default=datetime.date.today)), ('student_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='academic_information.student')), ], ), - migrations.CreateModel( - name='Semdates', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('start_date', models.DateField(default=datetime.date.today)), - ('end_date', models.DateField(default=datetime.date.today)), - ], - options={ - 'unique_together': {('start_date', 'end_date')}, - }, - ), - migrations.CreateModel( - name='Registration_Request', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('Txn_no', models.CharField(max_length=20)), - ('img', models.ImageField(default=None, upload_to='images/')), - ('amount', models.IntegerField(default=0)), - ('status', models.CharField(default='pending', max_length=10)), - ('registration_remark', models.CharField(default='NA', max_length=50)), - ('start_date', models.DateField(default=None, null=True)), - ('payment_date', models.DateField(default=None, null=True)), - ('student_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='academic_information.student')), - ], - ), - migrations.CreateModel( - name='Reg_records', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('start_date', models.DateField(default=datetime.date.today)), - ('end_date', models.DateField(default=None, null=True)), - ('student_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='academic_information.student')), - ], - ), - migrations.CreateModel( - name='Reg_main', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('program', models.CharField(max_length=10)), - ('current_mess_status', models.CharField(default='Deregistered', max_length=20)), - ('balance', models.IntegerField(default=0)), - ('mess_option', models.CharField(default='mess2', max_length=20)), - ('student_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='academic_information.student')), - ], - ), migrations.CreateModel( name='Rebate', fields=[ @@ -145,18 +96,17 @@ class Migration(migrations.Migration): ('status', models.CharField(choices=[('0', 'rejected'), ('1', 'pending'), ('2', 'accepted')], default='1', max_length=20)), ('app_date', models.DateField(default=datetime.date.today)), ('leave_type', models.CharField(choices=[('casual', 'Casual'), ('vacation', 'Vacation')], default='casual', max_length=20)), - ('rebate_remark', models.CharField(default='NA', max_length=50)), ('student_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='academic_information.student')), ], ), migrations.CreateModel( - name='Payments', + name='Nonveg_data', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('amount_paid', models.IntegerField(default=0)), - ('payment_month', models.CharField(default=applications.central_mess.models.current_month, max_length=20)), - ('payment_year', models.IntegerField(default=applications.central_mess.models.current_year)), - ('payment_date', models.DateField(default=datetime.date(2024, 6, 19))), + ('order_date', models.DateField(default=datetime.date.today)), + ('order_interval', models.CharField(choices=[('Breakfast', 'Breakfast'), ('Lunch', 'Lunch'), ('Dinner', 'Dinner')], default='Breakfast', max_length=20)), + ('app_date', models.DateField(default=datetime.date.today)), + ('dish', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='central_mess.nonveg_menu')), ('student_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='academic_information.student')), ], ), @@ -184,22 +134,11 @@ class Migration(migrations.Migration): name='Feedback', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('mess', models.CharField(choices=[('mess1', 'Mess1'), ('mess2', 'Mess2')], default='mess1', max_length=10)), + ('mess', models.CharField(choices=[('mess1', 'Veg_mess'), ('mess2', 'Non_veg_mess')], default='mess1', max_length=10)), ('mess_rating', models.PositiveSmallIntegerField(default='5')), ('fdate', models.DateField(default=datetime.date.today)), ('description', models.TextField()), ('feedback_type', models.CharField(choices=[('maintenance', 'Maintenance'), ('food', 'Food'), ('cleanliness', 'Cleanliness & Hygiene'), ('others', 'Others')], max_length=20)), - ('feedback_remark', models.CharField(default='NA', max_length=50)), - ('student_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='academic_information.student')), - ], - ), - migrations.CreateModel( - name='Deregistration_Request', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('status', models.CharField(default='pending', max_length=10)), - ('deregistration_remark', models.CharField(default='NA', max_length=50)), - ('end_date', models.DateField(default=None, null=True)), ('student_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='academic_information.student')), ], ), @@ -207,14 +146,13 @@ class Migration(migrations.Migration): name='Payments', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('sem', models.IntegerField()), + ('year', models.IntegerField(default=applications.central_mess.models.current_year)), ('amount_paid', models.IntegerField(default=0)), - ('payment_month', models.CharField(default=applications.central_mess.models.current_month, max_length=20)), - ('payment_year', models.IntegerField(default=applications.central_mess.models.current_year)), - ('payment_date', models.DateField(default=datetime.date(2024, 7, 16))), ('student_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='academic_information.student')), ], options={ - 'unique_together': {('student_id', 'payment_date')}, + 'unique_together': {('student_id', 'sem', 'year')}, }, ), migrations.CreateModel( @@ -226,8 +164,8 @@ class Migration(migrations.Migration): ('amount', models.IntegerField(default=0)), ('rebate_count', models.IntegerField(default=0)), ('rebate_amount', models.IntegerField(default=0)), + ('nonveg_total_bill', models.IntegerField(default=0)), ('total_bill', models.IntegerField(default=0)), - ('paid', models.BooleanField(default=False)), ('student_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='academic_information.student')), ], options={ @@ -238,7 +176,7 @@ class Migration(migrations.Migration): name='Messinfo', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('mess_option', models.CharField(choices=[('mess1', 'Mess1'), ('mess2', 'Mess2')], default='mess2', max_length=20)), + ('mess_option', models.CharField(choices=[('mess1', 'Veg_mess'), ('mess2', 'Non_veg_mess')], default='mess2', max_length=20)), ('student_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='academic_information.student')), ], options={ diff --git a/FusionIIIT/applications/central_mess/migrations/0002_auto_20241020_1126.py b/FusionIIIT/applications/central_mess/migrations/0002_auto_20241020_1126.py deleted file mode 100644 index fa5bb79af..000000000 --- a/FusionIIIT/applications/central_mess/migrations/0002_auto_20241020_1126.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 3.1.5 on 2024-10-20 11:26 - -import datetime -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('central_mess', '0001_initial'), - ] - - operations = [ - migrations.AlterField( - model_name='payments', - name='payment_date', - field=models.DateField(default=datetime.date.today), - ), - migrations.AlterUniqueTogether( - name='payments', - unique_together=set(), - ), - ] diff --git a/FusionIIIT/applications/central_mess/models.py b/FusionIIIT/applications/central_mess/models.py index 3e6030952..cdd4de8a3 100644 --- a/FusionIIIT/applications/central_mess/models.py +++ b/FusionIIIT/applications/central_mess/models.py @@ -8,18 +8,6 @@ ('vacation', 'Vacation') ) -SPECIAL_FOOD = ( - ('dal_chawal', 'Dal Chawal'), - ('khicdi', 'Khicdi'), - ('tomato_soup','Tomato Soup') -) - -MEAL_TIME = ( - ('breakfast', 'Breakfast'), - ('lunch', 'Lunch'), - ('dinner','Dinner') -) - MEAL = ( ('MB', 'Monday Breakfast'), ('ML', 'Monday Lunch'), @@ -95,8 +83,8 @@ ) MESS_OPTION = ( - ('mess1', 'Mess1'), - ('mess2', 'Mess2') + ('mess1', 'Veg_mess'), + ('mess2', 'Non_veg_mess') ) @@ -138,9 +126,8 @@ class Monthly_bill(models.Model): amount = models.IntegerField(default=0) rebate_count = models.IntegerField(default=0) rebate_amount = models.IntegerField(default=0) - # nonveg_total_bill = models.IntegerField(default=0) + nonveg_total_bill = models.IntegerField(default=0) total_bill = models.IntegerField(default=0) - paid = models.BooleanField(default=False) class Meta: unique_together = (('student_id', 'month', 'year'),) @@ -151,16 +138,15 @@ def __str__(self): class Payments(models.Model): student_id = models.ForeignKey(Student, on_delete=models.CASCADE) + sem = models.IntegerField() + year = models.IntegerField(default=current_year) amount_paid = models.IntegerField(default=0) - payment_month = models.CharField(max_length=20, default=current_month) - payment_year = models.IntegerField(default = current_year) - payment_date = models.DateField(default= datetime.date.today) - # class Meta: - # unique_together = (('student_id', 'payment_date')) + class Meta: + unique_together = (('student_id', 'sem', 'year'),) def __str__(self): - return '{}'.format(self.student_id.id) + return '{} - {}'.format(self.student_id.id, self.sem) class Menu(models.Model): @@ -181,10 +167,9 @@ class Rebate(models.Model): purpose = models.TextField() status = models.CharField(max_length=20, choices=STATUS, default='1') app_date = models.DateField(default=datetime.date.today) - # TODO = remove leave type leave_type = models.CharField(choices=LEAVE_TYPE, max_length=20, default="casual") # leave_document = models.FileField(upload_to='central_mess/') - rebate_remark = models.CharField(max_length=50,default='NA') + def __str__(self): return str(self.student_id.id) @@ -201,26 +186,26 @@ def __str__(self): return str(self.student_id.id) -# class Nonveg_menu(models.Model): -# dish = models.CharField(max_length=20) -# price = models.IntegerField() -# order_interval = models.CharField(max_length=20, choices=INTERVAL, -# default='Breakfast') +class Nonveg_menu(models.Model): + dish = models.CharField(max_length=20) + price = models.IntegerField() + order_interval = models.CharField(max_length=20, choices=INTERVAL, + default='Breakfast') -# def __str__(self): -# return '{} - {}'.format(self.dish, self.price) + def __str__(self): + return '{} - {}'.format(self.dish, self.price) -# class Nonveg_data(models.Model): -# student_id = models.ForeignKey(Student, on_delete=models.CASCADE) -# order_date = models.DateField(default=datetime.date.today) -# order_interval = models.CharField(max_length=20, choices=INTERVAL, -# default='Breakfast') -# dish = models.ForeignKey(Nonveg_menu, on_delete=models.CASCADE) -# app_date = models.DateField(default=datetime.date.today) +class Nonveg_data(models.Model): + student_id = models.ForeignKey(Student, on_delete=models.CASCADE) + order_date = models.DateField(default=datetime.date.today) + order_interval = models.CharField(max_length=20, choices=INTERVAL, + default='Breakfast') + dish = models.ForeignKey(Nonveg_menu, on_delete=models.CASCADE) + app_date = models.DateField(default=datetime.date.today) -# def __str__(self): -# return str(self.student_id.id) + def __str__(self): + return str(self.student_id.id) class Special_request(models.Model): @@ -229,10 +214,9 @@ class Special_request(models.Model): end_date = models.DateField(default=datetime.date.today) request = models.TextField() status = models.CharField(max_length=20, choices=STATUS, default='1') - item1 = models.CharField(choices = SPECIAL_FOOD, max_length=50, default ='dal_chawal') - item2 = models.CharField(choices = MEAL_TIME, max_length=50, default ='breakfast') + item1 = models.CharField(max_length=50) + item2 = models.CharField(max_length=50) app_date = models.DateField(default=datetime.date.today) - # special_food = models.CharField(choices = SPECIAL_FOOD, max_length = 50) def __str__(self): return str(self.student_id.id) @@ -275,62 +259,6 @@ class Feedback(models.Model): fdate = models.DateField(default=datetime.date.today) description = models.TextField() feedback_type = models.CharField(max_length=20, choices=FEEDBACK_TYPE) - feedback_remark = models.CharField(max_length=50, default ="NA") - def __str__(self): - return str(self.student_id.id) - - -class Registration_Request(models.Model): - student_id = models.ForeignKey(Student, on_delete=models.CASCADE) - Txn_no =models.CharField(max_length=20) - img = models.ImageField(upload_to='images/',default=None) - amount=models.IntegerField(default=0) - status=models.CharField(max_length=10,default='pending') - registration_remark=models.CharField(max_length=50,default='NA') - start_date=models.DateField(default=None, null=True) - # end_date=models.DateField(default=None, null=True) - payment_date= models.DateField(default=None, null=True) - def __str__(self): - return str(self.student_id.id) - -class Reg_main(models.Model): - student_id = models.ForeignKey(Student, on_delete=models.CASCADE) - program = models.CharField(max_length=10) - current_mess_status = models.CharField(max_length=20,default='Deregistered') - balance = models.IntegerField(default=0) - mess_option = models.CharField(max_length=20,default='mess2') - def __str__(self): - return str(self.student_id.id) -class Reg_records(models.Model): - student_id = models.ForeignKey(Student, on_delete=models.CASCADE) - start_date = models.DateField(default=datetime.date.today) - end_date = models.DateField(default=None, null=True) - def __str__(self): - return str(self.student_id.id) - -class Deregistration_Request(models.Model): - student_id = models.ForeignKey(Student, on_delete=models.CASCADE) - status=models.CharField(max_length=10,default='pending') - deregistration_remark=models.CharField(max_length=50,default='NA') - end_date = models.DateField(default=None, null=True) - def __str__(self): - return str(self.student_id.id) -class Semdates(models.Model): - start_date = models.DateField(blank=False,default=datetime.date.today) - end_date = models.DateField(blank=False,default=datetime.date.today) - class Meta: - unique_together = (('start_date', 'end_date'),) - - -class Update_Payment(models.Model): - student_id = models.ForeignKey(Student, on_delete=models.CASCADE) - Txn_no =models.CharField(max_length=20) - img = models.ImageField(upload_to='images/',default=None) - amount=models.IntegerField(default=0) - status=models.CharField(max_length=10,default='pending') - update_remark=models.CharField(max_length=50,default='NA') - payment_date= models.DateField(default=None, null=True) def __str__(self): return str(self.student_id.id) - \ No newline at end of file diff --git a/FusionIIIT/applications/central_mess/tasks.py b/FusionIIIT/applications/central_mess/tasks.py index e27254b36..36ce85d45 100644 --- a/FusionIIIT/applications/central_mess/tasks.py +++ b/FusionIIIT/applications/central_mess/tasks.py @@ -18,154 +18,3 @@ # @periodic_task(run_every=(crontab(minute='*/1')), name="some_task", ignore_result=True) # def some_task(): # print("5") - -from datetime import date, datetime, timedelta -from django.db import transaction -from threading import Thread -from .models import * -from notification.views import central_mess_notif - -today_g = date.today() -year_g = today_g.year -tomorrow_g = today_g + timedelta(days=1) -first_day_of_this_month = date.today().replace(day=1) -this_month = first_day_of_this_month.strftime('%B') -this_year = first_day_of_this_month.year -last_day_prev_month = first_day_of_this_month - timedelta(days=1) -previous_month = last_day_prev_month.strftime('%B') -previous_month_year = last_day_prev_month.year -first_day_of_next_month = (date.today().replace(day=28) + timedelta(days=4)).replace(day=1) -last_day_of_this_month = first_day_of_next_month - timedelta(days=1) -next_month = first_day_of_next_month.month -first_day_prev_month = last_day_prev_month.replace(day=1) - - -def generate_bill(): - print(today_g) - print(this_month) - print(previous_month) - - t1 = Thread(target=generate_per_day_bill(), args=()) - t1.setDaemon(True) - t1.start() - - - - -def check_registration(student): - try: - reg_date = Reg_records.objects.filter(student_id = student).latest('start_date') - if(reg_date.start_date == today_g): - try: - reg_object = Reg_main.objects.get(student_id = student) - reg_object.current_mess_status = "Registered" - except: - pass - except: - pass - - - - -def check_deregistration(student): - try: - reg_end_date = Reg_records.objects.filter(student_id = student).latest('start_date') - if(reg_end_date.end_date == today_g): - try: - reg_object = Reg_main.objects.get(student_id = student) - reg_object.current_mess_status = "Deregistered" - reg_object.save() - except: - pass - except: - pass - - - -def check_next_month_status(per_day_cost, current_balance, amount): - rem_days = abs(int(last_day_of_this_month.day) -25) - amount_for_remain_days = int(rem_days)*int(per_day_cost) - if(current_balance - amount_for_remain_days < amount): - # TODO send notification to student for paying the fees - central_mess_notif() - - -def check_daily(student, per_day_cost): - two_day_amount = int(2)*int(per_day_cost) - reg_main_obj = Reg_main.objects.get(student_id=student) - balance = reg_main_obj.balance - if(balance <= two_day_amount): - #TODO add the notification for the student - central_mess_notif() - - reg_end_date_obj = Reg_records.objects.filter(student_id = student).latest('start_date') - reg_end_date = tomorrow_g + timedelta(days=1) - reg_end_date_obj.end_date = reg_end_date - reg_end_date_obj.save() - - -def generate_per_day_bill(): - per_day_cost_obj = MessBillBase.objects.latest('timestamp') - per_day_cost = per_day_cost_obj.bill_amount - amount = int(30) * int(per_day_cost) - # print(per_day_cost) - deregistered_students = Reg_main.objects.filter(current_mess_status = "Deregistered") - for student in deregistered_students: - student_id = student.student_id - check_registration(student_id) - - registered_students = Reg_main.objects.filter(current_mess_status = "Registered") - for student in registered_students: - student_id = student.student_id - check_deregistration(student_id) - - registered_students = Reg_main.objects.filter(current_mess_status = "Registered") - for student in registered_students: - student_id = student.student_id - current_balance = student.balance - check_daily(student_id, per_day_cost) - if(int(today_g.day) == 25): - check_next_month_status(per_day_cost, current_balance, amount) - try: - rebate_obj = Rebate.objects.get(student_id = student_id, start_date__lte = today_g, end_date__gte= today_g, status = 2) # TODO check for the correct value of accepting status - try: - monthly_bill_object = Monthly_bill.objects.get(student_id=student_id, month= this_month, year=this_year) - monthly_bill_object.rebate_count = monthly_bill_object.rebate_count + 1 - monthly_bill_object.rebate_amount = monthly_bill_object.rebate_amount + per_day_cost - monthly_bill_object.save() - except: - new_monthly_bill_object = Monthly_bill(student_id=student_id, month = this_month, year=this_year, amount= amount, total_bill = 0, rebate_count = 1, rebate_amount= per_day_cost) - new_monthly_bill_object.save() - except: - try: - monthly_bill_object = Monthly_bill.objects.get(student_id=student_id, month= this_month, year=this_year) - print(monthly_bill_object.total_bill) - monthly_bill_object.total_bill = monthly_bill_object.total_bill + per_day_cost - print(monthly_bill_object.total_bill) - - current_balance = current_balance - per_day_cost - student.balance = current_balance - monthly_bill_object.save() - except: - new_monthly_bill_object = Monthly_bill(student_id=student_id, month = this_month, year=this_year, amount= amount, total_bill = per_day_cost, rebate_count = 0, rebate_amount= 0) - current_balance = current_balance - per_day_cost - student.balance = current_balance - new_monthly_bill_object.save() - if(today_g == last_day_of_this_month and current_balance < amount): - student.current_mess_status = "Deregistered" - try: - reg_record_end_date = Reg_records.objects.filter(student_id = student_id).order_by('start_date')[0] - reg_record_end_date.end_date = first_day_of_next_month - print(reg_record_end_date) - reg_record_end_date.save() - except: - pass - student.save() - - - - - -# def my_scheduled_task(): - # Your task code goes here - # Monthly_bill.objects.all().delete() \ No newline at end of file diff --git a/FusionIIIT/applications/central_mess/urls.py b/FusionIIIT/applications/central_mess/urls.py index 78b65f22c..f98b83047 100644 --- a/FusionIIIT/applications/central_mess/urls.py +++ b/FusionIIIT/applications/central_mess/urls.py @@ -1,6 +1,4 @@ -from django.conf.urls import url , include -from django.conf import settings -from django.conf.urls.static import static +from django.conf.urls import url from . import views @@ -10,16 +8,15 @@ url(r'^$', views.mess, name='mess'), url(r'^menurequest/', views.menu_change_request, name='menu_change_request'), - # url(r'^placeorder/', views.place_order, name='placeorder'), + url(r'^placeorder/', views.place_order, name='placeorder'), url(r'^addleavet/', views.add_leave_manager, name='addleavet'), url(r'^submitmessfeedback/', views.submit_mess_feedback, name='submitmessfeedback'), url(r'^messvacationsubmit/', views.mess_vacation_submit, name='messvacationsubmit'), url(r'^messmenusubmit/', views.submit_mess_menu, name='messmenusubmit'), url(r'^regsubmit/', views.regsubmit, name='regsubmit'), url(r'^startmessregistration/', views.start_mess_registration, name='startmessregistration'), - url(r'^closemessregistration/', views.closeRegistration, name='closemessregistration'), url(r'^menudownload/', views.MenuPDF.as_view(), name='MenuPDF'), - url(r'^menudownload1/', views.MenuPDF1.as_view(), name='MenuPDF1'), + url(r'^menudownload1/', views.MenuPDF.as_view(), name='MenuPDF1'), # url(r'^(?P[0-9]+)/response/', views.response, name='response'), url(r'^response', views.menu_change_response, name='response'), url(r'^(?P[0-9]+)/responsevacationfood/', views.response_vacation_food, name='responsevacationfood'), @@ -32,26 +29,12 @@ url(r'^rebateresponse', views.rebate_response, name='rebateresponse'), url(r'^specialrequestresponse', views.special_request_response, name='specialrequestresponse'), url(r'^updatecost', views.update_cost, name='updatecost'), - # url(r'^getnonvegorder', views.get_nonveg_order, name='getnonvegorder'), + url(r'^getnonvegorder', views.get_nonveg_order, name='getnonvegorder'), url(r'^getleave', views.get_leave_data, name='getleave'), url(r'^generatemessbill', views.generate_mess_bill, name='generatemessbill'), url(r'^acceptleave', views.accept_vacation_leaves, name='acceptleave'), url(r'^selectmessconvener', views.select_mess_convener, name='selectmessconvener'), - url(r'^billdownload', views.BillPDFStudent.as_view(), name='BillPDFStudent'), + url(r'^billdownload', views.download_bill_mess, name='billdownload'), url("info-form", views.mess_info, name="info"), - url(r'^updatemenuu', views.update_menu1, name='update_menu1'), - url(r'^updatemenu', views.update_menu2, name='update_menu2'), - url(r'^api', include('applications.central_mess.api.urls')), - - url(r'^registeredstudent', views.searchAddOrRemoveStudent, name='registeredstudent'), - url(r'^registrationRequest', views.reg_request, name='reg_request'), - url(r'^updatePaymentReq', views.update_payment, name='update_payment'), - url(r'^respond_to_updatePayment', views.respond_to_update_payment, name='response_to_update_payment'), - url(r'^respond_to_reg_req',views.respond_to_reg, name='reg_response'), - url(r'^deregistrationRequest', views.de_reg_request, name='deregistrationRequest'), - url(r'^updatesemdates', views.update_semdates, name='updatesemdates'), - url(r'^updateBill', views.update_bill, name='updateBill'), - url(r'^updatemonthlybill',views.update_bill_excel,name="update_bill"), - -]+ static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) \ No newline at end of file +] diff --git a/FusionIIIT/applications/central_mess/utils.py b/FusionIIIT/applications/central_mess/utils.py index 480ef91a3..17ea7826e 100644 --- a/FusionIIIT/applications/central_mess/utils.py +++ b/FusionIIIT/applications/central_mess/utils.py @@ -10,7 +10,7 @@ def render_to_pdf(template_src, context_dict={}): html = template.render(context_dict) result = BytesIO() print(result.read) - pdf = pisa.pisaDocument(BytesIO(html.encode("utf-8")), result) + pdf = pisa.pisaDocument(BytesIO(html.encode("ISO-8859-1")), result) if not pdf.err: return HttpResponse(result.getvalue(), content_type='application/pdf') return None diff --git a/FusionIIIT/applications/central_mess/views.py b/FusionIIIT/applications/central_mess/views.py index 60a4abb3b..bb1498d55 100644 --- a/FusionIIIT/applications/central_mess/views.py +++ b/FusionIIIT/applications/central_mess/views.py @@ -14,30 +14,25 @@ from .utils import render_to_pdf from applications.academic_information.models import Student from applications.globals.models import ExtraInfo, HoldsDesignation, Designation -from .forms import MinuteForm, MessInfoForm,RegistrationRequest,UpdatePaymentRequest -from .tasks import * +from .forms import MinuteForm, MessInfoForm from .models import (Feedback, Menu, Menu_change_request, Mess_meeting, Mess_minutes, Mess_reg, Messinfo, Monthly_bill, - Payments, Rebate, - Special_request, Vacation_food, MessBillBase,Registration_Request, Reg_records ,Reg_main,Deregistration_Request,Semdates,Update_Payment) -from .handlers import (add_mess_feedback, add_sem_dates, add_vacation_food_request, + Nonveg_data, Nonveg_menu, Payments, Rebate, + Special_request, Vacation_food, MessBillBase) +from .handlers import (add_nonveg_order, add_mess_feedback, add_vacation_food_request, add_menu_change_request, handle_menu_change_response, handle_vacation_food_request, add_mess_registration_time, add_leave_request, add_mess_meeting_invitation, - handle_rebate_response, add_special_food_request, handle_update_payment_response, - handle_special_request, add_bill_base_amount, add_mess_committee, handle_reg_response, handle_dreg_response, update_month_bill,handle_add_reg) - + handle_rebate_response, add_special_food_request, + handle_special_request, add_bill_base_amount, add_mess_committee, generate_bill) from notification.views import central_mess_notif -import csv -import openpyxl - -today_g = datetime.datetime.now() +today_g = datetime.today() month_g = today_g.month month_g_l = today_g.strftime('%B') year_g = today_g.year tomorrow_g = today_g + timedelta(days=1) -first_day_of_this_month = date.today().replace(day=1) +first_day_of_this_month = date.today().replace(day=1) first_day_of_next_month = (date.today().replace(day=28) + timedelta(days=4)).replace(day=1) last_day_of_this_month = first_day_of_next_month - timedelta(days=1) next_month = first_day_of_next_month.month @@ -46,20 +41,19 @@ year_last_g = last_day_prev_month.year previous_month = last_day_prev_month.strftime('%B') -@login_required + def mess(request): """ This view get the access to the central mess dashboard. View all details and apply for any changes. It also shows the previous feedback submitted by the user. """ user = request.user - notifs=request.user.notifications.all() extrainfo = ExtraInfo.objects.select_related().get(user=user) current_date = date.today() holds_designations = HoldsDesignation.objects.select_related().filter(user=user) desig = holds_designations form = MinuteForm() - # mess_reg = Mess_reg.objects.select_related().last() + mess_reg = Mess_reg.objects.select_related().last() count1 = 0 count2 = 0 count3 = 0 @@ -68,444 +62,338 @@ def mess(request): count6 = 0 count7 = 0 count8 = 0 - reg_form = RegistrationRequest() + if extrainfo.user_type == 'student': - # def deleteEntries(): - # Registration_Request.objects.all().delete() - # deleteEntries() student = Student.objects.select_related('id','id__user','id__department').get(id=extrainfo) vaca_obj = Vacation_food.objects.select_related('student_id','student_id__id','student_id__id__user','student_id__id__department').filter(student_id=student) feedback_obj = Feedback.objects.select_related('student_id','student_id__id','student_id__id__user','student_id__id__department').filter(student_id=student).order_by('-fdate') + data = Nonveg_data.objects.select_related('student_id','student_id__id','student_id__id__user','student_id__id__department','dish').filter(student_id=student).order_by('-app_date') monthly_bill = Monthly_bill.objects.select_related('student_id','student_id__id','student_id__id__user','student_id__id__department').filter(student_id=student) payments = Payments.objects.select_related('student_id','student_id__id','student_id__id__user','student_id__id__department').filter(student_id=student) rebates = Rebate.objects.select_related('student_id','student_id__id','student_id__id__user','student_id__id__department').filter(student_id=student).order_by('-app_date') splrequest = Special_request.objects.select_related('student_id','student_id__id','student_id__id__user','student_id__id__department').filter(student_id=student).order_by('-app_date') - - reg_form = RegistrationRequest() - update_form=UpdatePaymentRequest() - - reg_request = Registration_Request.objects.filter(student_id=student) - update_payment_request = Update_Payment.objects.filter(student_id=student) - de_reg_request = Deregistration_Request.objects.filter(student_id=student) - - menu_data = Menu.objects.all() - - try: - mess_optn = Reg_main.objects.select_related('student_id','student_id__id','student_id__id__user','student_id__id__department').get(student_id=student) - y = Menu.objects.filter(mess_option=mess_optn.mess_option) - current_rem_balance = mess_optn.balance - current_mess_status = mess_optn.current_mess_status + mess_optn = Messinfo.objects.select_related('student_id','student_id__id','student_id__id__user','student_id__id__department').get(student_id=student) except: - mess_optn={} - mess_optn={'mess_option':'no-mess'} - y = Menu.objects.filter(mess_option="mess1") - current_rem_balance = 0 - current_mess_status = 'Deregistered' - - - - reg_record = Reg_records.objects.filter(student_id=student) - monthly_bill=monthly_bill[::-1] - - # tot_am=0 - # if len(payments)>0: - # tot_am=payments[0].amount_paid - # else: - # tot_am=0 - # for x in monthly_bill: - # tot_am=tot_am+x.total_bill - # Payments.objects.create(student_id=student,amount_paid=(-tot_am)) - # payments = Payments.objects.select_related('student_id','student_id__id','student_id__id__user','student_id__id__department').filter(student_id=student) - - - - - # for i in range(0,len(monthly_bill)): - # if(monthly_bill[i].paid): - # monthly_bill[i].due_amount=0; - # elif monthly_bill[i].total_bill+tot_am<0: - # monthly_bill[i].due_amount=(monthly_bill[i].total_bill) - # else: - # monthly_bill[i].due_amount=(-tot_am) - # tot_am+=monthly_bill[i].total_bill - # amount_due=-payments[0].amount_paid - # amount_due = 0 - ## adding the batch of student if btech or bdes then value of programme is 1 or else 0, holds value of phd and mtech. + return HttpResponseRedirect("/mess/info-form") - # if student.programme == 'B.Tech' or student.programme == 'B.Des': - # programme = 1 - # else: - # programme = 0 + if student.programme == 'B.Tech' or student.programme == 'B.Des': + programme = 1 + else: + programme = 0 + # newmenu = Menu_change_request.objects.all() # meeting = Mess_meeting.objects.all() # minutes = Mess_minutes.objects.all() - # count = 0 - - # try: - # mess_optn = Messinfo.objects.select_related('student_id','student_id__id','student_id__id__user','student_id__id__department').get(student_id=student) - # y = Menu.objects.filter(mess_option=mess_optn.mess_option) - - - - # bill = Monthly_bill.objects.select_related('student_id','student_id__id','student_id__id__user','student_id__id__department').filter(Q(student_id=student) & Q(month=month_g_l) & Q(year=year_g)) - # amount_c = MessBillBase.objects.latest('timestamp') - # rebate_count = 0 - - # for r in rebates: - # if r.status == '2': - # if r.start_date.month == month_g: - # if r.end_date.month == next_month: - # rebate_count = rebate_count + abs((last_day_of_this_month - r.start_date).days) + 1 - # else: - # rebate_count = rebate_count + abs((r.end_date - r.start_date).days) + 1 - # elif r.end_date.month == month_g: - # rebate_count = rebate_count + abs((r.end_date - first_day_of_this_month).days) + 1 - # else: - # rebate_count = 0 - # rebate_amount = rebate_count * amount_c.bill_amount / 30 - # total_bill = amount_c.bill_amount - rebate_amount - # if bill: - # bill.update(student_id = student, - # month = month_g_l, - # year = year_g, - # amount = amount_c.bill_amount, - # rebate_count = rebate_count, - # rebate_amount = rebate_amount, - # total_bill = total_bill) - - # else: - # bill_object = Monthly_bill(student_id=student, - # amount=amount_c.bill_amount, - # rebate_count=rebate_count, - # rebate_amount=rebate_amount, - # total_bill=total_bill, - # month=month_g_l, - # year=year_g) - # bill_object.save() - # except: - # mess_optn={'mess_option':'no-mess'} - # y = Menu.objects.filter(mess_option="mess1") - - # for d in desig: - # if d.designation.name == 'mess_committee' or d.designation.name == 'mess_convener': - # newmenu = Menu_change_request.objects.select_related('student_id','student_id__id','student_id__id__user','student_id__id__department','dish').filter(dish__mess_option='mess1').order_by('-app_date') - # meeting = Mess_meeting.objects.all() - # minutes = Mess_minutes.objects.select_related().all() - # feed = Feedback.objects.select_related('student_id','student_id__id','student_id__id__user','student_id__id__department').filter(mess='mess1').order_by('-fdate') - # feed2 = Feedback.objects.select_related('student_id','student_id__id','student_id__id__user','student_id__id__department').filter(mess='mess2').order_by('-fdate') - # sprequest = Special_request.objects.select_related('student_id','student_id__id','student_id__id__user','student_id__id__department').filter(status='1').order_by('-app_date') - # sprequest_past = Special_request.objects.select_related('student_id','student_id__id','student_id__id__user','student_id__id__department').filter(status='2').order_by('-app_date') - # menuchangerequest= Menu_change_request.objects.select_related('student_id').filter().order_by('-app_date') - - # # menu_data = Menu.objects.all() - - # for f in feed: - # if f.feedback_type == 'Maintenance' : - # count1 += 1 - - # elif f.feedback_type == 'Food' : - # count2 += 1 - - # elif f.feedback_type == 'Cleanliness' : - # count3 += 1 - - # elif f.feedback_type == 'Others' : - # count4 += 1 - # count5=0 - # count6=0 - # count7=0 - # count8=0 - - # context = { - # 'menu': menu_data, - # 'reg_menu': y, - # 'messinfo': mess_optn, - # 'newmenu': newmenu, - # 'monthly_bill': monthly_bill, - # 'total_due': amount_due, - - # 'vaca': vaca_obj, - # 'info': extrainfo, - # 'feedback': feedback_obj, - # 'feed1': feed, - # 'feed2':'', - # 'student': student, - # 'mess_reg': mess_reg, - # 'current_date': current_date, - # 'count': count, - # 'rebates': rebates, - # 'meeting': meeting, - # 'minutes': minutes, - # 'sprequest': sprequest, - # 'splrequest': splrequest, - # 'sprequest_past': sprequest_past, - # 'menuchangerequest':menuchangerequest, - # 'programme':programme, - # 'count1': count1, - # 'count2': count2, - # 'count3': count3, - # 'count4': count4, - # 'count5': count5, - # 'count6': count6, - # 'count7': count7, - # 'count8': count8, - # 'form': form, - # 'desig': desig, - # 'reg_form':reg_form, - # 'reg_request':reg_request, - # 'reg_main':mess_optn, - # 'reg_record':reg_record, - # 'de_reg_request':de_reg_request, - - # } - # return render(request, "messModule/mess.html", context) - - # if d.designation.name == 'mess_committee_mess2' or d.designation.name == 'mess_convener_mess2': - # newmenu = Menu_change_request.objects.select_related('student_id','student_id__id','student_id__id__user','student_id__id__department','dish').filter(dish__mess_option='mess2').order_by('-app_date') - # meeting = Mess_meeting.objects.all() - # minutes = Mess_minutes.objects.select_related().all() - # feed = Feedback.objects.select_related('student_id','student_id__id','student_id__id__user','student_id__id__department').filter(mess='mess2').order_by('-fdate') - # feed2 = Feedback.objects.select_related('student_id','student_id__id','student_id__id__user','student_id__id__department').filter(mess='mess1').order_by('-fdate') - # sprequest = Special_request.objects.select_related('student_id','student_id__id','student_id__id__user','student_id__id__department').filter(status='1').order_by('-app_date') - # sprequest_past = Special_request.objects.select_related('student_id','student_id__id','student_id__id__user','student_id__id__department').filter(status='2').order_by('-app_date') - # menuchangerequest= Menu_change_request.objects.select_related('student_id').filter().order_by('-app_date') - - # # menu_data = Menu.objects.all().order_by() - - # count5=0 - # count6=0 - # count7=0 - # count8=0 - # for f in feed: - # if f.feedback_type == 'Maintenance' : - # count1 += 1 - - # elif f.feedback_type == 'Food' : - # count2 += 1 - - # elif f.feedback_type == 'Cleanliness' : - # count3 += 1 - - # elif f.feedback_type == 'Others' : - # count4 += 1 - - # context = { - # 'menu': menu_data, - # 'reg_menu': y, - # 'messinfo': mess_optn, - # 'newmenu': newmenu, - # 'monthly_bill': monthly_bill, - # 'total_due': amount_due, - # 'vaca': vaca_obj, - # 'info': extrainfo, - # 'feedback': feedback_obj, - # 'feed2': feed, - # 'feed1':'', - # 'student': student, - # # 'data': data, - # 'mess_reg': mess_reg, - # 'current_date': current_date, - # 'count': count, - # 'rebates': rebates, - # 'programme': programme, - # 'meeting': meeting, - # 'minutes': minutes, - # 'splrequest': splrequest, - # 'sprequest': sprequest, - # 'sprequest_past': sprequest_past, - # 'menuchangerequest':menuchangerequest, - # 'count1': count1, - # 'count2': count2, - # 'count3': count3, - # 'count4': count4, - # 'count5': count5, - # 'count6': count6, - # 'count7': count7, - # 'count8': count8, - # 'form': form, - # 'desig': desig, - # 'reg_form':reg_form, - # 'reg_request':reg_request, - # 'reg_main':mess_optn, - # 'reg_record':reg_record, - # 'de_reg_request':de_reg_request, - # } - # return render(request, "messModule/mess.html", context) - - context = { - 'menu': menu_data, - # 'reg_menu': y, - 'messinfo': mess_optn, - 'monthly_bill': monthly_bill, - # 'total_due': amount_due, - 'vaca': vaca_obj, - 'info': extrainfo, - 'feedback': feedback_obj, - 'student': student, - # 'mess_reg': mess_reg, - 'current_date': current_date, - # 'count': count, - 'rebates': rebates, - 'splrequest': splrequest, - 'form': form, - # 'programme': programme, - 'desig': desig, - # 'minutes': minutes, - # 'meeting': meeting, - 'reg_form':reg_form, - 'update_form':update_form, - 'update_payment_request':update_payment_request, - 'reg_main_stud':mess_optn, - 'reg_request':reg_request, - 'reg_record':reg_record, - 'de_reg_request':de_reg_request, - 'payments': payments, - 'curr_balance': current_rem_balance, - 'curr_status':current_mess_status, - 'notifications':notifs - } - - return render(request, "messModule/mess.html", context) + # feed = Feedback.objects.all() + # sprequest = Special_request.objects.filter(status='1') + count = 0 + #variable y stores the menu items + + y = Menu.objects.filter(mess_option=mess_optn.mess_option) + x = Nonveg_menu.objects.all() + + # for item in rebates: + # d1 = item.start_date + # d2 = item.end_date + # item.duration = abs((d2 - d1).days)+1 + # item.save() + + # for items in rebates: + # if items.leave_type == 'casual' and (items.status == '1' or items.status == '2'): + # count += item.duration + + bill = Monthly_bill.objects.select_related('student_id','student_id__id','student_id__id__user','student_id__id__department').filter(Q(student_id=student) & Q(month=month_g_l) & Q(year=year_g)) + amount_c = MessBillBase.objects.latest('timestamp') + rebate_count = 0 + nonveg_total_bill = 0 + for z in data: + if z.order_date.month == month_g: + nonveg_total_bill = nonveg_total_bill + z.dish.price + else: + bill.nonveg_total_bill = 0 + + for r in rebates: + if r.status == '2': + if r.start_date.month == month_g: + if r.end_date.month == next_month: + rebate_count = rebate_count + abs((last_day_of_this_month - r.start_date).days) + 1 + else: + rebate_count = rebate_count + abs((r.end_date - r.start_date).days) + 1 + elif r.end_date.month == month_g: + rebate_count = rebate_count + abs((r.end_date - first_day_of_this_month).days) + 1 + else: + rebate_count = 0 + rebate_amount = rebate_count * amount_c.bill_amount / 30 + total_bill = amount_c.bill_amount - rebate_amount + nonveg_total_bill + if bill: + bill.update(student_id = student, + month = month_g_l, + year = year_g, + amount = amount_c.bill_amount, + rebate_count = rebate_count, + rebate_amount = rebate_amount, + nonveg_total_bill=nonveg_total_bill, + total_bill = total_bill) - + else: + bill_object = Monthly_bill(student_id=student, + amount=amount_c.bill_amount, + rebate_count=rebate_count, + rebate_amount=rebate_amount, + nonveg_total_bill=nonveg_total_bill, + total_bill=total_bill, + month=month_g_l, + year=year_g) + bill_object.save() - elif extrainfo.user_type == 'staff': for d in desig: - if(d.designation.name == 'mess_manager'): - try: - current_bill = MessBillBase.objects.latest('timestamp') - except: - new_entry = MessBillBase(bill_amount=150) - new_entry.save() - current_bill = MessBillBase.objects.latest('timestamp') - newmenu = Menu_change_request.objects.select_related('student_id','student_id__id','student_id__id__user','student_id__id__department','dish').all().order_by('-app_date') - vaca_all = Vacation_food.objects.select_related('student_id','student_id__id','student_id__id__user','student_id__id__department').all().order_by('-app_date') - members_mess = HoldsDesignation.objects.select_related().filter(Q(designation__name__contains='mess_convener') - | Q(designation__name__contains='mess_committee')) - y = Menu.objects.all() - leave = Rebate.objects.select_related('student_id','student_id__id','student_id__id__user','student_id__id__department').filter(status='1').order_by('-app_date') - leave_past = Rebate.objects.select_related('student_id','student_id__id','student_id__id__user','student_id__id__department').filter(status='2').order_by('-app_date') - # meeting = Mess_meeting.objects.all() - # minutes = Mess_minutes.objects.all() - feed1 = Feedback.objects.select_related('student_id','student_id__id','student_id__id__user','student_id__id__department').filter(mess='mess1').order_by('-fdate') + if d.designation.name == 'mess_committee_mess1' or d.designation.name == 'mess_convener_mess1': + newmenu = Menu_change_request.objects.select_related('student_id','student_id__id','student_id__id__user','student_id__id__department','dish').filter(dish__mess_option='mess1').order_by('-app_date') + # newmenu = Menu_change_request.objects.all() + meeting = Mess_meeting.objects.all() + minutes = Mess_minutes.objects.select_related().all() + feed = Feedback.objects.select_related('student_id','student_id__id','student_id__id__user','student_id__id__department').filter(mess='mess1').order_by('-fdate') feed2 = Feedback.objects.select_related('student_id','student_id__id','student_id__id__user','student_id__id__department').filter(mess='mess2').order_by('-fdate') - - for f in feed1: - if f.feedback_type == 'Maintenance' : + sprequest = Special_request.objects.select_related('student_id','student_id__id','student_id__id__user','student_id__id__department').filter(status='1').order_by('-app_date') + sprequest_past = Special_request.objects.select_related('student_id','student_id__id','student_id__id__user','student_id__id__department').filter(status='2').order_by('-app_date') + # count1 = feed.filter(Q(feedback_type='Maintenance') & Q(mess='mess1')).count() + for f in feed: + if f.feedback_type == 'Maintenance' and mess_optn.mess_option == 'mess1': count1 += 1 - elif f.feedback_type == 'Food' : + elif f.feedback_type == 'Food' and mess_optn.mess_option == 'mess1': count2 += 1 - elif f.feedback_type == 'Cleanliness' : + elif f.feedback_type == 'Cleanliness' and mess_optn.mess_option == 'mess1': count3 += 1 - elif f.feedback_type == 'Others' : + elif f.feedback_type == 'Others' and mess_optn.mess_option == 'mess1': count4 += 1 - for f in feed2: - if f.feedback_type == 'Maintenance': + if f.feedback_type == 'Maintenance' and mess_optn.mess_option == 'mess2': count5 += 1 - elif f.feedback_type == 'Food': + elif f.feedback_type == 'Food' and mess_optn.mess_option == 'mess2': count6 += 1 - elif f.feedback_type == 'Cleanliness': + elif f.feedback_type == 'Cleanliness' and mess_optn.mess_option == 'mess2': count7 += 1 - elif f.feedback_type == 'Others': + elif f.feedback_type == 'Others' and mess_optn.mess_option == 'mess2': count8 += 1 - - sprequest = Special_request.objects.select_related('student_id','student_id__id','student_id__id__user','student_id__id__department').filter(status='1').order_by('-app_date') - sprequest_past = Special_request.objects.select_related('student_id','student_id__id','student_id__id__user','student_id__id__department').filter(status='2').order_by('-app_date') - reg_request = Registration_Request.objects.select_related('student_id','student_id__id','student_id__id__user','student_id__id__department').all().filter(status='pending') - update_pay_request=Update_Payment.objects.select_related('student_id','student_id__id','student_id__id__user','student_id__id__department').all().filter(status='pending') - de_reg_request = Deregistration_Request.objects.select_related('student_id','student_id__id','student_id__id__user','student_id__id__department').all().filter(status='pending') - reg_main = Reg_main.objects.select_related('student_id','student_id__id','student_id__id__user','student_id__id__department').filter(current_mess_status='Registered') - reg_record = Reg_records.objects.select_related('student_id','student_id__id','student_id__id__user','student_id__id__department').all() - bills = Monthly_bill.objects.select_related('student_id','student_id__id','student_id__id__user','student_id__id__department').all() - # bills = Monthly_bill.objects.all() context = { - 'bill_base': current_bill, - 'today': today_g.date(), - 'tomorrow': tomorrow_g.date(), - 'members': members_mess, 'menu': y, + 'messinfo': mess_optn, 'newmenu': newmenu, - 'vaca_all': vaca_all, + 'monthly_bill': monthly_bill, + 'payments': payments, + 'nonveg': x, + 'vaca': vaca_obj, 'info': extrainfo, - 'leave': leave, - 'leave_past': leave_past, + 'feedback': feedback_obj, + 'feed': feed, + 'student': student, + 'data': data, + 'mess_reg': mess_reg, 'current_date': current_date, - # 'mess_reg': mess_reg, - 'desig': desig, - # 'meeting': meeting, - # 'minutes': minutes, + 'count': count, + 'rebates': rebates, + 'meeting': meeting, + 'minutes': minutes, 'sprequest': sprequest, + 'splrequest': splrequest, 'sprequest_past': sprequest_past, + 'programme':programme, 'count1': count1, - 'count2': count2, 'count3': count3, 'feed1': feed1,'feed2':feed2, - 'count4': count4, 'form': form, 'count5': count5, - 'count6': count6, 'count7': count7, 'count8': count8, 'desig': desig, - 'reg_request':reg_request,'reg_record':reg_record,'reg_main':reg_main, - 'de_reg_request':de_reg_request, - 'bill': bills, - 'reg_form':reg_form, - 'update_pay_request':update_pay_request + 'count2': count2, + 'count3': count3, + 'count4': count4, + 'count5': count5, + 'count6': count6, + 'count7': count7, + 'count8': count8, + 'form': form, + 'desig': desig } return render(request, "messModule/mess.html", context) - elif extrainfo.user_type == 'faculty': - for d in desig: - if(d.designation.name == 'mess_warden'): - - feed1 = Feedback.objects.select_related('student_id','student_id__id','student_id__id__user','student_id__id__department').filter(mess='mess1').order_by('-fdate') - feed2 = Feedback.objects.select_related('student_id','student_id__id','student_id__id__user','student_id__id__department').filter(mess='mess2').order_by('-fdate') - y = Menu.objects.all() - - reg_main = Reg_main.objects.select_related('student_id','student_id__id','student_id__id__user','student_id__id__department').filter(current_mess_status='Registered') - reg_record = Reg_records.objects.select_related('student_id','student_id__id','student_id__id__user','student_id__id__department').all() - bills = Monthly_bill.objects.select_related('student_id','student_id__id','student_id__id__user','student_id__id__department').all() - - for f in feed1: - if f.feedback_type == 'Maintenance' : + if d.designation.name == 'mess_committee_mess2' or d.designation.name == 'mess_convener_mess2': + # newmenu = Menu_change_request.objects.all() + newmenu = Menu_change_request.objects.select_related('student_id','student_id__id','student_id__id__user','student_id__id__department','dish').filter(dish__mess_option='mess2').order_by('-app_date') + meeting = Mess_meeting.objects.all() + minutes = Mess_minutes.objects.select_related().all() + feed = Feedback.objects.select_related('student_id','student_id__id','student_id__id__user','student_id__id__department').filter(mess='mess2').order_by('-fdate') + feed2 = Feedback.objects.select_related('student_id','student_id__id','student_id__id__user','student_id__id__department').filter(mess='mess1').order_by('-fdate') + sprequest = Special_request.objects.select_related('student_id','student_id__id','student_id__id__user','student_id__id__department').filter(status='1').order_by('-app_date') + sprequest_past = Special_request.objects.select_related('student_id','student_id__id','student_id__id__user','student_id__id__department').filter(status='2').order_by('-app_date') + # count5 = feed.filter(Q(feedback_type='Maintenance') & Q(mess='mess2')).count() + for f in feed2: + if f.feedback_type == 'Maintenance' and mess_optn.mess_option == 'mess1': count1 += 1 - elif f.feedback_type == 'Food' : + elif f.feedback_type == 'Food' and mess_optn.mess_option == 'mess1': count2 += 1 - elif f.feedback_type == 'Cleanliness' : + elif f.feedback_type == 'Cleanliness' and mess_optn.mess_option == 'mess1': count3 += 1 - elif f.feedback_type == 'Others' : + elif f.feedback_type == 'Others' and mess_optn.mess_option == 'mess1': count4 += 1 - - for f in feed2: - if f.feedback_type == 'Maintenance': + for f in feed: + if f.feedback_type == 'Maintenance' and mess_optn.mess_option == 'mess2': count5 += 1 - elif f.feedback_type == 'Food': + elif f.feedback_type == 'Food' and mess_optn.mess_option == 'mess2': count6 += 1 - elif f.feedback_type == 'Cleanliness': + elif f.feedback_type == 'Cleanliness' and mess_optn.mess_option == 'mess2': count7 += 1 - elif f.feedback_type == 'Others': + elif f.feedback_type == 'Others' and mess_optn.mess_option == 'mess2': count8 += 1 + context = { - 'info': extrainfo, - 'desig': desig, 'menu': y, + 'messinfo': mess_optn, + 'newmenu': newmenu, + 'monthly_bill': monthly_bill, + 'payments': payments, + 'nonveg': x, + 'vaca': vaca_obj, + 'info': extrainfo, + 'feedback': feedback_obj, + 'feed': feed, + 'student': student, + 'data': data, + 'mess_reg': mess_reg, + 'current_date': current_date, + 'count': count, + 'rebates': rebates, + 'programme': programme, + 'meeting': meeting, + 'minutes': minutes, + 'sprequest': sprequest, + 'splrequest': splrequest, + 'sprequest_past': sprequest_past, 'count1': count1, - 'count2': count2, 'count3': count3, 'feed1': feed1,'feed2':feed2, - 'count4': count4, 'form': form, 'count5': count5, - 'count6': count6, 'count7': count7, 'count8': count8, 'desig': desig, - 'reg_record':reg_record,'reg_main':reg_main,'bill': bills, + 'count2': count2, + 'count3': count3, + 'count4': count4, + 'count5': count5, + 'count6': count6, + 'count7': count7, + 'count8': count8, + 'form': form, + 'desig': desig } - return render(request, 'messModule/mess.html', context) + return render(request, "messModule/mess.html", context) + + context = { + 'menu': y, + 'messinfo': mess_optn, + 'monthly_bill': monthly_bill, + 'payments': payments, + 'nonveg': x, + 'vaca': vaca_obj, + 'info': extrainfo, + 'feedback': feedback_obj, + 'student': student, + 'data': data, + 'mess_reg': mess_reg, + 'current_date': current_date, + 'count': count, + 'rebates': rebates, + 'splrequest': splrequest, + 'form': form, + 'programme': programme, + 'desig': desig + } + + return render(request, "messModule/mess.html", context) + + elif extrainfo.user_type == 'staff': + current_bill = MessBillBase.objects.latest('timestamp') + nonveg_orders_today = Nonveg_data.objects.filter(order_date=today_g)\ + .values('dish__dish','order_interval').annotate(total=Count('dish')) + nonveg_orders_tomorrow = Nonveg_data.objects.select_related('student_id','student_id__id','student_id__id__user','student_id__id__department','dish').filter(order_date=tomorrow_g)\ + .values('dish__dish','order_interval').annotate(total=Count('dish')) + # make info with diff name and then pass context + newmenu = Menu_change_request.objects.select_related('student_id','student_id__id','student_id__id__user','student_id__id__department','dish').all().order_by('-app_date') + vaca_all = Vacation_food.objects.select_related('student_id','student_id__id','student_id__id__user','student_id__id__department').all().order_by('-app_date') + # members_mess = HoldsDesignation.objects.filter(designation__name='mess_convener') + members_mess = HoldsDesignation.objects.select_related().filter(Q(designation__name__contains='mess_convener') + | Q(designation__name__contains='mess_committee')) + y = Menu.objects.all() + x = Nonveg_menu.objects.all() + leave = Rebate.objects.select_related('student_id','student_id__id','student_id__id__user','student_id__id__department').filter(status='1').order_by('-app_date') + leave_past = Rebate.objects.select_related('student_id','student_id__id','student_id__id__user','student_id__id__department').filter(status='2').order_by('-app_date') + + context = { + 'bill_base': current_bill, + 'today': today_g.date(), + 'tomorrow': tomorrow_g.date(), + 'nonveg_orders_t':nonveg_orders_tomorrow, + 'nonveg_orders': nonveg_orders_today, + 'members': members_mess, + 'menu': y, + 'newmenu': newmenu, + 'vaca_all': vaca_all, + 'info': extrainfo, + 'leave': leave, + 'leave_past': leave_past, + 'current_date': current_date, + 'mess_reg': mess_reg, + 'desig': desig, + } + + return render(request, "messModule/mess.html", context) + + elif extrainfo.user_type == 'faculty': + meeting = Mess_meeting.objects.all() + minutes = Mess_minutes.objects.select_related().all() + feed = Feedback.objects.select_related('student_id','student_id__id','student_id__id__user','student_id__id__department').all().order_by('-fdate') + y = Menu.objects.all() + + for f in feed: + mess_opt = Messinfo.objects.select_related('student_id','student_id__id','student_id__id__user','student_id__id__department').get(student_id=f.student_id) + if f.feedback_type == 'Maintenance' and mess_opt.mess_option == 'mess1': + count1 += 1 + + elif f.feedback_type == 'Food' and mess_opt.mess_option == 'mess1': + count2 += 1 + + elif f.feedback_type == 'Cleanliness' and mess_opt.mess_option == 'mess1': + count3 += 1 + elif f.feedback_type == 'Others' and mess_opt.mess_option == 'mess1': + count4 += 1 + + for f in feed: + mess_opt = Messinfo.objects.select_related('student_id','student_id__id','student_id__id__user','student_id__id__department').get(student_id=f.student_id) + if f.feedback_type == 'Maintenance' and mess_opt.mess_option == 'mess2': + count5 += 1 + + elif f.feedback_type == 'Food' and mess_opt.mess_option == 'mess2': + count6 += 1 + + elif f.feedback_type == 'Cleanliness' and mess_opt.mess_option == 'mess2': + count7 += 1 + + elif f.feedback_type == 'Others' and mess_opt.mess_option == 'mess2': + count8 += 1 + context = { + 'info': extrainfo, + 'menu': y, + 'meeting': meeting, + 'minutes': minutes, + 'count1': count1, + 'count2': count2, 'count3': count3, 'feed': feed, + 'count4': count4, 'form': form, 'count5': count5, + 'count6': count6, 'count7': count7, 'count8': count8, 'desig': desig + } + return render(request, 'messModule/mess.html', context) @login_required @transaction.atomic @@ -529,19 +417,38 @@ def mess_info(request): mess_option = form.cleaned_data['mess_option'] Messinfo.objects.create(student_id=student_id, mess_option=mess_option) return HttpResponseRedirect("/mess") - user_id = request.user - student_id = Student.objects.select_related( - 'id').only('id__id').get(id__id=user_id) - if Messinfo.objects.filter(student_id=student_id).exists(): - return HttpResponseRedirect("/mess") + form = MessInfoForm() context = { "form": form } return render(request, "messModule/messInfoForm.html", context) +@login_required +@transaction.atomic +@csrf_exempt +def place_order(request): + """ + This function is to place non-veg food orders - + @param: + request: contains metadata about the requested page + + @variables: + user: Current user + order_interval: Time of the day for which order is placed eg breakfast/lunch/dinner + extra_info: Extra information about the current user. From model ExtraInfo + student: Student information about the current user + student_mess: Mess choices of the student + dish_request: Predefined dish available + """ + user = request.user + extra_info = ExtraInfo.objects.select_related().get(user=user) + if extra_info.user_type == 'student': + student = Student.objects.select_related('id','id__user','id__department').get(id=extra_info) + student_mess = Messinfo.objects.select_related('student_id','student_id__id','student_id__id__user','student_id__id__department').get(student_id=student) + add_nonveg_order(request, student) + return HttpResponseRedirect('/mess') @csrf_exempt @@ -618,9 +525,9 @@ def submit_mess_menu(request): extrainfo = ExtraInfo.objects.select_related().get(user=user) designation = holds_designations student = Student.objects.select_related('id','id__user','id__department').get(id=extrainfo) - + # globallyChange() context = {} - + # A user may hold multiple designations data = add_menu_change_request(request,student) if data['status'] == 1: @@ -662,7 +569,7 @@ def response_vacation_food(request, ap_id): holds_designations: designation of current user """ user = request.user - + # extra_info = ExtraInfo.objects.get(user=user) holds_designations = HoldsDesignation.objects.select_related().filter(user=user) designation = holds_designations @@ -752,12 +659,6 @@ def start_mess_registration(request): data = add_mess_registration_time(request) return JsonResponse(data) -@csrf_exempt -def closeRegistration(request): - mess_reg = Mess_reg.objects.last() - yesterday = date.today() - timedelta(days=1) - Mess_reg.objects.filter(id=mess_reg.id).update(end_reg=yesterday) - return HttpResponseRedirect('/mess') @transaction.atomic @csrf_exempt @@ -842,8 +743,6 @@ def rebate_response(request): for d in designation: if d.designation.name == 'mess_manager': data = handle_rebate_response(request) - print(data) - print(request) return JsonResponse(data) @@ -899,29 +798,10 @@ def update_cost(request): user - contains user details """ user = request.user + # extrainfo = ExtraInfo.objects.get(user=user) data = add_bill_base_amount(request) return JsonResponse(data) -@login_required -def update_semdates(request): - """ - This function is to update the semester start and end date - - @param: - request - contains metadata about the requested page - - @variables: - user - contains user details - """ - user = request.user - data = add_sem_dates(request) - return HttpResponseRedirect('/mess') -@csrf_exempt -@login_required -def update_bill(request): - # user = request.user - update_month_bill(request) - return HttpResponseRedirect('/mess') def generate_mess_bill(request): """ @@ -942,10 +822,10 @@ def generate_mess_bill(request): """ # todo generate proper logic for generate_mess_bill user = request.user - # t1 = Thread(target=generate_bill, args=()) - # t1.setDaemon(True) - # t1.start() - generate_bill() + t1 = Thread(target=generate_bill, args=()) + t1.setDaemon(True) + t1.start() + # int = generate_bill() data ={ 'status': 1 } @@ -959,13 +839,25 @@ def post(self, request, *args, **kwargs): extra_info = ExtraInfo.objects.select_related().get(user=user) y = Menu.objects.all() - context = { - 'menu': y, - 'mess_option': 'mess2', - 'date':str(today_g.date()) - } - return render_to_pdf('messModule/menudownloadable2.html', context) - # return HttpResponse(pdf, content_type='application/pdf') + if extra_info.user_type=='student': + student = Student.objects.select_related('id','id__user','id__department').get(id=extra_info) + mess_info = Messinfo.objects.select_related('student_id','student_id__id','student_id__id__user','student_id__id__department').get(student_id=student) + mess_option = mess_info.mess_option + context = { + 'menu': y, + 'mess_option': mess_option + } + if mess_option=='mess2': + return render_to_pdf('messModule/menudownloadable2.html', context) + else: + return render_to_pdf('messModule/menudownloadable1.html', context) + else: + context = { + 'menu': y, + 'mess_option': 'mess2' + } + return render_to_pdf('messModule/menudownloadable2.html', context) + # return HttpResponse(pdf, content_type='application/pdf') class MenuPDF1(View): @@ -985,29 +877,9 @@ def post(self, request, *args, **kwargs): y = Menu.objects.all() context = { 'menu': y, - 'mess_option': 'mess1', - 'date':str(today_g.date()) + 'mess_option': 'mess1' } return render_to_pdf('messModule/menudownloadable1.html', context) - -class BillPDFStudent(View): - def post(self, request, *args, **kwargs): - user = request.user - extra_info = ExtraInfo.objects.select_related().get(user=user) - student = Student.objects.select_related('id','id__user','id__department').get(id=extra_info) - # reg_student = Reg_records.objects.select_related('student_id','student_id__id','student_id__id__user','student_id__id__department').get(student_id_id=student) - try: - monthly_bill = Monthly_bill.objects.select_related('student_id','student_id__id','student_id__id__user','student_id__id__department').filter(student_id=student) - if monthly_bill.exists(): - context = { - 'student_bill': monthly_bill - } - return render_to_pdf('messModule/billpdfexport.html', context) - else : - return HttpResponseRedirect('/mess') - except: - return HttpResponseRedirect('/mess') - def menu_change_request(request): @@ -1027,7 +899,6 @@ def menu_change_request(request): return JsonResponse(data) -@csrf_exempt def submit_mess_committee(request): """ This function is to add the new mess committee @@ -1039,9 +910,10 @@ def submit_mess_committee(request): current_user - get user from request user_details - extract details and designation of the user from the database """ - roll_number = str(request.POST.get('roll_number')).upper() + roll_number = request.POST['rollnumber'] + data = add_mess_committee(request, roll_number) - return HttpResponseRedirect("/mess") + return JsonResponse(data) def remove_mess_committee(request): @@ -1059,10 +931,10 @@ def remove_mess_committee(request): member_id = request.POST['member_id'] data_m = member_id.split("-") roll_number = data_m[1] - if data_m[0] == 'mess_committee': - designation = Designation.objects.get(name='mess_committee') - elif data_m[0] == 'mess_convener': - designation = Designation.objects.get(name='mess_convener') + if data_m[0] == 'mess_committee_mess1': + designation = Designation.objects.get(name='mess_committee_mess1') + elif data_m[0] == 'mess_convener_mess1': + designation = Designation.objects.get(name='mess_convener_mess1') elif data_m[0] == 'mess_committee_mess2': designation = Designation.objects.get(name='mess_committee_mess2') else: @@ -1143,9 +1015,9 @@ def select_mess_convener(request): data_m = member_id.split("-") roll_number = data_m[1] - if data_m[0] == 'mess_committee': - designation = Designation.objects.get(name='mess_committee') - new_designation = Designation.objects.get(name='mess_convener') + if data_m[0] == 'mess_committee_mess1': + designation = Designation.objects.get(name='mess_committee_mess1') + new_designation = Designation.objects.get(name='mess_convener_mess1') # One mess can have only one mess convener existing_check = HoldsDesignation.objects.select_related().filter(designation=new_designation) if existing_check.count(): @@ -1209,6 +1081,27 @@ def download_bill_mess(request): } return render_to_pdf('messModule/billpdfexport.html', context) + +def get_nonveg_order(request): + """ + This function is to apply for non-veg order + + @param: + request - contains metadata about the requested page + + @variables: + current_user - get user from request + user_details - extract details of the user from the database + """ + date_o = request.POST['order_date'] + nonveg_orders_tomorrow = Nonveg_data.objects.select_related('student_id','student_id__id','student_id__id__user','student_id__id__department','dish').filter(order_date=date_o) \ + .values('dish__dish', 'order_interval').annotate(total=Count('dish')) + data = { + 'status': 1, + } + return JsonResponse(data) + + def add_leave_manager(request): """ This function is to apply for leave @@ -1269,419 +1162,3 @@ def add_leave_manager(request): central_mess_notif(request.user, student.id.user, 'leave_request', message) add_obj.save() return HttpResponseRedirect('/mess') - -def update_menu2(request): - if (request.method == "POST"): - mb = request.POST['MB2'] - ml = request.POST['ML2'] - md = request.POST['MD2'] - sud = request.POST['SUD2'] - sul = request.POST['SUL2'] - sub = request.POST['SUB2'] - sd = request.POST['SD2'] - sl = request.POST['SL2'] - sb = request.POST['SB2'] - fd = request.POST['FD2'] - fl = request.POST['FL2'] - fb = request.POST['FB2'] - thd = request.POST['THD2'] - thl = request.POST['THL2'] - thb = request.POST['THB2'] - wd = request.POST['WD2'] - wl = request.POST['WL2'] - wb = request.POST['WB2'] - td = request.POST['TD2'] - tl = request.POST['TL2'] - tb = request.POST['TB2'] - - print("mb", mb) - Menu.objects.filter(mess_option = 'mess2',meal_time='MB').update(dish = mb) - Menu.objects.filter(mess_option = 'mess2',meal_time='ML').update(dish = ml) - Menu.objects.filter(mess_option = 'mess2',meal_time='MD').update(dish = md) - Menu.objects.filter(mess_option = 'mess2',meal_time='TB').update(dish = tb) - Menu.objects.filter(mess_option = 'mess2',meal_time='TL').update(dish = tl) - Menu.objects.filter(mess_option = 'mess2',meal_time='TD').update(dish = td) - Menu.objects.filter(mess_option = 'mess2',meal_time='WB').update(dish = wb) - Menu.objects.filter(mess_option = 'mess2',meal_time='WL').update(dish = wl) - Menu.objects.filter(mess_option = 'mess2',meal_time='WD').update(dish = wd) - Menu.objects.filter(mess_option = 'mess2',meal_time='THB').update(dish = thb) - Menu.objects.filter(mess_option = 'mess2',meal_time='THL').update(dish = thl) - Menu.objects.filter(mess_option = 'mess2',meal_time='THD').update(dish = thd) - Menu.objects.filter(mess_option = 'mess2',meal_time='FB').update(dish = fb) - Menu.objects.filter(mess_option = 'mess2',meal_time='FL').update(dish = fl) - Menu.objects.filter(mess_option = 'mess2',meal_time='FD').update(dish = fd) - Menu.objects.filter(mess_option = 'mess2',meal_time='SB').update(dish = sb) - Menu.objects.filter(mess_option = 'mess2',meal_time='SL').update(dish = sl) - Menu.objects.filter(mess_option = 'mess2',meal_time='SD').update(dish = sd) - Menu.objects.filter(mess_option = 'mess2',meal_time='SUB').update(dish = sub) - Menu.objects.filter(mess_option = 'mess2',meal_time='SUL').update(dish = sul) - Menu.objects.filter(mess_option = 'mess2',meal_time='SUD').update(dish = sud) - - - return redirect('/mess') -def update_menu1(request): - if (request.method == "POST"): - mb1 = request.POST['MB1'] - ml1 = request.POST['ML1'] - md1 = request.POST['MD1'] - sud1 = request.POST['SUD1'] - sul1 = request.POST['SUL1'] - sub1 = request.POST['SUB1'] - sd1 = request.POST['SD1'] - sl1 = request.POST['SL1'] - sb1 = request.POST['SB1'] - fd1 = request.POST['FD1'] - fl1 = request.POST['FL1'] - fb1 = request.POST['FB1'] - thd1 = request.POST['THD1'] - thl1 = request.POST['THL1'] - thb1 = request.POST['THB1'] - wd1 = request.POST['WD1'] - wl1 = request.POST['WL1'] - wb1 = request.POST['WB1'] - td1 = request.POST['TD1'] - tl1 = request.POST['TL1'] - tb1 = request.POST['TB1'] - - print("mb", mb1) - Menu.objects.filter(mess_option = 'mess1',meal_time='MB').update(dish = mb1) - Menu.objects.filter(mess_option = 'mess1',meal_time='ML').update(dish = ml1) - Menu.objects.filter(mess_option = 'mess1',meal_time='MD').update(dish = md1) - Menu.objects.filter(mess_option = 'mess1',meal_time='TB').update(dish = tb1) - Menu.objects.filter(mess_option = 'mess1',meal_time='TL').update(dish = tl1) - Menu.objects.filter(mess_option = 'mess1',meal_time='TD').update(dish = td1) - Menu.objects.filter(mess_option = 'mess1',meal_time='WB').update(dish = wb1) - Menu.objects.filter(mess_option = 'mess1',meal_time='WL').update(dish = wl1) - Menu.objects.filter(mess_option = 'mess1',meal_time='WD').update(dish = wd1) - Menu.objects.filter(mess_option = 'mess1',meal_time='THB').update(dish = thb1) - Menu.objects.filter(mess_option = 'mess1',meal_time='THL').update(dish = thl1) - Menu.objects.filter(mess_option = 'mess1',meal_time='THD').update(dish = thd1) - Menu.objects.filter(mess_option = 'mess1',meal_time='FB').update(dish = fb1) - Menu.objects.filter(mess_option = 'mess1',meal_time='FL').update(dish = fl1) - Menu.objects.filter(mess_option = 'mess1',meal_time='FD').update(dish = fd1) - Menu.objects.filter(mess_option = 'mess1',meal_time='SB').update(dish = sb1) - Menu.objects.filter(mess_option = 'mess1',meal_time='SL').update(dish = sl1) - Menu.objects.filter(mess_option = 'mess1',meal_time='SD').update(dish = sd1) - Menu.objects.filter(mess_option = 'mess1',meal_time='SUB').update(dish = sub1) - Menu.objects.filter(mess_option = 'mess1',meal_time='SUL').update(dish = sul1) - Menu.objects.filter(mess_option = 'mess1',meal_time='SUD').update(dish = sud1) - - return redirect('/mess') - -@csrf_exempt -def searchAddOrRemoveStudent(request): - if request.method=='GET': - submitType=request.GET.get('type') - msg="" - if submitType=='searchStudent': - studentId=str((request.GET.get('roll_number'))).upper() - try: - reg_main = Reg_main.objects.values('current_mess_status','mess_option').get(student_id=studentId) - if(reg_main['current_mess_status']=="Registered"): - msg= str(studentId)+" is registered for "+str(reg_main['mess_option']) - else: - msg=str(studentId)+" is not registered for Mess" - except: - msg="unable to find this student in database." - - # try: - # mess_optn = Messinfo.objects.select_related().values('mess_option').get(student_id=studentId) - # msg= str(studentId)+" is registered for "+str(mess_optn['mess_option']) - # except: - # msg=str(studentId)+" is not registered for Mess" - - elif submitType=='addStudent': - messNo=request.GET.get('messNo') - studentId = str((request.GET.get('roll_number'))).upper() - try: - reg_main = Reg_main.objects.get(student_id=studentId) - - if(reg_main.current_mess_status=="Registered"): - msg=str(studentId)+" is already registered for "+str(reg_main.mess_option) - else: - reg_main.current_mess_status="Registered" - reg_main.mess_option=str(messNo) - reg_main.save() - msg="success" - except: - msg="unable to find this student in database." - - # try: - # mess_optn = Messinfo.objects.select_related().values('mess_option').get(student_id=studentId) - # msg=str(studentId)+" is already registered for "+str(mess_optn['mess_option']) - # except: - # try: - # studentHere = Student.objects.select_related('id','id__user','id__department').get(id=studentId) - # newData=Messinfo(student_id=studentHere,mess_option=str(messNo)) - # newData.save() - # msg=str(studentId)+" is successfully registered for Mess." - # except: - # msg="unable to find this student in database." - - elif submitType=='removeStudent': - studentId = str((request.GET.get('roll_number'))).upper() - try: - reg_main = Reg_main.objects.get(student_id=studentId,current_mess_status="Registered") - reg_main.current_mess_status="Deregistered" - reg_main.save() - msg=str(studentId)+" is successfully removed from mess." - except: - msg=str(studentId)+" is not registered for mess." - - # try: - # studentHere = Student.objects.select_related('id','id__user','id__department').get(id=studentId) - # data=Messinfo.objects.get(student_id=studentId) - # data.delete() - # Messinfo.objects.all() - # msg=str(studentId)+" is successfully removed from mess." - # except: - # msg=str(studentId)+" is not registered for mess." - elif (submitType=='removeAllStudent1' or submitType=='removeAllStudent2'): - messNo=request.GET.get('mess') - - try: - reg_main = Reg_main.objects.filter(mess_option=str(messNo),current_mess_status="Registered") - for reg in reg_main: - reg.current_mess_status="Deregistered" - reg.save() - msg="All students removed successfully from "+str(messNo) - except: - msg="can't remove students." - - return JsonResponse({'message':msg}) - else: - if(request.FILES): - # if 'excelUpload1' in request.POST: - # messNo='mess1' - # excel_file = request.FILES['excel_file1'] - # else: - # messNo='mess2' - # excel_file = request.FILES['excel_file2'] - try: - latest = Semdates.objects.latest('end_date') - latest_end_date=latest.end_date - print(latest_end_date) - except: - latest_end_date=None - excel_file = request.FILES['excel_file1'] - wb = openpyxl.load_workbook(excel_file) - flag = False - for row in wb.active: - if(flag==False): - flag=True - continue - studentId=(str(row[0].value)).upper() - studentHere = Student.objects.select_related('id','id__user','id__department').get(id=studentId) - balance=row[1].value - messNo = row[2].value - try: - reg_main = Reg_main.objects.get(student_id=studentId) - reg_main.current_mess_status="Registered" - reg_main.mess_option=str(messNo) - reg_main.balance=reg_main.balance+balance - reg_main.save() - # if Messinfo.objects.filter(student_id=studentId).exists(): - # Messinfo.objects.filter(student_id=studentId).update(mess_option=str(messNo)) - # else: - # newData=Messinfo(student_id=studentHere,mess_option=str(messNo)) - # newData.save() - except: - reg_main = Reg_main(student_id=reg_main.student_id,program=studentHere.programme,current_mess_status="Registered",mess_option=str(messNo),balance=balance) - reg_main.save() - - new_reg_record = Reg_records(student_id=reg_main.student_id,start_date=today_g,end_date=latest_end_date) - new_reg_record.save() - # messages.success(request,"Done.") - return HttpResponseRedirect("/mess") - -@csrf_exempt -def uploadPaymentDue(request): - if(request.FILES): - - excel_file = request.FILES['excel_file'] - wb = openpyxl.load_workbook(excel_file) - - for row in wb.active: - studentId=(str(row[0].value)).upper() - amount=(row[1].value) - try: - studentHere = Student.objects.get(id=studentId) - monthly_bill = Monthly_bill.objects.select_related('student_id').filter(student_id=studentHere) - try: - Payments.objects.filter(student_id=studentHere).delete() - except: - 1 - Payments.objects.create(student_id=studentHere,amount_paid=(-1*(amount))) - if amount<=0: - for x in monthly_bill: - Monthly_bill.objects.filter(student_id=studentHere).filter(month=x.month).filter(year=x.year).update(paid=True) - else: - monthly_bill=monthly_bill[::-1] - curr_amount=amount - for x in monthly_bill: - if(curr_amount<=0): - Monthly_bill.objects.filter(student_id=studentHere).filter(month=x.month).filter(year=x.year).update(paid=True) - else: - Monthly_bill.objects.filter(student_id=studentHere).filter(month=x.month).filter(year=x.year).update(paid=False) - curr_amount-=x.total_bill; - print(x) - except: - 1 - messages.success(request,"Done.") - return HttpResponseRedirect("/mess") - - -@csrf_exempt -@login_required -def respond_to_reg(request): - """ - This function is used to respond to registeration requests - - @param request: - request - contains metadata about the requested page - - @variables: - user: Current user details - designation : designation of the user - - @return: - data: returns the status of the application - """ - data = { - 'status': 1 - } - user = request.user - designation = HoldsDesignation.objects.select_related().filter(user=user) - type = request.POST['type'] - for d in designation: - if d.designation.name == 'mess_manager': - if(type=='reg'): - data = handle_reg_response(request) - elif(type=='dreg'): - data = handle_dreg_response(request) - return JsonResponse(data) - -@csrf_exempt -@login_required -def respond_to_update_payment(request): - data = { - 'status': 1 - } - user = request.user - designation = HoldsDesignation.objects.select_related().filter(user=user) - for d in designation: - if d.designation.name == 'mess_manager': - data = handle_update_payment_response(request) - - return JsonResponse(data) - - - -def reg_request(request): - - user = request.user - extra_info = ExtraInfo.objects.select_related().get(user=user) - try: - if request.POST['input_roll']: - # print(request.POST) - studentID = str(request.POST['input_roll']).upper() - handle_add_reg(request) - form = RegistrationRequest(request.POST, request.FILES) - student = Student.objects.select_related('id','id__user','id__department').get(id=studentID) - if form.is_valid(): - temp=form.save(commit=False) - temp.student_id=student - temp.status='accept' - temp.save() - return HttpResponseRedirect("/mess") - except: - student = Student.objects.select_related('id','id__user','id__department').get(id=extra_info) - if request.method == 'POST': - form = RegistrationRequest(request.POST, request.FILES) - - if form.is_valid(): - temp=form.save(commit=False) - temp.student_id=student - temp.save() - return HttpResponseRedirect("/mess") - - -def update_payment(request): - user = request.user - extra_info = ExtraInfo.objects.select_related().get(user=user) - student = Student.objects.select_related('id','id__user','id__department').get(id=extra_info) - if request.method == 'POST': - form = UpdatePaymentRequest(request.POST, request.FILES) - - if form.is_valid(): - temp=form.save(commit=False) - temp.student_id=student - temp.save() - return HttpResponseRedirect("/mess") - -@csrf_exempt -def update_bill_excel(request): - if(request.FILES): - excel_file = request.FILES['excel_file_bill'] - wb = openpyxl.load_workbook(excel_file) - flag = False - for row in wb.active: - if(flag==False): - flag=True - continue - studentId=(str(row[0].value)).upper() - studentHere = Student.objects.select_related('id','id__user','id__department').get(id=studentId) - month=str(row[1].value) - year = row[2].value - amt = row[3].value - rebate_cnt = row[4].value - rebate_amt = row[5].value - total_amt = row[6].value - try: - bill = Monthly_bill.objects.get(student_id=studentId,month=month,year=year) - reg_main = Reg_main.objects.get(student_id=studentId) - reg_main.balance=reg_main.balance+bill.total_bill - bill.amount=amt - bill.rebate_count=rebate_cnt - bill.rebate_amount=rebate_amt - bill.total_bill=total_amt - reg_main.balance=reg_main.balance-total_amt - bill.save() - reg_main.save() - except: - bill = Monthly_bill(student_id=studentHere,month=month,year=year,amount=amt,rebate_count=rebate_cnt,rebate_amount=rebate_amt,total_bill=total_amt) - bill.save() - # messages.success(request,"Done.") - return HttpResponseRedirect("/mess") - -def de_reg_request(request): - try: - if request.POST['input_roll']: - # print(request.POST) - studentID = str(request.POST['input_roll']).upper() - end_date = request.POST.get("end_date") - try: - reg_main = Reg_main.objects.get(student_id=studentID) - - if(end_date == str(date.today())): - reg_main.current_mess_status = 'Deregistered' - reg_main.save() - reg_record = Reg_records.objects.filter(student_id=studentID).latest('start_date') - reg_record.end_date=end_date - reg_record.save() - except: - pass - return HttpResponseRedirect('/mess') - except: - data={ - 'message':'request submitted successfully' - } - user = request.user - end_date = request.POST.get("end_date") - print(end_date) - extra_info = ExtraInfo.objects.select_related().get(user=user) - student = Student.objects.select_related('id','id__user','id__department').get(id=extra_info) - new_req=Deregistration_Request(student_id=student, end_date=end_date) - new_req.save() - return HttpResponseRedirect('/mess') - diff --git a/FusionIIIT/applications/complaint_system/admin.py b/FusionIIIT/applications/complaint_system/admin.py index 8bf43cab7..84314fcd9 100644 --- a/FusionIIIT/applications/complaint_system/admin.py +++ b/FusionIIIT/applications/complaint_system/admin.py @@ -1,9 +1,8 @@ from django.contrib import admin -from .models import Caretaker, StudentComplain, Supervisor, Workers, SectionIncharge +from .models import Caretaker, StudentComplain, Supervisor, Workers admin.site.register(Caretaker) admin.site.register(Workers) admin.site.register(StudentComplain) admin.site.register(Supervisor) -admin.site.register(SectionIncharge) diff --git a/FusionIIIT/applications/complaint_system/api/views.py b/FusionIIIT/applications/complaint_system/api/views.py index 304697017..612c6573c 100644 --- a/FusionIIIT/applications/complaint_system/api/views.py +++ b/FusionIIIT/applications/complaint_system/api/views.py @@ -23,7 +23,7 @@ def complaint_details_api(request,detailcomp_id1): if complaint_detail.worker_id is None: worker_detail_serialized = {} else : - worker_detail = Workers.objects.get(id=complaint_detail.worker_id.id) + worker_detail = worker_detail.objects.get(id=complaint_detail.worker_id) worker_detail_serialized = serializers.WorkersSerializers(instance=worker_detail).data complainer = User.objects.get(username=complaint_detail.complainer.user.username) complainer_serialized = serializers.UserSerializers(instance=complainer).data @@ -154,7 +154,7 @@ def caretaker_api(request): user = get_object_or_404(User ,username=request.user.username) user = ExtraInfo.objects.all().filter(user = user).first() try : - supervisor = Supervisor.objects.get(sup_id=user) + supervisor = Supervisor.objects.get(staff_id=user) except Supervisor.DoesNotExist: return Response({'message':'Logged in user does not have the permissions'},status=status.HTTP_203_NON_AUTHORITATIVE_INFORMATION) serializer = serializers.CaretakerSerializers(data=request.data) @@ -170,7 +170,7 @@ def edit_caretaker_api(request,c_id): user = get_object_or_404(User ,username=request.user.username) user = ExtraInfo.objects.all().filter(user = user).first() try : - supervisor = Supervisor.objects.get(sup_id=user) + supervisor = Supervisor.objects.get(staff_id=user) except Supervisor.DoesNotExist: return Response({'message':'Logged in user does not have the permissions'},status=status.HTTP_203_NON_AUTHORITATIVE_INFORMATION) try: diff --git a/FusionIIIT/applications/complaint_system/migrations/0001_initial.py b/FusionIIIT/applications/complaint_system/migrations/0001_initial.py index e40ba5606..db74dd14d 100644 --- a/FusionIIIT/applications/complaint_system/migrations/0001_initial.py +++ b/FusionIIIT/applications/complaint_system/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 3.1.5 on 2024-07-16 15:44 +# Generated by Django 3.1.5 on 2023-03-15 18:53 from django.db import migrations, models import django.db.models.deletion @@ -15,10 +15,12 @@ class Migration(migrations.Migration): operations = [ migrations.CreateModel( - name='SectionIncharge', + name='Caretaker', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('work_type', models.CharField(choices=[('Electricity', 'Electricity'), ('carpenter', 'carpenter'), ('plumber', 'plumber'), ('garbage', 'garbage'), ('dustbin', 'dustbin'), ('internet', 'internet'), ('other', 'other')], default='Electricity', max_length=20)), + ('area', models.CharField(choices=[('hall-1', 'hall-1'), ('hall-3', 'hall-3'), ('hall-4', 'hall-4'), ('CC1', 'CC1'), ('CC2', 'CC2'), ('core_lab', 'core_lab'), ('LHTC', 'LHTC'), ('NR2', 'NR2'), ('Rewa_Residency', 'Rewa_Residency'), ('Maa Saraswati Hostel', 'Maa Saraswati Hostel'), ('Nagarjun Hostel', 'Nagarjun Hostel'), ('Panini Hostel', 'Panini Hostel')], default='hall-3', max_length=20)), + ('rating', models.IntegerField(default=0)), + ('myfeedback', models.CharField(default='this is my feedback', max_length=400)), ('staff_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='globals.extrainfo')), ], ), @@ -30,14 +32,14 @@ class Migration(migrations.Migration): ('age', models.CharField(max_length=10)), ('phone', models.BigIntegerField(blank=True)), ('worker_type', models.CharField(choices=[('Electricity', 'Electricity'), ('carpenter', 'carpenter'), ('plumber', 'plumber'), ('garbage', 'garbage'), ('dustbin', 'dustbin'), ('internet', 'internet'), ('other', 'other')], default='internet', max_length=20)), - ('secincharge_id', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='complaint_system.sectionincharge')), + ('caretaker_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='complaint_system.caretaker')), ], ), migrations.CreateModel( name='Supervisor', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('type', models.CharField(choices=[('Electricity', 'Electricity'), ('carpenter', 'carpenter'), ('plumber', 'plumber'), ('garbage', 'garbage'), ('dustbin', 'dustbin'), ('internet', 'internet'), ('other', 'other')], default='Electricity', max_length=30)), + ('area', models.CharField(choices=[('hall-1', 'hall-1'), ('hall-3', 'hall-3'), ('hall-4', 'hall-4'), ('CC1', 'CC1'), ('CC2', 'CC2'), ('core_lab', 'core_lab'), ('LHTC', 'LHTC'), ('NR2', 'NR2'), ('Rewa_Residency', 'Rewa_Residency'), ('Maa Saraswati Hostel', 'Maa Saraswati Hostel'), ('Nagarjun Hostel', 'Nagarjun Hostel'), ('Panini Hostel', 'Panini Hostel')], max_length=20)), ('sup_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='globals.extrainfo')), ], ), @@ -48,7 +50,7 @@ class Migration(migrations.Migration): ('complaint_date', models.DateTimeField(default=django.utils.timezone.now)), ('complaint_finish', models.DateField(blank=True, null=True)), ('complaint_type', models.CharField(choices=[('Electricity', 'Electricity'), ('carpenter', 'carpenter'), ('plumber', 'plumber'), ('garbage', 'garbage'), ('dustbin', 'dustbin'), ('internet', 'internet'), ('other', 'other')], default='internet', max_length=20)), - ('location', models.CharField(choices=[('hall-1', 'hall-1'), ('hall-3', 'hall-3'), ('hall-4', 'hall-4'), ('library', 'CC1'), ('computer center', 'CC2'), ('core_lab', 'core_lab'), ('LHTC', 'LHTC'), ('NR2', 'NR2'), ('NR3', 'NR3'), ('Admin building', 'Admin building'), ('Rewa_Residency', 'Rewa_Residency'), ('Maa Saraswati Hostel', 'Maa Saraswati Hostel'), ('Nagarjun Hostel', 'Nagarjun Hostel'), ('Panini Hostel', 'Panini Hostel')], max_length=20)), + ('location', models.CharField(choices=[('hall-1', 'hall-1'), ('hall-3', 'hall-3'), ('hall-4', 'hall-4'), ('CC1', 'CC1'), ('CC2', 'CC2'), ('core_lab', 'core_lab'), ('LHTC', 'LHTC'), ('NR2', 'NR2'), ('Rewa_Residency', 'Rewa_Residency'), ('Maa Saraswati Hostel', 'Maa Saraswati Hostel'), ('Nagarjun Hostel', 'Nagarjun Hostel'), ('Panini Hostel', 'Panini Hostel')], max_length=20)), ('specific_location', models.CharField(blank=True, max_length=50)), ('details', models.CharField(max_length=100)), ('status', models.IntegerField(default='0')), @@ -62,14 +64,4 @@ class Migration(migrations.Migration): ('worker_id', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='complaint_system.workers')), ], ), - migrations.CreateModel( - name='Caretaker', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('area', models.CharField(choices=[('hall-1', 'hall-1'), ('hall-3', 'hall-3'), ('hall-4', 'hall-4'), ('library', 'CC1'), ('computer center', 'CC2'), ('core_lab', 'core_lab'), ('LHTC', 'LHTC'), ('NR2', 'NR2'), ('NR3', 'NR3'), ('Admin building', 'Admin building'), ('Rewa_Residency', 'Rewa_Residency'), ('Maa Saraswati Hostel', 'Maa Saraswati Hostel'), ('Nagarjun Hostel', 'Nagarjun Hostel'), ('Panini Hostel', 'Panini Hostel')], default='hall-3', max_length=20)), - ('rating', models.IntegerField(default=0)), - ('myfeedback', models.CharField(default='this is my feedback', max_length=400)), - ('staff_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='globals.extrainfo')), - ], - ), ] diff --git a/FusionIIIT/applications/complaint_system/models.py b/FusionIIIT/applications/complaint_system/models.py index 47145fad3..9f8849e69 100644 --- a/FusionIIIT/applications/complaint_system/models.py +++ b/FusionIIIT/applications/complaint_system/models.py @@ -12,13 +12,11 @@ class Constants: ('hall-1', 'hall-1'), ('hall-3', 'hall-3'), ('hall-4', 'hall-4'), - ('library', 'CC1'), - ('computer center', 'CC2'), + ('CC1', 'CC1'), + ('CC2', 'CC2'), ('core_lab', 'core_lab'), ('LHTC', 'LHTC'), ('NR2', 'NR2'), - ('NR3', 'NR3'), - ('Admin building', 'Admin building'), ('Rewa_Residency', 'Rewa_Residency'), ('Maa Saraswati Hostel', 'Maa Saraswati Hostel'), ('Nagarjun Hostel', 'Nagarjun Hostel'), @@ -44,18 +42,11 @@ class Caretaker(models.Model): # no_of_comps = models.CharField(max_length=1000) def __str__(self): - return str(self.id) + '-' + str(self.area) + return str(self.id) + '-' + self.area -class SectionIncharge(models.Model): - staff_id = models.ForeignKey(ExtraInfo, on_delete=models.CASCADE) - work_type = models.CharField(choices=Constants.COMPLAINT_TYPE, - max_length=20, default='Electricity') - - def __str__(self): - return str(self.id) + '-' + self.work_type class Workers(models.Model): - secincharge_id = models.ForeignKey(SectionIncharge, on_delete=models.CASCADE, null=True) + caretaker_id = models.ForeignKey(Caretaker, on_delete=models.CASCADE) name = models.CharField(max_length=50) age = models.CharField(max_length=10) phone = models.BigIntegerField(blank=True) @@ -91,7 +82,7 @@ def __str__(self): class Supervisor(models.Model): sup_id = models.ForeignKey(ExtraInfo, on_delete=models.CASCADE) - type = models.CharField(choices=Constants.COMPLAINT_TYPE, max_length=30,default='Electricity') + area = models.CharField(choices=Constants.AREA, max_length=20) def __str__(self): - return str(self.sup_id) + '-' + str(self.type) + return str(self.sup_id.user.username) diff --git a/FusionIIIT/applications/complaint_system/static/complaint_system/js/rating.js b/FusionIIIT/applications/complaint_system/static/complaint_system/js/rating.js index 825fe759d..3884f885b 100644 --- a/FusionIIIT/applications/complaint_system/static/complaint_system/js/rating.js +++ b/FusionIIIT/applications/complaint_system/static/complaint_system/js/rating.js @@ -58,93 +58,6 @@ $(document).ready(function(){ } }; - function sub2(event) - { - var specific_location= $('input[name="specific_location"]').val(); - var Location = $('input[name="Location"]').val(); - var complaint_type = $('input[name="complaint_type"]').val() ; - var details =$('input[name="details"]').val() ; - var myfile = $('input[name="myfile"]').val(); - if(specific_location=="" || Location=="" || details=="" || complaint_type=="") - { - alert("Please fill all the details!"); - return; - } - else - { - event.preventDefault(); - $.ajax({ - type : 'POST', - url : '.', - data : { - 'specific_location' : specific_location, - 'Location' : Location, - 'complaint_type' : complaint_type, - 'details' : details, - 'myfile' : myfile, - - }, - success : function (data){ - - // alert("Complaint successfully lodged"); - setTimeout(function() { - window.location.replace('http://localhost:8000/complaint/caretaker'); - }, 1500); - - - }, - error : function (data,err){ - alert('Complaint successfully lodged ... '); - - } - }); - } - }; - function sub3(event) - { - var specific_location= $('input[name="specific_location"]').val(); - var Location = $('input[name="Location"]').val(); - var complaint_type = $('input[name="complaint_type"]').val() ; - var details =$('input[name="details"]').val() ; - var myfile = $('input[name="myfile"]').val(); - if(specific_location=="" || Location=="" || details=="" || complaint_type=="") - { - alert("Please fill all the details!"); - return; - } - else - { - event.preventDefault(); - $.ajax({ - type : 'POST', - url : '.', - data : { - 'specific_location' : specific_location, - 'Location' : Location, - 'complaint_type' : complaint_type, - 'details' : details, - 'myfile' : myfile, - - }, - success : function (data){ - - // alert("Complaint successfully lodged"); - setTimeout(function() { - window.location.replace('http://localhost:8000/complaint/supervisor'); - }, 1500); - - - }, - error : function (data,err){ - alert('Complaint successfully lodged ... '); - - } - }); - } - }; - - - @@ -854,98 +767,3 @@ $.fn.rating.settings = { }; })( jQuery, window, document ); - - -function paginate(tableId, rowsPerPage, paginationDiv) { - var currentPage = 1; - var tableRows = document.querySelectorAll('#' + tableId + ' tbody tr'); - var totalPages = Math.ceil(tableRows.length / rowsPerPage); - - function displayRows() { - var startIndex = (currentPage - 1) * rowsPerPage; - var endIndex = startIndex + rowsPerPage; - tableRows.forEach(function (row, index) { - if (index >= startIndex && index < endIndex) { - row.style.display = ''; - } else { - row.style.display = 'none'; - } - }); - } - - function generatePagination() { - var pagination = document.getElementById(paginationDiv); - pagination.innerHTML = ''; - - var maxPagesToShow = 5; - - // Calculate the range of pages to display - var startPage = Math.max(1, currentPage - Math.floor(maxPagesToShow / 2)); - var endPage = Math.min(totalPages, startPage + maxPagesToShow - 1); - - // Adjust startPage and endPage if needed - if (endPage - startPage < maxPagesToShow - 1) { - startPage = Math.max(1, endPage - maxPagesToShow + 1); - } - - // Create the backward scroll button - if (currentPage > 1) { - pagination.appendChild(createScrollButton('<')); - } - - // Create the page links - for (var i = startPage; i <= endPage; i++) { - pagination.appendChild(createPageLink(i)); - } - - // Create the forward scroll button - if (currentPage < totalPages) { - pagination.appendChild(createScrollButton('>')); - } -} - -function createScrollButton(label) { - var button = document.createElement('button'); - button.textContent = label; - button.addEventListener('click', function () { - if (label === '<') { - currentPage = Math.max(1, currentPage - 1); - } else { - currentPage = Math.min(totalPages, currentPage + 1); - } - displayRows(); - generatePagination(); - }); - return button; -} - - - function createPageLink(pageNumber) { - var link = document.createElement('a'); - link.href = '#'; - link.textContent = pageNumber; - link.style.display = 'inline-block'; - link.style.padding = '5px 10px'; - link.style.marginRight = '5px'; - link.style.color = '#333'; - link.style.textDecoration = 'none'; - link.style.border = '1px solid #ccc'; - link.style.borderRadius = '3px'; - - if (pageNumber === currentPage) { - link.style.backgroundColor = '#007bff'; - link.style.color = '#fff'; - } - - link.addEventListener('click', function () { - currentPage = parseInt(this.textContent); - displayRows(); - generatePagination(); - }); - - return link; - } - - displayRows(); - generatePagination(); -} \ No newline at end of file diff --git a/FusionIIIT/applications/complaint_system/urls.py b/FusionIIIT/applications/complaint_system/urls.py index 2bd0c87e0..b95605ade 100644 --- a/FusionIIIT/applications/complaint_system/urls.py +++ b/FusionIIIT/applications/complaint_system/urls.py @@ -9,30 +9,33 @@ url(r'^$', views.check, name='complaint'), # url(r'^login/$', views.login1, name='complaint'), url(r'^user/$', views.user), + url(r'^user/caretakerfb/$' , views.caretaker_feedback), + url(r'^user/(?P[0-9]+)/$', views.submitfeedback), url(r'^user/detail/(?P[0-9]+)/$', views.detail,name='detail'), - # url(r'^user/check_complaint/$', views.save_comp), + # url(r'^user/check_complaint/$', views.save_comp), + # caretaker - url(r'^caretaker/lodge/$', views.caretakerlodge), url(r'^caretaker/$', views.caretaker, name='caretaker'), url(r'^caretaker/feedback/(?P[0-9]+)/$', views.feedback_care), + url(r'^caretaker/worker_id_know_more/(?P[0-9]+)/complaint_reassign/(?P[0-9]+)/discharge_worker/$', views.discharge_worker,name='discharge_worker'), + url(r'^caretaker/worker_id_know_more/(?P[0-9]+)/$', views.worker_id_know_more, name='come_back_to_this'), + url(r'^caretaker/worker_id_know_more/(?P[0-9]+)/complaint_reassign/(?P[0-9]+)/$', views.complaint_reassign), + #url(r'^caretaker/list_caretakers_area/$', views.caretaker, name='caretaker'), url(r'^caretaker/pending/(?P[0-9]+)/$', views.resolvepending), - url(r'^caretaker/detail2/(?P[0-9]+)/$', views.detail), + url(r'^caretaker/detail2/(?P[0-9]+)/$', views.detail2,name='detail2'), url(r'^caretaker/search_complaint$', views.search_complaint), - url(r'^caretaker/(?P[0-9]+)/feedback/$', views.submitfeedbackcaretaker), # supervisor - url(r'^supervisor/lodge/$', views.supervisorlodge), url(r'^supervisor/$', views.supervisor), url(r'^supervisor/feedback/(?P[0-9]+)/$', views.feedback_super), url(r'^supervisor/caretaker_id_know_more/(?P[0-9]+)/$', views.caretaker_id_know_more), - # url(r'^supervisor/caretaker_id_know_more/(?P[0-9]+)/complaint_reassign_super/(?P[0-9]+)/$', views.complaint_reassign_super, name = 'complaint_reassign_super'), - url(r'^supervisor/detail/(?P[0-9]+)/$', views.detail3, name = 'detail3'), - url(r'^supervisor/pending/(?P[0-9]+)/$', views.resolvependingsuper), - url(r'^supervisor/(?P[0-9]+)/$', views.submitfeedbacksuper), + url(r'^supervisor/caretaker_id_know_more/(?P[0-9]+)/complaint_reassign_super/(?P[0-9]+)/$', views.complaint_reassign_super, name = 'complaint_reassign_super'), + url(r'^supervisor/detail3/(?P[0-9]+)/$', views.detail3, name = 'detail3'), + @@ -42,7 +45,6 @@ url(r'^caretaker/deletecomplaint/(?P[0-9]+)/$', views.deletecomplaint), # url(r'^caretaker/(?P[0-9]+)/$', views.assign_worker), url(r'^caretaker/(?P[0-9]+)/(?P[0-9]+)/$', views.changestatus), - url(r'^supervisor/(?P[0-9]+)/(?P[0-9]+)/$', views.changestatussuper), url(r'^api/',include('applications.complaint_system.api.urls')) diff --git a/FusionIIIT/applications/complaint_system/views.py b/FusionIIIT/applications/complaint_system/views.py index 9d3dd7316..b566856aa 100644 --- a/FusionIIIT/applications/complaint_system/views.py +++ b/FusionIIIT/applications/complaint_system/views.py @@ -10,85 +10,96 @@ from applications.globals.models import User , ExtraInfo, HoldsDesignation from notifications.models import Notification -from .models import Caretaker, StudentComplain, Supervisor, Workers, SectionIncharge +from .models import Caretaker, StudentComplain, Supervisor, Workers from notification.views import complaint_system_notif - - -from applications.filetracking.sdk.methods import * -from applications.filetracking.models import * -from operator import attrgetter - #function for reassign to another worker -# @login_required -# def complaint_reassign(request,wid,iid): - # current_user = get_object_or_404(User, username=request.user.username) - # y = ExtraInfo.objects.all().select_related('user','department').filter(user=current_user).first() - # if request.method == 'POST': - # type = request.POST.get('submit', '') - # a = get_object_or_404(User, username=request.user.username) - # y = ExtraInfo.objects.all().select_related('user','department').filter(user=a).first() - # comp_id = y.id - # if type == 'assign': - - # complaint_finish = request.POST.get('complaint_finish', '') - # worker_id = request.POST.get('assign_worker', '') - # w = Workers.objects.select_related('secincharge_id','secincharge_id__staff_id','secincharge_id__staff_id__user','secincharge_id__staff_id__department').get(id=worker_id) - # StudentComplain.objects.select_for_update().filter(id=iid).\ - # update(worker_id=w, status=1) - # url = '/complaint/secincharge/worker_id_know_more/'+wid; - # complainer_details = StudentComplain.objects.select_related('complainer','complainer__user','complainer__department').get(id=iid) - # student=0 - # message = "Your complaint has been re-assigned" - # complaint_system_notif(request.user, complainer_details.complainer.user ,'reassign_worker_alert',complainer_details.id,student,message) - # return HttpResponseRedirect(url) +@login_required +def complaint_reassign(request,wid,iid): + current_user = get_object_or_404(User, username=request.user.username) + y = ExtraInfo.objects.all().select_related('user','department').filter(user=current_user).first() + if request.method == 'POST': + type = request.POST.get('submit', '') + a = get_object_or_404(User, username=request.user.username) + y = ExtraInfo.objects.all().select_related('user','department').filter(user=a).first() + comp_id = y.id + if type == 'assign': + + complaint_finish = request.POST.get('complaint_finish', '') + worker_id = request.POST.get('assign_worker', '') + w = Workers.objects.select_related('caretaker_id','caretaker_id__staff_id','caretaker_id__staff_id__user','caretaker_id__staff_id__department').get(id=worker_id) + StudentComplain.objects.select_for_update().filter(id=iid).\ + update(worker_id=w, status=1) + url = '/complaint/caretaker/worker_id_know_more/'+wid; + complainer_details = StudentComplain.objects.select_related('complainer','complainer__user','complainer__department','worker_id','worker_id__caretaker_id__staff_id','worker_id__caretaker_id__staff_id__user','worker_id__caretaker_id__staff_id__department').get(id=iid) + student=0 + message = "Your complaint has been re-assigned" + complaint_system_notif(request.user, complainer_details.complainer.user ,'reassign_worker_alert',complainer_details.id,student,message) + return HttpResponseRedirect(url) + elif type == 'redirect': + assign_caretaker = request.POST.get('assign_caretaker', '') + c = Caretaker.objects.select_related('staff_id','staff_id__user','staff_id__department').get(id=assign_caretaker) + c1 = Caretaker.objects.select_related('staff_id','staff_id__user','staff_id__department').get(id=iid) + remark = 'Redirect complaint from ' + c1.area + StudentComplain.objects.select_for_update().filter(id=iid).\ + update(location=c.area, remarks=remark) + url = '/complaint/caretaker/worker_id_know_more/'+wid; + complainer_details = StudentComplain.objects.select_related('complainer','complainer__user','complainer__department','worker_id','worker_id__caretaker_id__staff_id','worker_id__caretaker_id__staff_id__user','worker_id__caretaker_id__staff_id__department').get(id=iid) + student=0 + message = "Your complaint has been redirected to another caretaker" + complaint_system_notif(request.user, complainer_details.complainer.user ,'comp_redirect_alert',complainer_details.id,student,message) + return HttpResponseRedirect(url) - # else: - # y = ExtraInfo.objects.all().select_related('user','department').get(id=y.id) - # a = SectionIncharge.objects.select_related('staff_id','staff_id__user','staff_id__department').get(staff_id=y) - # b = a.work_type - # comp_id = y.id - # try: - # detail = StudentComplain.objects.select_related('complainer','complainer__user','complainer__department').filter(id=iid).first() - # total_secincharge = SectionIncharge.objects.select_related('staff_id','staff_id__user','staff_id__department').all() - # total_secincharges_in_area = SectionIncharge.objects.select_related('staff_id','staff_id__user','staff_id__department').filter(work_type=b) - # worker = [] - # workertemp = [] - # flag = '' - # temp = detail.location - # try: + else: + y = ExtraInfo.objects.all().select_related('user','department').get(id=y.id) + a = Caretaker.objects.select_related('staff_id','staff_id__user','staff_id__department').get(staff_id=y) + b = a.area + comp_id = y.id + try: + detail = StudentComplain.objects.select_related('complainer','complainer__user','complainer__department','worker_id','worker_id__caretaker_id__staff_id','worker_id__caretaker_id__staff_id__user','worker_id__caretaker_id__staff_id__department').filter(id=iid).first() + total_caretaker = Caretaker.objects.select_related('staff_id','staff_id__user','staff_id__department').all() + total_caretakers_in_area = Caretaker.objects.select_related('staff_id','staff_id__user','staff_id__department').filter(area=b) + worker = [] + workertemp = [] + flag = '' + temp = detail.location + try: - # if Workers.objects.select_related('secincharge_id','secincharge_id__staff_id','secincharge_id__staff_id__user','secincharge_id__staff_id__department').filter(secincharge_id=a).count() == 0: - # flag = 'no_worker' - # else: - # workertemp = Workers.objects.select_related('secincharge_id','secincharge_id__staff_id','secincharge_id__staff_id__user','secincharge_id__staff_id__department').filter(secincharge_id=a) - # j = 1 - # for i in workertemp: - # worker.append(i) - - # except SectionIncharge.DoesNotExist: - # flag = 'no_worker' - - # except StudentComplain.DoesNotExist: - # return HttpResponse("

Not a valid complaint

") - # return render(request, "complaintModule/reassignworker.html", - # {'detail': detail, 'worker': worker, 'flag': - # flag, 'total_secincharge': total_secincharge,'a':a, 'wid':wid, 'total_secincharges_in_area':total_secincharges_in_area}) - - - -# @login_required -# def complaint_reassign_super(request,caretaker_id,iid): - # current_user = get_object_or_404(User, username=request.user.username) - # y = ExtraInfo.objects.all().select_related('user','department').filter(user=current_user).first() - # sup = Supervisor.objects.select_related('sup_id','sup_id__user','sup_id__department').get(sup_id = y) - # this_area = sup.area - # if request.method == 'POST': - # a = get_object_or_404(User, username=request.user.username) - # y = ExtraInfo.objects.all().select_related('user','department').filter(user=a).first() - # comp_id = y.id - - -#for SectionIncharge + if Workers.objects.select_related('caretaker_id','caretaker_id__staff_id','caretaker_id__staff_id__user','caretaker_id__staff_id__department').filter(caretaker_id=a).count() == 0: + flag = 'no_worker' + else: + workertemp = Workers.objects.select_related('caretaker_id','caretaker_id__staff_id','caretaker_id__staff_id__user','caretaker_id__staff_id__department').filter(caretaker_id=a) + j = 1 + for i in workertemp: + worker.append(i) + # if j%2 != 0: + # worker.append(i) + # j = j + 1 + + + except Caretaker.DoesNotExist: + flag = 'no_worker' + + except StudentComplain.DoesNotExist: + return HttpResponse("

Not a valid complaint

") + return render(request, "complaintModule/reassignworker.html", + {'detail': detail, 'worker': worker, 'flag': + flag, 'total_caretaker': total_caretaker,'a':a, 'wid':wid, 'total_caretakers_in_area':total_caretakers_in_area}) + +@login_required +def complaint_reassign_super(request,caretaker_id,iid): + current_user = get_object_or_404(User, username=request.user.username) + y = ExtraInfo.objects.all().select_related('user','department').filter(user=current_user).first() + sup = Supervisor.objects.select_related('sup_id','sup_id__user','sup_id__department').get(sup_id = y) + this_area = sup.area + if request.method == 'POST': + a = get_object_or_404(User, username=request.user.username) + y = ExtraInfo.objects.all().select_related('user','department').filter(user=a).first() + comp_id = y.id + + + + + @login_required def assign_worker(request, comp_id1): current_user = get_object_or_404(User, username=request.user.username) @@ -110,89 +121,88 @@ def assign_worker(request, comp_id1): a = get_object_or_404(User, username=request.user.username) y = ExtraInfo.objects.all().select_related('user','department').filter(user=a).first() comp_id = y.id - - - complaint_details = StudentComplain.objects.all().filter(id=comp_id1) - - - - complaint_type=complaint_details[0].complaint_type - - supervisor=Supervisor.objects.all().filter(type=complaint_type) - if not supervisor.exists(): - return HttpResponse("

Supervisor does not exist of this complaint type

") - - supervisor_details=ExtraInfo.objects.all().filter(id=supervisor[0].sup_id.id) - - StudentComplain.objects.select_related('complainer','complainer__user','complainer__department','worker_id','worker_id__caretaker_id__staff_id','worker_id__caretaker_id__staff_id__user','worker_id__caretaker_id__staff_id__department').filter(id=comp_id1).\ - update(status=1) - - sup = HoldsDesignation.objects.select_related('user','working','designation').filter(user = supervisor_details[0].user_id) - - - files=File.objects.all().filter(src_object_id=comp_id1) - - supervisor_username=User.objects.all().filter(id=supervisor_details[0].user_id) - file=forward_file(file_id= files.first().id, - receiver=supervisor_username[0].username, - receiver_designation=sup[0].designation, - file_extra_JSON= {}, - remarks = "", - file_attachment= None) - print(file) - - - return HttpResponseRedirect('/complaint/caretaker/') + if type == 'assign': + complaint_finish = request.POST.get('complaint_finish', '') + worker_id = request.POST.get('assign_worker', '') + w = Workers.objects.select_related('caretaker_id','caretaker_id__staff_id','caretaker_id__staff_id__user','caretaker_id__staff_id__department').get(id=worker_id) + StudentComplain.objects.select_related('complainer','complainer__user','complainer__department','worker_id','worker_id__caretaker_id__staff_id','worker_id__caretaker_id__staff_id__user','worker_id__caretaker_id__staff_id__department').select_for_update().filter(id=comp_id1).\ + update(worker_id=w, status=1) + complainer_details = StudentComplain.objects.select_related('complainer','complainer__user','complainer__department','worker_id','worker_id__caretaker_id__staff_id','worker_id__caretaker_id__staff_id__user','worker_id__caretaker_id__staff_id__department').get(id=comp_id1) + student = 0 + message = "Worker has been assigned to your complaint" + complaint_system_notif(request.user, complainer_details.complainer.user ,'assign_worker_alert',complainer_details.id,student,message) + + return HttpResponseRedirect('/complaint/caretaker/') + elif type == 'redirect': + assign_caretaker = request.POST.get('assign_caretaker', '') + c = Caretaker.objects.select_related('staff_id','staff_id__user','staff_id__department').get(id=assign_caretaker) + c1 = Caretaker.objects.select_related('staff_id','staff_id__user','staff_id__department').get(id=comp_id) + remark = 'Redirect complaint from ' + c1.area + StudentComplain.objects.select_for_update().filter(id=comp_id1).\ + update(location=c.area, remarks=remark) + complainer_details = StudentComplain.objects.select_related('complainer','complainer__user','complainer__department','worker_id','worker_id__caretaker_id__staff_id','worker_id__caretaker_id__staff_id__user','worker_id__caretaker_id__staff_id__department').get(id=comp_id1) + student=0 + message = "Your Complaint has been redirected to another caretaker" + complaint_system_notif(request.user, complainer_details.complainer.user ,'comp_redirect_alert',complainer_details.id,student,message) + return HttpResponseRedirect('/complaint/caretaker/') else: y = ExtraInfo.objects.all().select_related('user','department').get(id=y.id) - # a = SectionIncharge.objects.select_related('staff_id','staff_id__user','staff_id__department').get(staff_id=y) - + a = Caretaker.objects.select_related('staff_id','staff_id__user','staff_id__department').get(staff_id=y) + b = a.area comp_id = y.id try: - detail = StudentComplain.objects.select_related('complainer','complainer__user','complainer__department').filter(id=comp_id1).first() - # total_caretaker = Caretaker.objects.select_related('staff_id','staff_id__user','staff_id__department').all() - # total_caretakers_in_area = Supervisor.objects.select_related('sup_id') - # supervisors_in_area= HoldsDesignation.objects.select_related('user','working','designation').get(total_caretakers_in_area = dsgn) - # workertemp = [] - # worker = [] - # flag = '' - # temp = detail.location - # try: - # if Workers.objects.select_related('secincharge_id','secincharge_id__staff_id','secincharge_id__staff_id__user','secincharge_id__staff_id__department').filter(secincharge_id=a).count() == 0: - # flag = 'no_worker' - # else: - # workertemp1 = Workers.objects.select_related('secincharge_id','secincharge_id__staff_id','secincharge_id__staff_id__user','secincharge_id__staff_id__department').filter(secincharge_id=a) - # workertemp = workertemp1.filter(worker_type=detail.complaint_type) - # j = 1 - # for i in workertemp: - # worker.append(i) - - # except SectionIncharge.DoesNotExist: - # flag = 'no_worker' + detail = StudentComplain.objects.select_related('complainer','complainer__user','complainer__department','worker_id','worker_id__caretaker_id__staff_id','worker_id__caretaker_id__staff_id__user','worker_id__caretaker_id__staff_id__department').filter(id=comp_id1).first() + total_caretaker = Caretaker.objects.select_related('staff_id','staff_id__user','staff_id__department').all() + total_caretakers_in_area = Caretaker.objects.select_related('staff_id','staff_id__user','staff_id__department').filter(area=b and id!=a.id) + workertemp = [] + worker = [] + flag = '' + temp = detail.location + try: + #care = Caretaker.objects.filter(area=temp).first() + if Workers.objects.select_related('caretaker_id','caretaker_id__staff_id','caretaker_id__staff_id__user','caretaker_id__staff_id__department').filter(caretaker_id=a).count() == 0: + flag = 'no_worker' + else: + workertemp1 = Workers.objects.select_related('caretaker_id','caretaker_id__staff_id','caretaker_id__staff_id__user','caretaker_id__staff_id__department').filter(caretaker_id=a) + workertemp = workertemp1.filter(worker_type=detail.complaint_type) + j = 1 + for i in workertemp: + worker.append(i) + # if j%2 != 0: + # worker.append(i) + # j = j + 1 + + + except Caretaker.DoesNotExist: + flag = 'no_worker' except StudentComplain.DoesNotExist: return HttpResponse("

Not a valid complaint

") return render(request, "complaintModule/assignworker.html", - {'detail': detail}) - + {'detail': detail, 'worker': worker, 'flag': + flag, 'total_caretaker': total_caretaker,'a':a, 'total_caretakers_in_area':total_caretakers_in_area}) -#for SectionIncharge @login_required def discharge_worker(request,wid,cid): current_user = get_object_or_404(User, username=request.user.username) y = ExtraInfo.objects.all().select_related('user','department').filter(user=current_user).first() - this_worker = Workers.objects.select_related('secincharge_id','secincharge_id__staff_id','secincharge_id__staff_id__user','secincharge_id__staff_id__department').get(id=wid) - com_in_concern= StudentComplain.objects.select_related('complainer','complainer__user','complainer__department').get(id=cid); + this_worker = Workers.objects.select_related('caretaker_id','caretaker_id__staff_id','caretaker_id__staff_id__user','caretaker_id__staff_id__department').get(id=wid) + com_in_concern= StudentComplain.objects.select_related('complainer','complainer__user','complainer__department','worker_id','worker_id__caretaker_id__staff_id','worker_id__caretaker_id__staff_id__user','worker_id__caretaker_id__staff_id__department').get(id=cid); com_in_concern.worker_id=None; com_in_concern.status=0; StudentComplain.objects.select_for_update().filter(id=cid).\ update(worker_id=None, status=0) - url='/complaint/secincharge/detail2/'+cid; + #StudentComplain.objects.get(id=cid).delete() + url='/complaint/caretaker/detail2/'+cid; return HttpResponseRedirect(url) + + + + @login_required def caretaker_feedback(request): """ @@ -224,23 +234,23 @@ def caretaker_feedback(request): return render(request, "complaintModule/submit_feedback_caretaker.html", {'a': a}) -#for SectionIncharge @login_required def worker_id_know_more(request, work_id): """ function to know pending complaints assigned to the worker """ - this_worker = Workers.objects.select_related('secincharge_id','secincharge_id__staff_id','secincharge_id__staff_id__user','secincharge_id__staff_id__department').get(id=work_id) - num = StudentComplain.objects.select_related('complainer','complainer__user','complainer__department').filter(worker_id=this_worker).count(); - complaints_list = StudentComplain.objects.select_related('complainer','complainer__user','complainer__department').filter(worker_id=this_worker); + this_worker = Workers.objects.select_related('caretaker_id','caretaker_id__staff_id','caretaker_id__staff_id__user','caretaker_id__staff_id__department').get(id=work_id) + num = StudentComplain.objects.select_related('complainer','complainer__user','complainer__department','worker_id','worker_id__caretaker_id__staff_id','worker_id__caretaker_id__staff_id__user','worker_id__caretaker_id__staff_id__department').filter(worker_id=this_worker).count(); + complaints_list = StudentComplain.objects.select_related('complainer','complainer__user','complainer__department','worker_id','worker_id__caretaker_id__staff_id','worker_id__caretaker_id__staff_id__user','worker_id__caretaker_id__staff_id__department').filter(worker_id=this_worker); complaints_list_onhold = [] for i in complaints_list: if i.status == 1: complaints_list_onhold.append(i) numpend = len(complaints_list_onhold) - work_under_secincharge1 = this_worker.secincharge_id.staff_id.user.first_name - work_under_secincharge2 = this_worker.secincharge_id.staff_id.user.last_name - return render(request, "complaintModule/worker_id_know_more.html",{'this_worker':this_worker,'work_under_secincharge1':work_under_secincharge1,'work_under_secincharge2':work_under_secincharge2, 'num':num, 'complaints_list':complaints_list, 'complaints_list_onhold':complaints_list_onhold, 'numpend':numpend}) + work_under_caretaker1 = this_worker.caretaker_id.staff_id.user.first_name + work_under_caretaker2 = this_worker.caretaker_id.staff_id.user.last_name + return render(request, "complaintModule/worker_id_know_more.html",{'this_worker':this_worker,'work_under_caretaker1':work_under_caretaker1,'work_under_caretaker2':work_under_caretaker2, 'num':num, 'complaints_list':complaints_list, 'complaints_list_onhold':complaints_list_onhold, 'numpend':numpend}) + @@ -273,31 +283,12 @@ def check(request): print('----------------------------') print('----------------------------') print('----------------------------') - supervisor_list=Supervisor.objects.all() - caretaker_list=Caretaker.objects.all() - is_supervisor=False - is_caretaker=False - for i in supervisor_list: - if b.id==i.sup_id_id: - is_supervisor=True - break - for i in caretaker_list: - if b.id==i.staff_id_id: - is_caretaker=True - break - if is_supervisor: - return HttpResponseRedirect('/complaint/supervisor/') - elif is_caretaker: - return HttpResponseRedirect('/complaint/caretaker/') - - elif b.user_type == 'student': + if b.user_type == 'student': return HttpResponseRedirect('/complaint/user/') - # elif b.user_type == 'fx': - # return HttpResponseRedirect('/complaint/supervisor/') elif b.user_type == 'staff': - return HttpResponseRedirect('/complaint/user/') + return HttpResponseRedirect('/complaint/caretaker/') elif b.user_type == 'faculty': - return HttpResponseRedirect('/complaint/user/') + return HttpResponseRedirect('/complaint/supervisor/') else: return HttpResponse("

wrong user credentials

") else: @@ -347,11 +338,20 @@ def user(request): complaint_finish = datetime.now() + timedelta(days=4) elif comp_type == 'other': complaint_finish = datetime.now() + timedelta(days=3) - + y = ExtraInfo.objects.all().select_related('user','department').get(id=comp_id) + #check if location given if location!="": - - user_details=User.objects.get(id=y.user_id) - + # x = StudentComplain(complainer=y, + # complaint_type=comp_type, + # location=location, + # specific_location=specific_location, + # details=details, + # status=status, + # complaint_finish=complaint_finish, + # upload_complaint=comp_file) + + + # x.save() obj1, created = StudentComplain.objects.get_or_create(complainer=y, complaint_type=comp_type, location=location, @@ -361,11 +361,27 @@ def user(request): complaint_finish=complaint_finish, upload_complaint=comp_file) + + historytemp = StudentComplain.objects.select_related('complainer','complainer__user','complainer__department','worker_id','worker_id__caretaker_id__staff_id','worker_id__caretaker_id__staff_id__user','worker_id__caretaker_id__staff_id__department').filter(complainer=y).order_by('-id') + history = [] + j = 1 + k = 1 + for i in historytemp: + history.append(i) + # if j%2 != 0: + # history.append(i) + # j = j+1 - - - - + + for h in history: + h.serial_no = k + k = k+1 + # if location == "hall1": + # dsgn = "hall1caretaker" + # elif location == "hall3": + # dsgn = "hall3caretaker" + # else : + # dsgn = "hall4caretaker" if location == "hall-1": dsgn ="hall1caretaker" elif location =="hall-3": @@ -392,38 +408,24 @@ def user(request): dsgn = "rewacaretaker" caretaker_name = HoldsDesignation.objects.select_related('user','working','designation').get(designation__name = dsgn) - c1=HoldsDesignation.objects.filter(user_id=y.user_id).all() - print(c1[0].designation) - file_id = create_file(uploader=user_details.username, - uploader_designation=c1[0].designation, - receiver=caretaker_name.user.username, - receiver_designation=caretaker_name.designation, - src_module="complaint", - src_object_id= str(obj1.id), - file_extra_JSON= {}, - attached_file = None) - - # print(" wertyuioiuhygfdsdfghjk") - print(file_id) # This is to allow the student - student = 0 + student = 1 message = "A New Complaint has been lodged" complaint_system_notif(request.user, caretaker_name.user,'lodge_comp_alert',obj1.id,student,message) - # complaint_system_notif(request.user, secincharge_name.staff_id.user,'lodge_comp_alert',obj1.id,1,message) - - messages.success(request,message) - + # return render(request, "complaintModule/complaint_user.html", + # {'history': history, 'comp_id': comp_id }) + # next = request.POST.get('next', '/') + messages.success(request,message) return HttpResponseRedirect('/complaint/user') else: a = get_object_or_404(User, username=request.user.username) y = ExtraInfo.objects.all().select_related('user','department').filter(user=a).first() - - user_details=User.objects.get(id=y.user_id) - + historytemp = StudentComplain.objects.select_related('complainer','complainer__user','complainer__department','worker_id','worker_id__caretaker_id__staff_id','worker_id__caretaker_id__staff_id__user','worker_id__caretaker_id__staff_id__department').filter(complainer=y).order_by('-id') + history=[] notification = Notification.objects.filter(recipient=a.id) notification = notification.filter(data__exact={'url':'complaint:detail','module':'Complaint System'}) @@ -435,54 +437,45 @@ def user(request): # notification_message.append(notification.verb+' by '+ to + ' ' + duration + ' ago ') - c1=HoldsDesignation.objects.filter(user_id=y.user_id).all() - print(c1[0].designation) - # c2=Designation.objects.filter(i) - - - - outbox_files = view_outbox( - username=user_details.username, - designation=c1[0].designation, - src_module="complaint" - ) - print(outbox_files) - - outbox=[] - comp_list=set() - for i in outbox_files: - - outbox.append(i) - print(outbox) - for i in outbox: - file_history = view_history(file_id=i['id']) - print(i['id']) - comp=File.objects.filter(id=i['id']) - print(comp[0].src_object_id) - complaint=StudentComplain.objects.all().filter(id=comp[0].src_object_id) - print(complaint) - if complaint[0].complainer.user.username == user_details.username : - comp_list.add(complaint) - # file_history = view_history(file_id=i['id']) - - # comp=File.objects.filter(uploader=file_history[0]['current_id']) - # for j in comp: - # c=StudentComplain.objects.all().filter(id=j.src_object_id) - # comp_list.add(c) - # print(c[0]) - - # break - complaint_final_list=[] - for i in comp_list: - complaint_final_list.append(i[0]) + + j = 1 + for i in historytemp: + history.append(i) + # if j%2 != 0: + # history.append(i) + # j = j+1 + + for i in history: + i.serial_no = j + j = j+1 + + # if location == "hall-1": + # dsgn ="hall1caretaker" + # elif location =="hall-3": + # dsgn ="hall3caretaker" + # elif location =="hall-4": + # dsgn ="hall4caretaker" + # elif location =="CC1": + # dsgn ="CC convenor" + # elif location =="CC2": + # dsgn ="CC2 convener" + # elif location == "core_lab": + # dsgn = "corelabcaretaker" + # elif location =="LHTC": + # dsgn ="lhtccaretaker" + # elif location =="NR2": + # dsgn ="nr2caretaker" + # else: + # dsgn = "rewacaretaker" + # caretaker_name = HoldsDesignation.objects.get(designation__name = dsgn) - sorted_history = sorted(complaint_final_list, key=attrgetter('complaint_date'), reverse=True) - print(complaint_final_list) + # complaint_system_notif(request.user, caretaker_name.user,'lodge_comp_alert') return render(request, "complaintModule/complaint_user.html", - {'outbox': sorted_history,'notification':notification, 'comp_id': y.id, 'history':outbox}) - + {'history': history,'notification':notification, 'comp_id': y.id}) + return render(request, "complaintModule/complaint_user.html", + {'history': history, 'comp_id': comp_id }) @login_required def save_comp(request): """ @@ -530,15 +523,14 @@ def save_comp(request): upload_complaint =comp_file) x.save() - + # messages.info(request,'Complaint successfully launched.') + # return HttpResponseRedirect('/complaint/user/') return HttpResponseRedirect('/complaint/user/') - - @login_required def caretaker(request): """ - The function is used to display details to the caretaker such as registered complaints + The function is used to display details to the caretaker such as registered complaints and allows to assign workers @param: request - trivial. @@ -551,21 +543,21 @@ def caretaker(request): """ current_user = get_object_or_404(User, username=request.user.username) y = ExtraInfo.objects.all().select_related('user','department').filter(user=current_user).first() - + if request.method == 'POST': - # type = request.POST.get('submit', '') - # worker_type = request.POST.get('complaint_type', '') - # name = request.POST.get('name', '') - # phone = request.POST.get('phone_no', '') - # age = request.POST.get('age', '') - # try: - # y = ExtraInfo.objects.all().select_related('user','department').get(id=y.id) - # a = Caretaker.objects.select_related('staff_id','staff_id__user','staff_id__department').get(staff_id=y) - # except Exception as e: - # a = None - # y = None - # intage = int(age) - # intphone = int(phone) + type = request.POST.get('submit', '') + worker_type = request.POST.get('complaint_type', '') + name = request.POST.get('name', '') + phone = request.POST.get('phone_no', '') + age = request.POST.get('age', '') + try: + y = ExtraInfo.objects.all().select_related('user','department').get(id=y.id) + a = Caretaker.objects.select_related('staff_id','staff_id__user','staff_id__department').get(staff_id=y) + except Exception as e: + a = None + y = None + intage = int(age) + intphone = int(phone) # if len(phone) == 10 and intage > 20 and intage < 50 and intphone > 1999999999: # x = Workers(caretaker_id=a, # name=name, @@ -575,252 +567,116 @@ def caretaker(request): # if not Workers.objects.filter(caretaker_id=a,name=name, age=age,phone=phone,worker_type=worker_type).exists(): # x.save() - # if len(phone) == 10 and intage > 20 and intage < 50 and intphone > 1999999999: - # obj, created = Workers.objects.get_or_create(caretaker_id=a, - # name=name, - # age=age, - # phone=phone, - # worker_type=worker_type) + if len(phone) == 10 and intage > 20 and intage < 50 and intphone > 1999999999: + obj, created = Workers.objects.get_or_create(caretaker_id=a, + name=name, + age=age, + phone=phone, + worker_type=worker_type) + + b = a.area + historytemp = StudentComplain.objects.select_related('complainer','complainer__user','complainer__department','worker_id','worker_id__caretaker_id__staff_id','worker_id__caretaker_id__staff_id__user','worker_id__caretaker_id__staff_id__department').filter(location=b).order_by('-id') + history = [] + j = 1 + k = 1 + for i in historytemp: + history.append(i) + # if j%2 == 1: + # history.append(i) + # j = j+1 + for h in history: + h.serial_no = k + k=k+1 - # b = a.area - # historytemp = StudentComplain.objects.select_related('complainer','complainer__user','complainer__department','worker_id','worker_id__caretaker_id__staff_id','worker_id__caretaker_id__staff_id__user','worker_id__caretaker_id__staff_id__department').filter(location=b).order_by('-id') - # history = [] - # j = 1 - # k = 1 - # for i in historytemp: - # history.append(i) - # # if j%2 == 1: - # # history.append(i) - # # j = j+1 - - # for h in history: - # h.serial_no = k - # k=k+1 - user_details=User.objects.get(id=y.user_id) - # if user_details.username=="shyams": - # desgn="hall3caretaker" - # if user_details.username=="hall4caretaker": - # desgn="hall4caretaker" - - # total_worker = [] - - # total_workertemp = Workers.objects.select_related('caretaker_id','caretaker_id__staff_id','caretaker_id__staff_id__user','caretaker_id__staff_id__department').filter(caretaker_id=a) - # j = 1 + total_worker = [] + total_workertemp = Workers.objects.select_related('caretaker_id','caretaker_id__staff_id','caretaker_id__staff_id__user','caretaker_id__staff_id__department').filter(caretaker_id=a) + j = 1 # for i in total_workertemp: # if j%2 != 0: # total_worker.append(i) # j = j + 1 - # for i in total_workertemp: - # total_worker.append(i) + for i in total_workertemp: + total_worker.append(i) complaint_assign_no = [] - comp_type = request.POST.get('complaint_type', '') - location = request.POST.get('Location', '') - specific_location = request.POST.get('specific_location', '') - comp_file = request.FILES.get('myfile') - - details = request.POST.get('details', '') - status = 0 - # finish time is according to complaint type - complaint_finish = datetime.now() + timedelta(days=2) - if comp_type == 'Electricity': - complaint_finish = datetime.now() + timedelta(days=2) - elif comp_type == 'carpenter': - complaint_finish = datetime.now() + timedelta(days=2) - elif comp_type == 'plumber': - complaint_finish = datetime.now() + timedelta(days=2) - elif comp_type == 'garbage': - complaint_finish = datetime.now() + timedelta(days=1) - elif comp_type == 'dustbin': - complaint_finish = datetime.now() + timedelta(days=1) - elif comp_type == 'internet': - complaint_finish = datetime.now() + timedelta(days=4) - elif comp_type == 'other': - complaint_finish = datetime.now() + timedelta(days=3) - # y = ExtraInfo.objects.all().select_related('user','department').get(id=comp_id) - #check if location given - if location!="": - user_details=User.objects.get(id=y.user_id) - obj1, created = StudentComplain.objects.get_or_create(complainer=y, - complaint_type=comp_type, - location=location, - specific_location=specific_location, - details=details, - status=status, - complaint_finish=complaint_finish, - upload_complaint=comp_file) - - - if location == "hall-1": - dsgn ="hall1caretaker" - elif location =="hall-3": - dsgn ="hall3caretaker" - elif location =="hall-4": - dsgn ="hall4caretaker" - elif location =="CC1": - dsgn ="cc1convener" - elif location =="CC2": - dsgn ="CC2 convener" - elif location == "core_lab": - dsgn = "corelabcaretaker" - elif location =="LHTC": - dsgn ="lhtccaretaker" - elif location =="NR2": - dsgn ="nr2caretaker" - elif location =="Maa Saraswati Hostel": - dsgn ="mshcaretaker" - elif location =="Nagarjun Hostel": - dsgn ="nhcaretaker" - elif location =="Panini Hostel": - dsgn ="phcaretaker" - else: - dsgn = "rewacaretaker" - caretaker_name = HoldsDesignation.objects.select_related('user','working','designation').get(designation__name = dsgn) - print(caretaker_name.user.username) - print(user_details.username) - print(caretaker_name.designation) - - user_details=User.objects.get(id=y.user_id) - des=HoldsDesignation.objects.filter(user=user_details).all() - file_id = create_file(uploader=user_details.username, - uploader_designation=des[0].designation, - receiver=caretaker_name.user.username, - receiver_designation=dsgn, - src_module="complaint", - src_object_id= str(obj1.id), - file_extra_JSON= {}, - attached_file = None) - - - # This is to allow the student - student = 1 - message = "A New Complaint has been lodged" - complaint_system_notif(request.user, caretaker_name.user,'lodge_comp_alert',obj1.id,student,message) - - # return render(request, "complaintModule/complaint_user.html", - # {'history': history, 'comp_id': comp_id }) - # next = request.POST.get('next', '/') - - messages.success(request,message) - # return HttpResponseRedirect('/complaint/user') - - # for x in total_worker: - # worker = Workers.objects.select_related('caretaker_id','caretaker_id__staff_id','caretaker_id__staff_id__user','caretaker_id__staff_id__department').get(id=x.id) - # temp = StudentComplain.objects.select_related('complainer','complainer__user','complainer__department','worker_id','worker_id__caretaker_id__staff_id','worker_id__caretaker_id__staff_id__user','worker_id__caretaker_id__staff_id__department').filter(worker_id=worker).count() - # worker.total_complaint = temp - # complaint_assign_no.append(worker) + for x in total_worker: + worker = Workers.objects.select_related('caretaker_id','caretaker_id__staff_id','caretaker_id__staff_id__user','caretaker_id__staff_id__department').get(id=x.id) + temp = StudentComplain.objects.select_related('complainer','complainer__user','complainer__department','worker_id','worker_id__caretaker_id__staff_id','worker_id__caretaker_id__staff_id__user','worker_id__caretaker_id__staff_id__department').filter(worker_id=worker).count() + worker.total_complaint = temp + complaint_assign_no.append(worker) notification = Notification.objects.filter(recipient=current_user.id) notification = notification.filter(data__exact={'url':'complaint:detail2','module':'Complaint System'}) - - return HttpResponseRedirect('/complaint/caretaker') + return render(request, "complaintModule/complaint_caretaker.html", + {'history': history, 'comp_id': y.id, + 'notification': notification, 'total_worker': + total_worker, 'complaint_assign_no': complaint_assign_no}) else: - # y = ExtraInfo.objects.all().select_related('user','department').get(id=y.id) - - a = Caretaker.objects.select_related('staff_id','staff_id__user','staff_id__department').get(staff_id=y.id) + y = ExtraInfo.objects.all().select_related('user','department').get(id=y.id) + a = Caretaker.objects.select_related('staff_id','staff_id__user','staff_id__department').get(staff_id=y) b = a.area history = [] - - - complaint_assign_no = [] - user_details=User.objects.get(id=y.user_id) - - notification = Notification.objects.filter(recipient=current_user.id) - notification = notification.filter(data__exact={'url':'complaint:detail2','module':'Complaint System'}) - user_details=User.objects.get(id=y.user_id) - - - des=HoldsDesignation.objects.filter(user=user_details).all() - print("######") - print(user_details.username) - print(des[0].designation) - print("&&&&&") - outbox_files = view_outbox( - username=user_details.username, - designation=des[0].designation, - src_module="complaint" - ) - - outbox=[] - comp_list=set() - for i in outbox_files: - # print(i) - outbox.append(i) - - for i in outbox: - file_history = view_history(file_id=i['id']) - print(i['id']) - print("********") - comp=File.objects.filter(id=i['id']) - print(comp[0].src_object_id) - print("------") - complaint=StudentComplain.objects.all().filter(id=comp[0].src_object_id) - print(complaint[0].complainer.user.username) - - print("......") - if complaint[0].complainer.user.username== user_details.username : - comp_list.add(complaint) + historytemp = StudentComplain.objects.select_related('complainer','complainer__user','complainer__department','worker_id','worker_id__caretaker_id__staff_id','worker_id__caretaker_id__staff_id__user','worker_id__caretaker_id__staff_id__department').filter(location=b).order_by('-id') + total_worker = [] + total_workertemp = Workers.objects.select_related('caretaker_id','caretaker_id__staff_id','caretaker_id__staff_id__user','caretaker_id__staff_id__department').filter(caretaker_id=a) + j = 1 + for i in total_workertemp: + total_worker.append(i) - # break - complaint_final_list=[] - for i in comp_list: - complaint_final_list.append(i[0]) - - sorted_history_out = sorted(complaint_final_list, key=attrgetter('complaint_date'), reverse=True) - - inbox_files = view_inbox( - username=user_details.username, - designation=des[0].designation, - src_module="complaint" - ) - print(inbox_files) - - inbox=[] - comp_list_in=set() - for i in inbox_files: - # print(i) - inbox.append(i) - file_history_list=[] - for i in inbox: - file_history = view_history(file_id=i['id']) - print(i['id']) - comp=File.objects.filter(id=i['id']) - print(comp[0].src_object_id) - complaint=StudentComplain.objects.all().filter(id=comp[0].src_object_id) - print(complaint) - comp_list_in.add(complaint) - - complaint_final_list_in=[] - for i in comp_list_in: - complaint_final_list_in.append(i[0]) + complaint_assign_no = [] + complaint_assign_no = [] + + for x in total_worker: + worker = Workers.objects.select_related('caretaker_id','caretaker_id__staff_id','caretaker_id__staff_id__user','caretaker_id__staff_id__department').get(id=x.id) + temp = StudentComplain.objects.select_related('complainer','complainer__user','complainer__department','worker_id','worker_id__caretaker_id__staff_id','worker_id__caretaker_id__staff_id__user','worker_id__caretaker_id__staff_id__department').filter(worker_id=worker).count() + worker.total_complaint = temp + complaint_assign_no.append(worker) + + overduecomplaint = [] + j = 1 + k = 1 + for i in historytemp: + history.append(i) + # if j%2 != 0: + # history.append(i) + # j=j+1 + for i in history: + i.serial_no = k + k = k + 1 - # print(complaint_final_list_in) - sorted_history = sorted(complaint_final_list_in, key=attrgetter('complaint_date'), reverse=True) + if i.status != 2 and i.status !=3: + if i.complaint_finish < date.today(): + i.delay = date.today() - i.complaint_finish + overduecomplaint.append(i) + + notification = Notification.objects.filter(recipient=current_user.id) + notification = notification.filter(data__exact={'url':'complaint:detail2','module':'Complaint System'}) + + return render(request, "complaintModule/complaint_caretaker.html", - { 'history': sorted_history, - 'comp_id': y.id, - 'carehistory':sorted_history_out, - 'notification':notification, - 'care_id': a}) + { 'history': history, 'comp_id': y.id, 'total_worker': total_worker, + 'complaint_assign_no': total_worker, + 'notification':notification, + 'overduecomplaint': overduecomplaint, 'care_id': a}) @login_required def remove_worker_from_complaint(request,complaint_id): """ - The function is used by secincharge to remove a worker + The function is used by caretaker to remove a worker already assigned to a complaint @param: request - trivial complaint_id - used to get complaint_id registered """ - complaint = StudentComplain.objects.select_related('complainer','complainer__user','complainer__department').filter(complaint_id=complaint_id).update(worker_id='') + complaint = StudentComplain.objects.select_related('complainer','complainer__user','complainer__department','worker_id','worker_id__caretaker_id__staff_id','worker_id__caretaker_id__staff_id__user','worker_id__caretaker_id__staff_id__department').filter(complaint_id=complaint_id).update(worker_id='') return HttpResponseRedirect('/complaint/caretaker/') @@ -835,37 +691,6 @@ def changestatus(request, complaint_id, status): complaint_id - used to get complaint_id registered. status-used to get the current status of complaints - @variables: - issue - The issue object. - supported - True if the user's intention is to support the issue. - support_count - Total supporters of the above issue. - context - Holds data needed to make necessary changes in the template. - """ - if status == '3': - StudentComplain.objects.select_related('complainer','complainer__user','complainer__department').filter(id=complaint_id).\ - update(status=status, worker_id='') - return HttpResponseRedirect('/complaint/caretaker/') - elif status == '2': - StudentComplain.objects.select_related('complainer','complainer__user','complainer__department').filter(id=complaint_id).\ - update(status=status, worker_id='') - return HttpResponseRedirect('/complaint/caretaker/') - else: - StudentComplain.objects.select_related('complainer','complainer__user','complainer__department').filter(id=complaint_id).\ - update(status=status) - return HttpResponseRedirect('/complaint/caretaker/') - - - - -@login_required -def changestatussuper(request, complaint_id, status): - """ - The function is used by caretaker to change the status of a complaint. - @param: - request - trivial. - complaint_id - used to get complaint_id registered. - status-used to get the current status of complaints - @variables: issue - The issue object. supported - True if the user's intention is to support the issue. @@ -875,21 +700,21 @@ def changestatussuper(request, complaint_id, status): if status == '3': StudentComplain.objects.select_related('complainer','complainer__user','complainer__department','worker_id','worker_id__caretaker_id__staff_id','worker_id__caretaker_id__staff_id__user','worker_id__caretaker_id__staff_id__department').filter(id=complaint_id).\ update(status=status, worker_id='') - return HttpResponseRedirect('/complaint/supervisor/') + return HttpResponseRedirect('/complaint/caretaker/') elif status == '2': StudentComplain.objects.select_related('complainer','complainer__user','complainer__department','worker_id','worker_id__caretaker_id__staff_id','worker_id__caretaker_id__staff_id__user','worker_id__caretaker_id__staff_id__department').filter(id=complaint_id).\ update(status=status, worker_id='') - return HttpResponseRedirect('/complaint/supervisor/') + return HttpResponseRedirect('/complaint/caretaker/') else: StudentComplain.objects.select_related('complainer','complainer__user','complainer__department','worker_id','worker_id__caretaker_id__staff_id','worker_id__caretaker_id__staff_id__user','worker_id__caretaker_id__staff_id__department').filter(id=complaint_id).\ update(status=status) - return HttpResponseRedirect('/complaint/supervisor/') + return HttpResponseRedirect('/complaint/caretaker/') @login_required def removew(request, work_id): """ - The function is used by secincharge to remove workers. + The function is used by caretaker to remove workers. @param: request - trivial. work_id - id of the issue object which the user intends to support/unsupport. @@ -900,18 +725,15 @@ def removew(request, work_id): support_count - Total supporters of the above issue. context - Holds data needed to make necessary changes in the template. """ - worker = Workers.objects.select_related('secincharge_id','secincharge_id__staff_id','secincharge_id__staff_id__user','secincharge_id__staff_id__department').get(id=work_id) - temp = StudentComplain.objects.select_related('complainer','complainer__user','complainer__department').filter(worker_id=worker).count() + worker = Workers.objects.select_related('caretaker_id','caretaker_id__staff_id','caretaker_id__staff_id__user','caretaker_id__staff_id__department').get(id=work_id) + temp = StudentComplain.objects.select_related('complainer','complainer__user','complainer__department','worker_id','worker_id__caretaker_id__staff_id','worker_id__caretaker_id__staff_id__user','worker_id__caretaker_id__staff_id__department').filter(worker_id=worker).count() if temp == 0: worker.delete() - return HttpResponseRedirect('/complaint/secincharge/') + return HttpResponseRedirect('/complaint/caretaker/') else: return HttpResponse('

Worker is assign some complaint

') - - - @login_required def submitfeedback(request, complaint_id): """ @@ -930,9 +752,9 @@ def submitfeedback(request, complaint_id): if request.method == 'POST': feedback = request.POST.get('feedback', '') rating = request.POST.get('rating', '') - StudentComplain.objects.select_related('complainer','complainer__user','complainer__department').filter(id=complaint_id).\ + StudentComplain.objects.select_related('complainer','complainer__user','complainer__department','worker_id','worker_id__caretaker_id__staff_id','worker_id__caretaker_id__staff_id__user','worker_id__caretaker_id__staff_id__department').filter(id=complaint_id).\ update(feedback=feedback, flag=rating) - a = StudentComplain.objects.select_related('complainer','complainer__user','complainer__department').filter(id=complaint_id).first() + a = StudentComplain.objects.select_related('complainer','complainer__user','complainer__department','worker_id','worker_id__caretaker_id__staff_id','worker_id__caretaker_id__staff_id__user','worker_id__caretaker_id__staff_id__department').filter(id=complaint_id).first() care = Caretaker.objects.select_related('staff_id','staff_id__user','staff_id__department').filter(area=a.location).first() rate = care.rating newrate = 0 @@ -946,26 +768,27 @@ def submitfeedback(request, complaint_id): Caretaker.objects.select_related('staff_id','staff_id__user','staff_id__department').filter(area=a.location).update(rating=newrate) return HttpResponseRedirect('/complaint/user/') + return render(request,"complaintModule/feedback.html",{'a' : a}) else: - a = StudentComplain.objects.select_related('complainer','complainer__user','complainer__department').get(id=complaint_id) + a = StudentComplain.objects.select_related('complainer','complainer__user','complainer__department','worker_id','worker_id__caretaker_id__staff_id','worker_id__caretaker_id__staff_id__user','worker_id__caretaker_id__staff_id__department').get(id=complaint_id) return render(request, "complaintModule/submit_feedback.html", {'a': a}) -#for SectionIncharge + + @login_required def deletecomplaint(request, comp_id1): """ function to delete complaint """ StudentComplain.objects.get(id=comp_id1).delete() - return HttpResponseRedirect('/complaint/secincharge/') - - + return HttpResponseRedirect('/complaint/caretaker/') def testEntry(): + # list1 = [('SKM','hall-1'),('HS','hall-3'),('PS','hall-4'),('MSR','Maa Saraswati Hostel'),('KKB','Maa Saraswati Hostel'), ('RP','Nagarjun Hostel'),('DS','Nagarjun Hostel'),('AV','Panini Hostel')] list1 = [('eecivil','NR2'),('eecivil','Rewa_Residency'),('eecivil','LHTC'),('eecivil','core_lab')] # to delete supervisors @@ -997,11 +820,9 @@ def supervisor(request): """ # print("--------------------------") # testEntry() - # print(request.type) - location = request.POST.get('Location', '') current_user = get_object_or_404(User, username=request.user.username) + y = ExtraInfo.objects.all().select_related('user','department').filter(user=current_user).first() - comp_id = y.id if request.method == 'POST' : try: y = ExtraInfo.objects.all().select_related('user','department').get(id=y.id) @@ -1009,231 +830,78 @@ def supervisor(request): except Exception as e: a = None y = None - all_caretaker = Caretaker.objects.select_related('staff_id','staff_id__user','staff_id__department').filter(area=location).order_by('-id') + all_caretaker = Caretaker.objects.select_related('staff_id','staff_id__user','staff_id__department').filter(area=a.area).order_by('-id') area = all_caretaker[0].area # ExtraInfo.objects.get(id=sup_id) all_complaint = [] - numtemp = StudentComplain.objects.select_related('complainer','complainer__user','complainer__department').filter(location = area).filter(status = 0).count() + numtemp = StudentComplain.objects.select_related('complainer','complainer__user','complainer__department','worker_id','worker_id__caretaker_id__staff_id','worker_id__caretaker_id__staff_id__user','worker_id__caretaker_id__staff_id__department').filter(location = area).filter(status = 0).count() num = int(numtemp/2+0.5) - - - - - - - comp_type = request.POST.get('complaint_type', '') - location = request.POST.get('Location', '') - specific_location = request.POST.get('specific_location', '') - comp_file = request.FILES.get('myfile') - - details = request.POST.get('details', '') - status = 0 - # finish time is according to complaint type - complaint_finish = datetime.now() + timedelta(days=2) - if comp_type == 'Electricity': - complaint_finish = datetime.now() + timedelta(days=2) - elif comp_type == 'carpenter': - complaint_finish = datetime.now() + timedelta(days=2) - elif comp_type == 'plumber': - complaint_finish = datetime.now() + timedelta(days=2) - elif comp_type == 'garbage': - complaint_finish = datetime.now() + timedelta(days=1) - elif comp_type == 'dustbin': - complaint_finish = datetime.now() + timedelta(days=1) - elif comp_type == 'internet': - complaint_finish = datetime.now() + timedelta(days=4) - elif comp_type == 'other': - complaint_finish = datetime.now() + timedelta(days=3) - y = ExtraInfo.objects.all().select_related('user','department').get(id=comp_id) - #check if location given - if location!="": - # x = StudentComplain(complainer=y, - # complaint_type=comp_type, - # location=location, - # specific_location=specific_location, - # details=details, - # status=status, - # complaint_finish=complaint_finish, - # upload_complaint=comp_file) - - - # x.save() - obj1, created = StudentComplain.objects.get_or_create(complainer=y, - complaint_type=comp_type, - location=location, - specific_location=specific_location, - details=details, - status=status, - complaint_finish=complaint_finish, - upload_complaint=comp_file) - - - - if location == "hall-1": - dsgn ="hall1caretaker" - elif location =="hall-3": - dsgn ="hall3caretaker" - elif location =="hall-4": - dsgn ="hall4caretaker" - elif location =="CC1": - dsgn ="cc1convener" - elif location =="CC2": - dsgn ="CC2 convener" - elif location == "core_lab": - dsgn = "corelabcaretaker" - elif location =="LHTC": - dsgn ="lhtccaretaker" - elif location =="NR2": - dsgn ="nr2caretaker" - elif location =="Maa Saraswati Hostel": - dsgn ="mshcaretaker" - elif location =="Nagarjun Hostel": - dsgn ="nhcaretaker" - elif location =="Panini Hostel": - dsgn ="phcaretaker" - else: - dsgn = "rewacaretaker" - caretaker_name = HoldsDesignation.objects.select_related('user','working','designation').get(designation__name = dsgn) - user_details=User.objects.get(id=y.user_id) - # c2=Supervisor.objects.all().filter(area=location) - print(caretaker_name.user.username) - print(user_details.username) - print(caretaker_name.designation) - - # sup = HoldsDesignation.objects.select_related('user','working','designation').get(user = y.id) - # print(sup.designation) - - user_details=User.objects.get(id=y.user_id) - des=HoldsDesignation.objects.filter(user=user_details).all() - - - file_id = create_file(uploader=user_details.username, - uploader_designation=des[0].designation, - receiver=caretaker_name.user.username, - receiver_designation=str(caretaker_name.designation), - src_module="complaint", - src_object_id= str(obj1.id), - file_extra_JSON= {}, - attached_file = None) - - - # This is to allow the student - student = 1 - message = "A New Complaint has been lodged" - complaint_system_notif(request.user, caretaker_name.user,'lodge_comp_alert',obj1.id,student,message) - - # return render(request, "complaintModule/complaint_user.html", - # {'history': history, 'comp_id': comp_id }) - # next = request.POST.get('next', '/') - - messages.success(request,message) - # return HttpResponseRedirect('/complaint/user') - - return HttpResponseRedirect('/complaint/supervisor') + all_complainttemp = StudentComplain.objects.select_related('complainer','complainer__user','complainer__department','worker_id','worker_id__caretaker_id__staff_id','worker_id__caretaker_id__staff_id__user','worker_id__caretaker_id__staff_id__department').filter(location=a.area).order_by('-id') + j = 1 + for i in all_complainttemp: + all_complaint.append(i) + # if j%2 != 0: + # all_complaint.append(i) + # j = j + 1 + overduecomplaint = [] + for i in all_complaint: + if i.status != 2 and i.status != 3: + if i.complaint_finish < date.today(): + i.delay = date.today() - i.complaint_finish + overduecomplaint.append(i) + return render(request, "complaintModule/supervisor1.html", + {'all_caretaker': all_caretaker, 'all_complaint': all_complaint, + 'overduecomplaint': overduecomplaint, 'area': area,'num':num}) else: - - - all_caretaker = Caretaker.objects.select_related('staff_id','staff_id__user','staff_id__department').order_by('-id') + print('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx') + y = ExtraInfo.objects.all().select_related('user','department').get(id=y.id) + try: + a = get_object_or_404(Supervisor, sup_id=y) + except : + return HttpResponseRedirect('/') + + #print(a) + # if(len(a)==0) : + # return render('../dashboard/') + a = Supervisor.objects.select_related('sup_id','sup_id__user','sup_id__department').get(sup_id=y) + all_caretaker = Caretaker.objects.select_related('staff_id','staff_id__user','staff_id__department').filter(area=a.area).order_by('-id') area = all_caretaker[0].area - numtemp = StudentComplain.objects.select_related('complainer','complainer__user','complainer__department','worker_id','worker_id__caretaker_id__staff_id','worker_id__caretaker_id__staff_id__user','worker_id__caretaker_id__staff_id__department').filter(location = area).filter(status = 0).count() num = int(numtemp/2+0.5) all_complaint = [] - user_details=User.objects.get(id=y.user_id) - - - + all_complainttemp = StudentComplain.objects.select_related('complainer','complainer__user','complainer__department','worker_id','worker_id__caretaker_id__staff_id','worker_id__caretaker_id__staff_id__user','worker_id__caretaker_id__staff_id__department').filter(location=a.area).order_by('-id') + j = 1 + for i in all_complainttemp: + all_complaint.append(i) + # if j%2 != 0: + # all_complaint.append(i) + # j = j + 1 + overduecomplaint = [] + for i in all_complaint: + if i.status != 2 and i.status != 3: + if i.complaint_finish < date.today(): + i.delay = date.today() - i.complaint_finish + overduecomplaint.append(i) - - current_user = get_object_or_404(User, username=request.user.username) - y = ExtraInfo.objects.all().select_related('user','department').filter(user=current_user).first() - user_details=User.objects.get(id=y.user_id) - des=HoldsDesignation.objects.filter(user=user_details).all() - # print(y.user_id) - # print(user_details.username) - # print(des[0].user) - outbox_files = view_outbox( - username=user_details.username, - designation=des[0].designation, - src_module="complaint" - ) - - outbox=[] - comp_list=set() - for i in outbox_files: - # print(i) - outbox.append(i) - - for i in outbox: - file_history = view_history(file_id=i['id']) - print(i['id']) - comp=File.objects.filter(id=i['id']) - print(comp[0].src_object_id) - complaint=StudentComplain.objects.all().filter(id=comp[0].src_object_id) - print(complaint) - comp_list.add(complaint) - # file_history = view_history(file_id=i['id']) - - # comp=File.objects.filter(uploader=file_history[0]['current_id']) - # for j in comp: - # c=StudentComplain.objects.all().filter(id=j.src_object_id) - # comp_list.add(c) - # print(c[0]) - - # break - complaint_final_list=[] - for i in comp_list: - complaint_final_list.append(i[0]) - sorted_history_out = sorted(complaint_final_list, key=attrgetter('complaint_date'), reverse=True) - - inbox_files = view_inbox( - username=user_details.username, - designation=des[0].designation, - src_module="complaint" - ) - inbox=[] - comp_list_in=set() - for i in inbox_files: - inbox.append(i) - # print(inbox[0]['id']) - - for i in inbox: - file_history = view_history(file_id=i['id']) - print(i['id']) - comp=File.objects.filter(id=i['id']) - print(comp[0].src_object_id) - complaint=StudentComplain.objects.all().filter(id=comp[0].src_object_id) - print(complaint) - comp_list_in.add(complaint) - # print(complaint[0]) - # for j in comp: - # c=StudentComplain.objects.all().filter(id=j.src_object_id) - # comp_list_in.add(c) - # print(c[0]) - - # break - complaint_final_list_in=[] - for i in comp_list_in: - complaint_final_list_in.append(i[0]) - - sorted_history = sorted(complaint_final_list_in, key=attrgetter('complaint_date'), reverse=True) - print(complaint_final_list_in) return render(request, "complaintModule/supervisor1.html", - - {'history':sorted_history,'all_caretaker': all_caretaker, 'all_complaint': all_complaint,'outbox':sorted_history_out}) + {'all_caretaker': all_caretaker, 'all_complaint': all_complaint, + 'overduecomplaint': overduecomplaint, 'area': area, 'num' : num}) @login_required def caretaker_id_know_more(request,caretaker_id): this_caretaker = Caretaker.objects.select_related('staff_id','staff_id__user','staff_id__department').get(id = caretaker_id) - this_caretaker_area = this_caretaker.area + this_caretaker_area = this_caretaker.area; list_pending_complaints = [] - list_pending_complaintstemp = StudentComplain.objects.select_related('complainer','complainer__user','complainer__department').filter(location = this_caretaker_area).filter(status = 0) + list_pending_complaintstemp = StudentComplain.objects.select_related('complainer','complainer__user','complainer__department','worker_id','worker_id__caretaker_id__staff_id','worker_id__caretaker_id__staff_id__user','worker_id__caretaker_id__staff_id__department').filter(location = this_caretaker_area).filter(status = 0) j = 1 for i in list_pending_complaintstemp: list_pending_complaints.append(i) - + # if j%2 != 0: + # list_pending_complaints.append(i) + # j = j + 1 + # num = StudentComplain.objects.filter(location = this_caretaker_area).filter(status = 0).count(); num = len(list_pending_complaints) return render(request, "complaintModule/caretaker_id_know_more.html",{'this_caretaker':this_caretaker , 'list_pending_complaints':list_pending_complaints, 'num':num}) @@ -1243,36 +911,6 @@ def search_complaint(request): @login_required def resolvepending(request, cid): - a = get_object_or_404(User, username=request.user.username) - y = ExtraInfo.objects.all().select_related('user','department').filter(user=a).first() - thiscomplaint = StudentComplain.objects.select_related('complainer','complainer__user','complainer__department').get(id=cid) - if request.method == 'POST': - newstatus = request.POST.get('yesorno','') - comment = request.POST.get('comment') - intstatus = 0 - if newstatus == 'Yes': - intstatus = 2 - else: - intstatus = 3 - StudentComplain.objects.select_related('complainer','complainer__user','complainer__department').filter(id=cid).\ - update(status=intstatus) - StudentComplain.objects.select_related('complainer','complainer__user','complainer__department').filter(id=cid).\ - update(comment=comment) - complainer_details = StudentComplain.objects.select_related('complainer','complainer__user','complainer__department').get(id=cid) - student=0 - message = "Congrats! Your complaint has been resolved" - complaint_system_notif(request.user, complainer_details.complainer.user ,'comp_resolved_alert',complainer_details.id,student,message) - return HttpResponseRedirect("/complaint/caretaker/") - else: - # complainer_details = StudentComplain.objects.get(id=cid) - # complaint_system_notif(request.user, complainer_details.complainer.user ,'comp_resolved_alert') - return render(request,"complaintModule/resolve_pending.html",{"a" : a,"thiscomplaint" : thiscomplaint}) - - - - -@login_required -def resolvependingsuper(request, cid): a = get_object_or_404(User, username=request.user.username) y = ExtraInfo.objects.all().select_related('user','department').filter(user=a).first() thiscomplaint = StudentComplain.objects.select_related('complainer','complainer__user','complainer__department','worker_id','worker_id__caretaker_id__staff_id','worker_id__caretaker_id__staff_id__user','worker_id__caretaker_id__staff_id__department').get(id=cid) @@ -1292,37 +930,7 @@ def resolvependingsuper(request, cid): student=0 message = "Congrats! Your complaint has been resolved" complaint_system_notif(request.user, complainer_details.complainer.user ,'comp_resolved_alert',complainer_details.id,student,message) - return HttpResponseRedirect("/complaint/supervisor/") - else: - # complainer_details = StudentComplain.objects.get(id=cid) - # complaint_system_notif(request.user, complainer_details.complainer.user ,'comp_resolved_alert') - return render(request,"complaintModule/resolve_pending.html",{"a" : a,"thiscomplaint" : thiscomplaint}) - - - - -@login_required -def resolvependingsuper(request, cid): - a = get_object_or_404(User, username=request.user.username) - y = ExtraInfo.objects.all().select_related('user','department').filter(user=a).first() - thiscomplaint = StudentComplain.objects.select_related('complainer','complainer__user','complainer__department').get(id=cid) - if request.method == 'POST': - newstatus = request.POST.get('yesorno','') - comment = request.POST.get('comment') - intstatus = 0 - if newstatus == 'Yes': - intstatus = 2 - else: - intstatus = 3 - StudentComplain.objects.select_related('complainer','complainer__user','complainer__department').filter(id=cid).\ - update(status=intstatus) - StudentComplain.objects.select_related('complainer','complainer__user','complainer__department').filter(id=cid).\ - update(comment=comment) - complainer_details = StudentComplain.objects.select_related('complainer','complainer__user','complainer__department').get(id=cid) - student=0 - message = "Congrats! Your complaint has been resolved" - complaint_system_notif(request.user, complainer_details.complainer.user ,'comp_resolved_alert',complainer_details.id,student,message) - return HttpResponseRedirect("/complaint/supervisor/") + return HttpResponseRedirect("/complaint/caretaker/") else: # complainer_details = StudentComplain.objects.get(id=cid) # complaint_system_notif(request.user, complainer_details.complainer.user ,'comp_resolved_alert') @@ -1330,7 +938,6 @@ def resolvependingsuper(request, cid): - def login1(request): if request.method == 'POST': u = request.POST.get('username', '') @@ -1349,10 +956,10 @@ def login1(request): @login_required def feedback_super(request, feedcomp_id): - detail3 = StudentComplain.objects.select_related('complainer','complainer__user','complainer__department').get(id=feedcomp_id) + detail3 = StudentComplain.objects.select_related('complainer','complainer__user','complainer__department','worker_id','worker_id__caretaker_id__staff_id','worker_id__caretaker_id__staff_id__user','worker_id__caretaker_id__staff_id__department').get(id=feedcomp_id) a=User.objects.get(username=detail3.complainer.user.username) y=ExtraInfo.objects.all().select_related('user','department').get(user=a) - temp=StudentComplain.objects.select_related('complainer','complainer__user','complainer__department').filter(complainer=y).first() + temp=StudentComplain.objects.select_related('complainer','complainer__user','complainer__department','worker_id','worker_id__caretaker_id__staff_id','worker_id__caretaker_id__staff_id__user','worker_id__caretaker_id__staff_id__department').filter(complainer=y).first() comp_id=temp.id loc = detail3.location care = Caretaker.objects.select_related('staff_id','staff_id__user','staff_id__department').filter(area=loc).first() @@ -1361,49 +968,47 @@ def feedback_super(request, feedcomp_id): @login_required def feedback_care(request, feedcomp_id): - detail2 = StudentComplain.objects.select_related('complainer','complainer__user','complainer__department').get(id=feedcomp_id) + detail2 = StudentComplain.objects.select_related('complainer','complainer__user','complainer__department','worker_id','worker_id__caretaker_id__staff_id','worker_id__caretaker_id__staff_id__user','worker_id__caretaker_id__staff_id__department').get(id=feedcomp_id) a=User.objects.get(username=detail2.complainer.user.username) y=ExtraInfo.objects.all().select_related('user','department').get(user=a) - temp=StudentComplain.objects.select_related('complainer','complainer__user','complainer__department').filter(complainer=y).first() + temp=StudentComplain.objects.select_related('complainer','complainer__user','complainer__department','worker_id','worker_id__caretaker_id__staff_id','worker_id__caretaker_id__staff_id__user','worker_id__caretaker_id__staff_id__department').filter(complainer=y).first() comp_id=temp.id return render(request, "complaintModule/feedback_care.html", {"detail2": detail2,"comp_id":comp_id}) -#for complainaint and caretaker + + @login_required def detail(request, detailcomp_id1): """ function that shows detail about complaint """ - detail = StudentComplain.objects.select_related('complainer','complainer__user','complainer__department').get(id=detailcomp_id1) + detail = StudentComplain.objects.select_related('complainer','complainer__user','complainer__department','worker_id','worker_id__caretaker_id__staff_id','worker_id__caretaker_id__staff_id__user','worker_id__caretaker_id__staff_id__department').get(id=detailcomp_id1) if(detail.worker_id is None): worker_name = None worker_id = detail.worker_id else: worker_id = detail.worker_id.id - worker = Workers.objects.select_related('secincharge_id','secincharge_id__staff_id','secincharge_id__staff_id__user','secincharge_id__staff_id__department').get(id=worker_id) + worker = Workers.objects.select_related('caretaker_id','caretaker_id__staff_id','caretaker_id__staff_id__user','caretaker_id__staff_id__department').get(id=worker_id) worker_name = worker.name a=User.objects.get(username=detail.complainer.user.username) y=ExtraInfo.objects.all().select_related('user','department').get(user=a) num=0 if detail.upload_complaint != "": num = 1 - temp=StudentComplain.objects.select_related('complainer','complainer__user','complainer__department').filter(complainer=y).first() + temp=StudentComplain.objects.select_related('complainer','complainer__user','complainer__department','worker_id','worker_id__caretaker_id__staff_id','worker_id__caretaker_id__staff_id__user','worker_id__caretaker_id__staff_id__department').filter(complainer=y).first() comp_id=temp.id return render(request, "complaintModule/complaint_user_detail.html", {"detail": detail, "comp_id":detail.id,"num":num,"worker_name":worker_name}) - - -#for SectionIncharge @login_required def detail2(request, detailcomp_id1): - detail2 = StudentComplain.objects.select_related('complainer','complainer__user','complainer__department').get(id=detailcomp_id1) + detail2 = StudentComplain.objects.select_related('complainer','complainer__user','complainer__department','worker_id','worker_id__caretaker_id__staff_id','worker_id__caretaker_id__staff_id__user','worker_id__caretaker_id__staff_id__department').get(id=detailcomp_id1) if(detail2.worker_id is None): worker_name = None worker_id = detail2.worker_id else: worker_id = detail2.worker_id.id - worker = Workers.objects.select_related('secincharge_id','secincharge_id__staff_id','secincharge_id__staff_id__user','secincharge_id__staff_id__department').get(id=worker_id) + worker = Workers.objects.select_related('caretaker_id','caretaker_id__staff_id','caretaker_id__staff_id__user','caretaker_id__staff_id__department').get(id=worker_id) worker_name = worker.name a=User.objects.get(username=detail2.complainer.user.username) y=ExtraInfo.objects.all().select_related('user','department').get(user=a) @@ -1411,474 +1016,20 @@ def detail2(request, detailcomp_id1): if detail2.upload_complaint != "": num = 1 - temp=StudentComplain.objects.select_related('complainer','complainer__user','complainer__department').filter(complainer=y).first() + temp=StudentComplain.objects.select_related('complainer','complainer__user','complainer__department','worker_id','worker_id__caretaker_id__staff_id','worker_id__caretaker_id__staff_id__user','worker_id__caretaker_id__staff_id__department').filter(complainer=y).first() comp_id=temp.id - return render(request, "complaintModule/complaint_secincharge_detail.html", {"detail2": detail2, "comp_id":detail2.id,"num":num,"worker_name":worker_name,"wid":worker_id}) - + return render(request, "complaintModule/complaint_caretaker_detail.html", {"detail2": detail2, "comp_id":detail2.id,"num":num,"worker_name":worker_name,"wid":worker_id}) @login_required def detail3(request, detailcomp_id1): - detail3 = StudentComplain.objects.select_related('complainer','complainer__user','complainer__department').get(id=detailcomp_id1) + detail3 = StudentComplain.objects.select_related('complainer','complainer__user','complainer__department','worker_id','worker_id__caretaker_id__staff_id','worker_id__caretaker_id__staff_id__user','worker_id__caretaker_id__staff_id__department').get(id=detailcomp_id1) a=User.objects.get(username=detail3.complainer.user.username) y=ExtraInfo.objects.all().select_related('user','department').get(user=a) num=0 if detail3.upload_complaint != "": num = 1 - temp=StudentComplain.objects.select_related('complainer','complainer__user','complainer__department').filter(complainer=y).first() + temp=StudentComplain.objects.select_related('complainer','complainer__user','complainer__department','worker_id','worker_id__caretaker_id__staff_id','worker_id__caretaker_id__staff_id__user','worker_id__caretaker_id__staff_id__department').filter(complainer=y).first() comp_id=temp.id loc = detail3.location care = Caretaker.objects.select_related('staff_id','staff_id__user','staff_id__department').filter(area=loc).first() return render(request, "complaintModule/complaint_supervisor_detail.html", {"detail3": detail3,"comp_id":comp_id,"care":care,"num":num}) - - - - -@login_required - -def supervisorlodge(request): - """ - The function is used to register a complaint - @param: - request - trivial. - - - @variables: - issue - The issue object. - supported - True if the user's intention is to support the issue. - support_count - Total supporters of the above issue. - context - Holds data needed to make necessary changes in the template. - """ - a = get_object_or_404(User, username=request.user.username) - y = ExtraInfo.objects.all().select_related('user','department').filter(user=a).first() - num = 1 - comp_id = y.id - if request.method == 'POST': - comp_type = request.POST.get('complaint_type', '') - location = request.POST.get('Location', '') - specific_location = request.POST.get('specific_location', '') - comp_file = request.FILES.get('myfile') - - details = request.POST.get('details', '') - status = 0 - # finish time is according to complaint type - complaint_finish = datetime.now() + timedelta(days=2) - if comp_type == 'Electricity': - complaint_finish = datetime.now() + timedelta(days=2) - elif comp_type == 'carpenter': - complaint_finish = datetime.now() + timedelta(days=2) - elif comp_type == 'plumber': - complaint_finish = datetime.now() + timedelta(days=2) - elif comp_type == 'garbage': - complaint_finish = datetime.now() + timedelta(days=1) - elif comp_type == 'dustbin': - complaint_finish = datetime.now() + timedelta(days=1) - elif comp_type == 'internet': - complaint_finish = datetime.now() + timedelta(days=4) - elif comp_type == 'other': - complaint_finish = datetime.now() + timedelta(days=3) - y = ExtraInfo.objects.all().select_related('user','department').get(id=comp_id) - #check if location given - if location!="": - # x = StudentComplain(complainer=y, - # complaint_type=comp_type, - # location=location, - # specific_location=specific_location, - # details=details, - # status=status, - # complaint_finish=complaint_finish, - # upload_complaint=comp_file) - - - # x.save() - obj1, created = StudentComplain.objects.get_or_create(complainer=y, - complaint_type=comp_type, - location=location, - specific_location=specific_location, - details=details, - status=status, - complaint_finish=complaint_finish, - upload_complaint=comp_file) - - - historytemp = StudentComplain.objects.select_related('complainer','complainer__user','complainer__department','worker_id','worker_id__caretaker_id__staff_id','worker_id__caretaker_id__staff_id__user','worker_id__caretaker_id__staff_id__department').filter(complainer=y).order_by('-id') - history = [] - j = 1 - k = 1 - for i in historytemp: - history.append(i) - # if j%2 != 0: - # history.append(i) - # j = j+1 - - - for h in history: - h.serial_no = k - k = k+1 - # if location == "hall1": - # dsgn = "hall1caretaker" - # elif location == "hall3": - # dsgn = "hall3caretaker" - # else : - # dsgn = "hall4caretaker" - if location == "hall-1": - dsgn ="hall1caretaker" - elif location =="hall-3": - dsgn ="hall3caretaker" - elif location =="hall-4": - dsgn ="hall4caretaker" - elif location =="CC1": - dsgn ="cc1convener" - elif location =="CC2": - dsgn ="CC2 convener" - elif location == "core_lab": - dsgn = "corelabcaretaker" - elif location =="LHTC": - dsgn ="lhtccaretaker" - elif location =="NR2": - dsgn ="nr2caretaker" - elif location =="Maa Saraswati Hostel": - dsgn ="mshcaretaker" - elif location =="Nagarjun Hostel": - dsgn ="nhcaretaker" - elif location =="Panini Hostel": - dsgn ="phcaretaker" - else: - dsgn = "rewacaretaker" - caretaker_name = HoldsDesignation.objects.select_related('user','working','designation').get(designation__name = dsgn) - - - # This is to allow the student - student = 1 - message = "A New Complaint has been lodged" - complaint_system_notif(request.user, caretaker_name.user,'lodge_comp_alert',obj1.id,student,message) - - # return render(request, "complaintModule/complaint_user.html", - # {'history': history, 'comp_id': comp_id }) - # next = request.POST.get('next', '/') - - messages.success(request,message) - return HttpResponseRedirect('/complaint/supervisor') - - else: - a = get_object_or_404(User, username=request.user.username) - y = ExtraInfo.objects.all().select_related('user','department').filter(user=a).first() - historytemp = StudentComplain.objects.select_related('complainer','complainer__user','complainer__department','worker_id','worker_id__caretaker_id__staff_id','worker_id__caretaker_id__staff_id__user','worker_id__caretaker_id__staff_id__department').filter(complainer=y).order_by('-id') - history=[] - - notification = Notification.objects.filter(recipient=a.id) - notification = notification.filter(data__exact={'url':'complaint:detail','module':'Complaint System'}) - # notification_message = [] - # for notification in x: - # to = User.objects.get(id=notification.actor_object_id).username - # from django.utils.timesince import timesince as timesince_ - # duration = timesince_(notification.timestamp,None) - # notification_message.append(notification.verb+' by '+ to + ' ' + duration + ' ago ') - - - - - j = 1 - for i in historytemp: - history.append(i) - # if j%2 != 0: - # history.append(i) - # j = j+1 - - for i in history: - i.serial_no = j - j = j+1 - - # if location == "hall-1": - # dsgn ="hall1caretaker" - # elif location =="hall-3": - # dsgn ="hall3caretaker" - # elif location =="hall-4": - # dsgn ="hall4caretaker" - # elif location =="CC1": - # dsgn ="CC convenor" - # elif location =="CC2": - # dsgn ="CC2 convener" - # elif location == "core_lab": - # dsgn = "corelabcaretaker" - # elif location =="LHTC": - # dsgn ="lhtccaretaker" - # elif location =="NR2": - # dsgn ="nr2caretaker" - # else: - # dsgn = "rewacaretaker" - # caretaker_name = HoldsDesignation.objects.get(designation__name = dsgn) - - # complaint_system_notif(request.user, caretaker_name.user,'lodge_comp_alert') - return render(request, "complaintModule/supervisor1.html", - {'history': history,'notification':notification, 'comp_id': y.id}) - - return render(request, "complaintModule/complaint_user.html", - {'history': history, 'comp_id': comp_id }) - - - - -@login_required -def submitfeedbacksuper(request, complaint_id): - """ - The function is used by the complainant to enter feedback after the complaint has been resolved - @param: - request - trivial. - complaint_id - id of the registerd complaint. - - @variables: - issue - The issue object. - supported - True if the user's intention is to support the issue. - support_count - Total supporters of the above issue. - context - Holds data needed to make necessary changes in the template. - """ - - if request.method == 'POST': - feedback = request.POST.get('feedback', '') - rating = request.POST.get('rating', '') - StudentComplain.objects.select_related('complainer','complainer__user','complainer__department').filter(id=complaint_id).\ - update(feedback=feedback, flag=rating) - a = StudentComplain.objects.select_related('complainer','complainer__user','complainer__department').filter(id=complaint_id).first() - care = Caretaker.objects.select_related('staff_id','staff_id__user','staff_id__department').filter(area=a.location).first() - rate = care.rating - newrate = 0 - if rate == 0: - newrate = rating - else: - a1 = int(rating) - b1 = int(rate) - c1 = int((a1+b1)/2) - newrate = c1 - - Caretaker.objects.select_related('staff_id','staff_id__user','staff_id__department').filter(area=a.location).update(rating=newrate) - return HttpResponseRedirect('/complaint/supervisor/') - return render(request,"complaintModule/feedback.html",{'a' : a}) - - else: - a = StudentComplain.objects.select_related('complainer','complainer__user','complainer__department').get(id=complaint_id) - return render(request, "complaintModule/submit_feedback.html", {'a': a}) - - - -@login_required - -def caretakerlodge(request): - """ - The function is used to register a complaint - @param: - request - trivial. - - - @variables: - issue - The issue object. - supported - True if the user's intention is to support the issue. - support_count - Total supporters of the above issue. - context - Holds data needed to make necessary changes in the template. - """ - a = get_object_or_404(User, username=request.user.username) - y = ExtraInfo.objects.all().select_related('user','department').filter(user=a).first() - num = 1 - comp_id = y.id - if request.method == 'POST': - comp_type = request.POST.get('complaint_type', '') - location = request.POST.get('Location', '') - specific_location = request.POST.get('specific_location', '') - comp_file = request.FILES.get('myfile') - - details = request.POST.get('details', '') - status = 0 - # finish time is according to complaint type - complaint_finish = datetime.now() + timedelta(days=2) - if comp_type == 'Electricity': - complaint_finish = datetime.now() + timedelta(days=2) - elif comp_type == 'carpenter': - complaint_finish = datetime.now() + timedelta(days=2) - elif comp_type == 'plumber': - complaint_finish = datetime.now() + timedelta(days=2) - elif comp_type == 'garbage': - complaint_finish = datetime.now() + timedelta(days=1) - elif comp_type == 'dustbin': - complaint_finish = datetime.now() + timedelta(days=1) - elif comp_type == 'internet': - complaint_finish = datetime.now() + timedelta(days=4) - elif comp_type == 'other': - complaint_finish = datetime.now() + timedelta(days=3) - y = ExtraInfo.objects.all().select_related('user','department').get(id=comp_id) - #check if location given - if location!="": - # x = StudentComplain(complainer=y, - # complaint_type=comp_type, - # location=location, - # specific_location=specific_location, - # details=details, - # status=status, - # complaint_finish=complaint_finish, - # upload_complaint=comp_file) - - - # x.save() - obj1, created = StudentComplain.objects.get_or_create(complainer=y, - complaint_type=comp_type, - location=location, - specific_location=specific_location, - details=details, - status=status, - complaint_finish=complaint_finish, - upload_complaint=comp_file) - - - historytemp = StudentComplain.objects.select_related('complainer','complainer__user','complainer__department','worker_id','worker_id__caretaker_id__staff_id','worker_id__caretaker_id__staff_id__user','worker_id__caretaker_id__staff_id__department').filter(complainer=y).order_by('-id') - history = [] - j = 1 - k = 1 - for i in historytemp: - history.append(i) - # if j%2 != 0: - # history.append(i) - # j = j+1 - - - for h in history: - h.serial_no = k - k = k+1 - # if location == "hall1": - # dsgn = "hall1caretaker" - # elif location == "hall3": - # dsgn = "hall3caretaker" - # else : - # dsgn = "hall4caretaker" - if location == "hall-1": - dsgn ="hall1caretaker" - elif location =="hall-3": - dsgn ="hall3caretaker" - elif location =="hall-4": - dsgn ="hall4caretaker" - elif location =="CC1": - dsgn ="cc1convener" - elif location =="CC2": - dsgn ="CC2 convener" - elif location == "core_lab": - dsgn = "corelabcaretaker" - elif location =="LHTC": - dsgn ="lhtccaretaker" - elif location =="NR2": - dsgn ="nr2caretaker" - elif location =="Maa Saraswati Hostel": - dsgn ="mshcaretaker" - elif location =="Nagarjun Hostel": - dsgn ="nhcaretaker" - elif location =="Panini Hostel": - dsgn ="phcaretaker" - else: - dsgn = "rewacaretaker" - caretaker_name = HoldsDesignation.objects.select_related('user','working','designation').get(designation__name = dsgn) - - - # This is to allow the student - student = 1 - message = "A New Complaint has been lodged" - complaint_system_notif(request.user, caretaker_name.user,'lodge_comp_alert',obj1.id,student,message) - - # return render(request, "complaintModule/complaint_user.html", - # {'history': history, 'comp_id': comp_id }) - # next = request.POST.get('next', '/') - - messages.success(request,message) - return HttpResponseRedirect('/complaint/caretaker') - - else: - a = get_object_or_404(User, username=request.user.username) - y = ExtraInfo.objects.all().select_related('user','department').filter(user=a).first() - historytemp = StudentComplain.objects.select_related('complainer','complainer__user','complainer__department','worker_id','worker_id__caretaker_id__staff_id','worker_id__caretaker_id__staff_id__user','worker_id__caretaker_id__staff_id__department').filter(complainer=y).order_by('-id') - history=[] - - notification = Notification.objects.filter(recipient=a.id) - notification = notification.filter(data__exact={'url':'complaint:detail','module':'Complaint System'}) - # notification_message = [] - # for notification in x: - # to = User.objects.get(id=notification.actor_object_id).username - # from django.utils.timesince import timesince as timesince_ - # duration = timesince_(notification.timestamp,None) - # notification_message.append(notification.verb+' by '+ to + ' ' + duration + ' ago ') - - - - - j = 1 - for i in historytemp: - history.append(i) - # if j%2 != 0: - # history.append(i) - # j = j+1 - - for i in history: - i.serial_no = j - j = j+1 - - # if location == "hall-1": - # dsgn ="hall1caretaker" - # elif location =="hall-3": - # dsgn ="hall3caretaker" - # elif location =="hall-4": - # dsgn ="hall4caretaker" - # elif location =="CC1": - # dsgn ="CC convenor" - # elif location =="CC2": - # dsgn ="CC2 convener" - # elif location == "core_lab": - # dsgn = "corelabcaretaker" - # elif location =="LHTC": - # dsgn ="lhtccaretaker" - # elif location =="NR2": - # dsgn ="nr2caretaker" - # else: - # dsgn = "rewacaretaker" - # caretaker_name = HoldsDesignation.objects.get(designation__name = dsgn) - - # complaint_system_notif(request.user, caretaker_name.user,'lodge_comp_alert') - return render(request, "complaintModule/complaint_caretaker.html", - {'history': history,'notification':notification, 'comp_id': y.id}) - - return render(request, "complaintModule/complaint_user.html", - {'history': history, 'comp_id': comp_id }) - - -@login_required -def submitfeedbackcaretaker(request, complaint_id): - """ - The function is used by the complainant to enter feedback after the complaint has been resolved - @param: - request - trivial. - complaint_id - id of the registerd complaint. - - @variables: - issue - The issue object. - supported - True if the user's intention is to support the issue. - support_count - Total supporters of the above issue. - context - Holds data needed to make necessary changes in the template. - """ - - if request.method == 'POST': - feedback = request.POST.get('feedback', '') - rating = request.POST.get('rating', '') - StudentComplain.objects.select_related('complainer','complainer__user','complainer__department').filter(id=complaint_id).\ - update(feedback=feedback, flag=rating) - a = StudentComplain.objects.select_related('complainer','complainer__user','complainer__department').filter(id=complaint_id).first() - care = Caretaker.objects.select_related('staff_id','staff_id__user','staff_id__department').filter(area=a.location).first() - rate = care.rating - newrate = 0 - if rate == 0: - newrate = rating - else: - a1 = int(rating) - b1 = int(rate) - c1 = int((a1+b1)/2) - newrate = c1 - - Caretaker.objects.select_related('staff_id','staff_id__user','staff_id__department').filter(area=a.location).update(rating=newrate) - return HttpResponseRedirect('/complaint/caretaker/') - return render(request,"complaintModule/feedback.html",{'a' : a}) - - else: - a = StudentComplain.objects.select_related('complainer','complainer__user','complainer__department').get(id=complaint_id) - return render(request, "complaintModule/submit_feedback.html", {'a': a}) \ No newline at end of file diff --git a/FusionIIIT/applications/counselling_cell/migrations/0001_initial.py b/FusionIIIT/applications/counselling_cell/migrations/0001_initial.py index 7cd42f1b2..b87618488 100644 --- a/FusionIIIT/applications/counselling_cell/migrations/0001_initial.py +++ b/FusionIIIT/applications/counselling_cell/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 3.1.5 on 2024-07-16 15:44 +# Generated by Django 3.1.5 on 2023-03-15 18:53 import datetime from django.db import migrations, models diff --git a/FusionIIIT/applications/department/api/permissions.py b/FusionIIIT/applications/department/api/permissions.py deleted file mode 100644 index 2b870575b..000000000 --- a/FusionIIIT/applications/department/api/permissions.py +++ /dev/null @@ -1,18 +0,0 @@ -from rest_framework import permissions -from django.shortcuts import get_object_or_404 -from applications.academic_information.models import ExtraInfo -from applications.globals.models import User - - -class IsFacultyStaffOrReadOnly(permissions.BasePermission): - """ - Custom permission to only allow faculty and staff to edit it. - """ - - def has_permission(self, request, view): - # Read permissions are allowed to any request, - # so we'll always allow GET, HEAD or OPTIONS requests. - if request.method in permissions.SAFE_METHODS: - return True - #only faculty and staff are able to make post request - return not request.user.holds_designations.filter(designation__name='student').exists() diff --git a/FusionIIIT/applications/department/api/serializers.py b/FusionIIIT/applications/department/api/serializers.py deleted file mode 100644 index aa005ad31..000000000 --- a/FusionIIIT/applications/department/api/serializers.py +++ /dev/null @@ -1,62 +0,0 @@ -from rest_framework import serializers -from applications.department.models import Announcements -from applications.academic_information.models import Spi, Student -from applications.globals.models import (Designation, ExtraInfo, - HoldsDesignation,Faculty) -from applications.eis.models import (faculty_about, emp_research_projects) - - -class AnnouncementSerializer(serializers.ModelSerializer): - class Meta: - model = Announcements - fields = ('__all__') - - extra_kwargs = { - 'maker_id': {'required': False} - } - - def create(self, validated_data): - user = self.context['request'].user - user_info = ExtraInfo.objects.all().select_related('user','department').filter(user=user).first() - validated_data['maker_id'] = user_info - return Announcements.objects.create(**validated_data) - -class ExtraInfoSerializer(serializers.ModelSerializer): - class Meta: - model = ExtraInfo - fields = ('__all__') - -class SpiSerializer(serializers.ModelSerializer): - class Meta: - model = Spi - fields = ('__all__') - -class StudentSerializer(serializers.ModelSerializer): - class Meta: - model = Student - fields = ('__all__') - -class DesignationSerializer(serializers.ModelSerializer): - class Meta: - model = Designation - fields = ('__all__') - -class HoldsDesignationSerializer(serializers.ModelSerializer): - class Meta: - model = HoldsDesignation - fields = ('__all__') - -class FacultySerializer(serializers.ModelSerializer): - class Meta: - model = Faculty - fields = ('__all__') - -class faculty_aboutSerializer(serializers.ModelSerializer): - class Meta: - model = faculty_about - fields = ('__all__') - -class emp_research_projectsSerializer(serializers.ModelSerializer): - class Meta: - model = emp_research_projects - fields = ('__all__') \ No newline at end of file diff --git a/FusionIIIT/applications/department/api/urls.py b/FusionIIIT/applications/department/api/urls.py deleted file mode 100644 index 9e0dae890..000000000 --- a/FusionIIIT/applications/department/api/urls.py +++ /dev/null @@ -1,12 +0,0 @@ -from django.conf.urls import url - -from . import views - -urlpatterns = [ - url(r'announcements/$', views.ListCreateAnnouncementView.as_view(),name='announcements'), - url(r'dep-main/$', views.DepMainAPIView.as_view(), name='depmain'), - url(r'fac-view/$', views.FacAPIView.as_view(), name='facapi'), - url(r'staff-view/$',views.StaffAPIView.as_view(),name='staffapi'), - url(r'all-students/(?P[0-9]+)/$', views.AllStudentsAPIView.as_view() ,name='all_students') - -] diff --git a/FusionIIIT/applications/department/api/views.py b/FusionIIIT/applications/department/api/views.py deleted file mode 100644 index 686f0268e..000000000 --- a/FusionIIIT/applications/department/api/views.py +++ /dev/null @@ -1,216 +0,0 @@ -from rest_framework import generics -from rest_framework.views import APIView -from applications.department.models import Announcements -from applications.academic_information.models import Spi, Student -from applications.globals.models import (Designation, ExtraInfo, - HoldsDesignation,Faculty) -from applications.eis.models import (faculty_about, emp_research_projects) -from .serializers import (AnnouncementSerializer,ExtraInfoSerializer,SpiSerializer,StudentSerializer,DesignationSerializer - ,HoldsDesignationSerializer,FacultySerializer,faculty_aboutSerializer,emp_research_projectsSerializer) -from rest_framework.permissions import IsAuthenticated -from .permissions import IsFacultyStaffOrReadOnly -from django.http import JsonResponse -from django.shortcuts import get_object_or_404, render, redirect -from django.contrib.auth.models import User -from rest_framework.response import Response -from rest_framework import status -from django.contrib.auth.decorators import login_required -from django.urls import reverse -from datetime import date -from notification.views import department_notif - - -class ListCreateAnnouncementView(generics.ListCreateAPIView): - queryset = Announcements.objects.all() - serializer_class = AnnouncementSerializer - permission_classes = (IsAuthenticated, IsFacultyStaffOrReadOnly) - - -class DepMainAPIView(APIView): - def get(self, request): - user = request.user - usrnm = get_object_or_404(User, username=user.username) - user_info = ExtraInfo.objects.all().select_related('user', 'department').filter(user=usrnm).first() - ann_maker_id = user_info.id - user_info = ExtraInfo.objects.all().select_related('user', 'department').get(id=ann_maker_id) - - fac_view = user.holds_designations.filter(designation__name='faculty').exists() - student = user.holds_designations.filter(designation__name='student').exists() - staff = user.holds_designations.filter(designation__name='staff').exists() - - context = browse_announcements() - context_f = faculty() - user_designation = "" - - if fac_view: - user_designation = "faculty" - elif student: - user_designation = "student" - else: - user_designation = "staff" - - # serailizing the data - # announcements_serailizer = AnnouncementSerializer(context, many=True) - - - response_data = { - "user_designation": user_designation, - "announcements": context, - "fac_list": context_f - } - - - return Response(data = response_data, status=status.HTTP_200_OK) - # return Response(data = response_data, status=status.HTTP_200_OK) - -class FacAPIView(APIView): - def get(self,request): - usrnm = get_object_or_404(User, username=request.user.username) - user_info = ExtraInfo.objects.all().select_related('user','department').filter(user=usrnm).first() - - - context = browse_announcements() - - - # Serialize the data into JSON formats - data = { - "user_designation": user_info.user_type, - "announcements": list(context.values()), # Assuming 'context' is a dictionary - } - - return Response(data) - -class StaffAPIView(APIView): - def get(self,request): - usrnm = get_object_or_404(User, username=request.user.username) - user_info = ExtraInfo.objects.all().select_related('user','department').filter(user=usrnm).first() - - - context = browse_announcements() - - - # Serialize the data into JSON formats - data = { - "user_designation": user_info.user_type, - "announcements": list(context.values()), # Assuming 'context' is a dictionary - } - - return Response(data) - -class AllStudentsAPIView(APIView): - def get(self,request,bid): - print(self.request.query_params) - # bid = self.request.query_params.get() - - # Decode bid into filter criteria - filter_criteria = decode_bid(bid) - if not filter_criteria: - return Response({'detail': 'Invalid bid value'}, status=status.HTTP_400_BAD_REQUEST) - - # Apply additional department filter since it seems fixed - filter_criteria['id__department__name'] = 'CSE' - - student_list1 = Student.objects.order_by('id').filter( - id__user_type='student', - **filter_criteria - ).select_related('id') - - # paginator = Paginator(student_list1, 25, orphans=5) - # page_number = request.GET.get('page') - # student_list = paginator.get_page(page_number) - - # Serialize the queryset - serializer = StudentSerializer(student_list1, many=True) - serialized_data = serializer.data - - # Create a response dictionary - response_data = {'student_list': serialized_data} - - return Response(response_data) - - -def decode_bid(bid): - """Decodes the bid structure into programme, batch, and department (if applicable).""" - try: - department_code = bid[0] - programme = { - '1': 'B.Tech', - '2': 'M.Tech', - '3': 'PhD', # Assuming there are more departments - }[department_code] - batch = 2021 - len(bid) + 1 - return {'programme': programme, 'batch': batch} - except (IndexError, KeyError): - return None # Handle malformed bid values - -def browse_announcements(): - """ - This function is used to browse Announcements Department-Wise - made by different faculties and admin. - - @variables: - cse_ann - Stores CSE Department Announcements - ece_ann - Stores ECE Department Announcements - me_ann - Stores ME Department Announcements - sm_ann - Stores SM Department Announcements - all_ann - Stores Announcements intended for all Departments - context - Dictionary for storing all above data - - """ - cse_ann = Announcements.objects.filter(department="CSE") - ece_ann = Announcements.objects.filter(department="ECE") - me_ann = Announcements.objects.filter(department="ME") - sm_ann = Announcements.objects.filter(department="SM") - all_ann = Announcements.objects.filter(department="ALL") - - # serailizing the data - cse_ann_serialized = AnnouncementSerializer(cse_ann, many=True) - ece_ann_serialized = AnnouncementSerializer(ece_ann, many=True) - me_ann_serialized = AnnouncementSerializer(me_ann, many=True) - sm_ann_serialized = AnnouncementSerializer(sm_ann, many=True) - all_ann_serialized = AnnouncementSerializer(all_ann, many=True) - - context = { - "cse" : cse_ann_serialized.data, - "ece" : ece_ann_serialized.data, - "me" : me_ann_serialized.data, - "sm" : sm_ann_serialized.data, - "all" : all_ann_serialized.data - } - - return context - -def faculty(): - """ - This function is used to Return data of Faculties Department-Wise. - - @variables: - cse_f - Stores data of faculties from CSE Department - ece_f - Stores data of faculties from ECE Department - me_f - Stores data of faculties from ME Department - sm_f - Stores data of faculties from ME Department - context_f - Stores all above variables in Dictionary - - """ - cse_f=ExtraInfo.objects.filter(department__name='CSE',user_type='faculty') - ece_f=ExtraInfo.objects.filter(department__name='ECE',user_type='faculty') - me_f=ExtraInfo.objects.filter(department__name='ME',user_type='faculty') - sm_f=ExtraInfo.objects.filter(department__name='SM',user_type='faculty') - staff=ExtraInfo.objects.filter(user_type='staff') - - # serailizing the data - cse_f = ExtraInfoSerializer(cse_f, many=True) - ece_f = ExtraInfoSerializer(ece_f, many=True) - me_f = ExtraInfoSerializer(me_f, many=True) - sm_f = ExtraInfoSerializer(sm_f, many=True) - staff = ExtraInfoSerializer(staff, many=True) - - - context_f = { - "cse_f" : cse_f.data, - "ece_f" : ece_f.data, - "me_f" : me_f.data, - "sm_f" : sm_f.data, - "staff" : staff.data, - } - return context_f \ No newline at end of file diff --git a/FusionIIIT/applications/department/migrations/0001_initial.py b/FusionIIIT/applications/department/migrations/0001_initial.py index 21c891a8e..fc8a34834 100644 --- a/FusionIIIT/applications/department/migrations/0001_initial.py +++ b/FusionIIIT/applications/department/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 3.1.5 on 2024-07-16 15:44 +# Generated by Django 3.1.5 on 2023-03-15 18:53 import datetime from django.db import migrations, models @@ -28,22 +28,11 @@ class Migration(migrations.Migration): ('request_maker', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='globals.extrainfo')), ], ), - migrations.CreateModel( - name='Information', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('phone_number', models.BigIntegerField()), - ('email', models.CharField(max_length=200)), - ('facilites', models.TextField()), - ('labs', models.TextField()), - ('department', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='globals.departmentinfo')), - ], - ), migrations.CreateModel( name='Announcements', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('ann_date', models.DateTimeField(auto_now_add=True)), + ('ann_date', models.DateTimeField(default='04-04-2021')), ('message', models.CharField(max_length=200)), ('batch', models.CharField(default='Year-1', max_length=40)), ('department', models.CharField(default='ALL', max_length=40)), diff --git a/FusionIIIT/applications/department/models.py b/FusionIIIT/applications/department/models.py index caebb4d50..a78b35171 100644 --- a/FusionIIIT/applications/department/models.py +++ b/FusionIIIT/applications/department/models.py @@ -3,8 +3,7 @@ from datetime import date # Create your models here. -from applications.globals.models import ExtraInfo , DepartmentInfo - +from applications.globals.models import ExtraInfo class SpecialRequest(models.Model): request_maker = models.ForeignKey(ExtraInfo, on_delete=models.CASCADE) @@ -22,7 +21,7 @@ def __str__(self): class Announcements(models.Model): maker_id = models.ForeignKey(ExtraInfo, on_delete=models.CASCADE) - ann_date = models.DateTimeField(auto_now_add=True) + ann_date = models.DateTimeField(default="04-04-2021") message = models.CharField(max_length=200) batch = models.CharField(max_length=40,default="Year-1") department = models.CharField(max_length=40,default="ALL") @@ -30,14 +29,3 @@ class Announcements(models.Model): upload_announcement = models.FileField(upload_to='department/upload_announcement', null=True, default=" ") def __str__(self): return str(self.maker_id.user.username) - -class Information(models.Model): - department = models.OneToOneField( - DepartmentInfo, - on_delete=models.CASCADE, - ) - - phone_number = models.BigIntegerField() - email = models.CharField(max_length=200) - facilites = models.TextField() - labs = models.TextField() diff --git a/FusionIIIT/applications/department/static/department/js/function.js b/FusionIIIT/applications/department/static/department/js/function.js index 0a64c2c95..732b50473 100644 --- a/FusionIIIT/applications/department/static/department/js/function.js +++ b/FusionIIIT/applications/department/static/department/js/function.js @@ -5,79 +5,46 @@ $(document).ready(function(){ function announce(event) - { - var message= $('input[name="announcement"]').val(); - var batch = $('input[name="batch"]').val(); - var programme = $('input[name="programme"]').val(); - var department = $('input[name="department"]').val(); - var upload_announcement =$('input[name="upload_announcement"]').val(); - if(message=="" || batch=="" || programme =="" || department=="") - { - alert("Please fill all the details!"); - return; - } - else - { - event.preventDefault(); - $.ajax({ - type : 'POST', - url : '.', - data : { - 'message' : message, - 'batch' : batch, - 'programme' : programme, - 'upload_announcement' : upload_announcement, - 'department' : department, - }, - success : function (data){ + { + var message= $('input[name="announcement"]').val(); + var batch = $('input[name="batch"]').val(); + var programme = $('input[name="programme"]').val(); + var department = $('input[name="department"]').val(); + var upload_announcement =$('input[name="upload_announcement"]').val(); + if(message=="" || batch=="" || programme =="" || department=="") + { + alert("Please fill all the details!"); + return; + } + else + { + event.preventDefault(); + $.ajax({ + type : 'POST', + url : '.', + data : { + 'message' : message, + 'batch' : batch, + 'programme' : programme, + 'upload_announcement' : upload_announcement, + 'department' : department, + }, + success : function (data){ - alert("Announcement successfully made!!"); - setTimeout(function() { - window.location.reload(); - }, 1500); + alert("Announcement successfully made!!"); + setTimeout(function() { + window.location.reload(); + }, 1500); - - }, - error : function (data,err){ - alert('Announcement successfully made ... '); + + }, + error : function (data,err){ + alert('Announcement successfully made ... '); - } - }); - } -}; - -function updateDepartment(event) { - var email = $('input[name="email"]').val(); - var contactNumber = $('input[name="contact_number"]').val(); - var facilities = $('textarea[name="facilities"]').val(); - var labs = $('textarea[name="labs"]').val(); - - if (email === "" || contactNumber === "" || facilities === "" || labs === "" || departmentId === "") { - alert("Please fill all the details!"); - return; - } else { - event.preventDefault(); - $.ajax({ - type: 'POST', - url: '.', // Specify the URL to your view for updating department information - data: { - 'email': email, - 'contact_number': contactNumber, - 'facilities': facilities, - 'labs': labs, - }, - success: function(data) { - alert("Department information updated successfully!"); - setTimeout(function() { - window.location.reload(); - }, 1500); - }, - error: function(data, err) { - alert('Failed to update department information.'); - } - }); - } -} + } + }); + } + }; function request(event) { diff --git a/FusionIIIT/applications/department/urls.py b/FusionIIIT/applications/department/urls.py index 094e1e90d..b7338374a 100644 --- a/FusionIIIT/applications/department/urls.py +++ b/FusionIIIT/applications/department/urls.py @@ -1,5 +1,4 @@ from django.conf.urls import url -from django.urls import include from . import views @@ -10,11 +9,7 @@ url(r'^$', views.dep_main, name='dep'), url(r'^facView/$', views.faculty_view, name='faculty_view'), url(r'^staffView/$', views.staff_view, name='staff_view'), - url(r'All_Students/(?P[0-9]+)/$', views.all_students,name='all_students'), - url(r'alumni/$', views.alumni, name='alumni'), + url(r'^All_Students/(?P[0-9]+)/$', views.all_students,name='all_students'), url(r'^approved/$', views.approved, name='approved'), - url(r'^deny/$', views.deny, name='deny'), - - url(r'^api/', include("applications.department.api.urls")) - -] \ No newline at end of file + url(r'^deny/$', views.deny, name='deny') +] diff --git a/FusionIIIT/applications/department/views.py b/FusionIIIT/applications/department/views.py index 6e5a0d936..ef7e71c64 100644 --- a/FusionIIIT/applications/department/views.py +++ b/FusionIIIT/applications/department/views.py @@ -5,7 +5,7 @@ from django.contrib import messages from django.contrib.auth.decorators import login_required -from django.http import HttpResponse, HttpResponseRedirect, HttpResponseBadRequest +from django.http import HttpResponse, HttpResponseRedirect # Create your views here. from django.db.models import Q from django.shortcuts import get_object_or_404, render, redirect @@ -13,30 +13,16 @@ from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger from applications.academic_information.models import Spi, Student from applications.globals.models import (Designation, ExtraInfo, - HoldsDesignation,Faculty,DepartmentInfo) + HoldsDesignation,Faculty) from applications.eis.models import (faculty_about, emp_research_projects) -from .models import Information + from notification.views import department_notif -from .models import SpecialRequest, Announcements , Information +from .models import SpecialRequest, Announcements from jsonschema import validate from jsonschema.exceptions import ValidationError +# Create your views here. -def department_information(request): - - cse_info = Information.objects.filter(department_id=51).first() - ece_info = Information.objects.filter(department_id=30).first() - me_info = Information.objects.filter(department_id=37).first() - sm_info = Information.objects.filter(department_id=28).first() - department_context = { - "cse_info" : cse_info, - "ece_info" : ece_info, - "me_info" : me_info, - "sm_info" : sm_info - } - # print(department_context) - # print(me_info.phone_number,me_info.email,me_info.department_id) - return department_context def browse_announcements(): """ @@ -65,7 +51,7 @@ def browse_announcements(): "sm" : sm_ann, "all" : all_ann } - # print(context) + return context def get_make_request(user_id): @@ -111,8 +97,7 @@ def dep_main(request): user_info = ExtraInfo.objects.all().select_related('user','department').filter(user=usrnm).first() ann_maker_id = user_info.id user_info = ExtraInfo.objects.all().select_related('user','department').get(id=ann_maker_id) - user_departmentid = ExtraInfo.objects.all().select_related('user','department').get(id=ann_maker_id).department_id - + requests_made = get_make_request(user_info) fac_view = request.user.holds_designations.filter(designation__name='faculty').exists() @@ -122,10 +107,7 @@ def dep_main(request): context = browse_announcements() context_f = faculty() user_designation = "" - - - department_context = department_information(request) - + if fac_view: user_designation = "faculty" elif student: @@ -149,26 +131,13 @@ def dep_main(request): ) if user_designation == "student": - department_templates = { - 51: 'department/cse_index.html', - 30: 'department/ece_index.html', - 37: 'department/me_index.html', - 53: 'department/sm_index.html' - } - default_template = 'department/cse_index.html' - template_name = department_templates.get(user_departmentid, default_template) - - return render(request, template_name, { - "announcements": context, - "fac_list": context_f, - "requests_made": requests_made, - "department_info": department_context - - }) - + return render(request,"department/index.html", {"announcements":context, + "fac_list" : context_f, + "requests_made" : requests_made + }) + # elif(str(user.extrainfo.user_type)=="faculty"): elif user_designation=="faculty": return HttpResponseRedirect("facView") - elif user_designation=="staff": return HttpResponseRedirect("staffView") @@ -186,15 +155,11 @@ def faculty_view(request): department, ann_date, user_info - Gets and store data from FORM used for Announcements. """ - context_f = faculty() usrnm = get_object_or_404(User, username=request.user.username) user_info = ExtraInfo.objects.all().select_related('user','department').filter(user=usrnm).first() num = 1 ann_maker_id = user_info.id requests_received = get_to_request(usrnm) - user_departmentid = ExtraInfo.objects.all().select_related('user','department').get(id=ann_maker_id).department_id - department_context = department_information(request) - if request.method == 'POST': batch = request.POST.get('batch', '') programme = request.POST.get('programme', '') @@ -213,29 +178,13 @@ def faculty_view(request): upload_announcement=upload_announcement, department = department, ann_date=ann_date) - - department_notif(usrnm, recipients , message) + # department_notif(usrnm, recipients , message) context = browse_announcements() - - department_templates = { - 51: 'department/csedep_request.html', - 30: 'department/ecedep_request.html', - 37: 'department/medep_request.html', - 53: 'department/smdep_request.html' - } - default_template = 'department/dep_request.html' - - template_name = department_templates.get(user_departmentid, default_template) - - return render(request, template_name, { - "user_designation": user_info.user_type, - "announcements": context, - "request_to": requests_received, - "fac_list": context_f, - "department_info": department_context - }) - + return render(request, 'department/dep_request.html', {"user_designation":user_info.user_type, + "announcements":context, + "request_to":requests_received + }) def staff_view(request): """ @@ -251,143 +200,39 @@ def staff_view(request): department, ann_date, user_info - Gets and store data from FORM used for Announcements for Students. """ - context_f = faculty() usrnm = get_object_or_404(User, username=request.user.username) user_info = ExtraInfo.objects.all().select_related('user','department').filter(user=usrnm).first() num = 1 ann_maker_id = user_info.id - user_departmentid = ExtraInfo.objects.all().select_related('user','department').get(id=ann_maker_id).department_id - - department_context = department_information(request) - requests_received = get_to_request(usrnm) if request.method == 'POST': - form_type = request.POST.get('form_type', '') - if form_type == 'form1' : - - batch = request.POST.get('batch', '') - programme = request.POST.get('programme', '') - message = request.POST.get('announcement', '') - upload_announcement = request.FILES.get('upload_announcement') - department = request.POST.get('department') - ann_date = date.today() - user_info = ExtraInfo.objects.all().select_related('user','department').get(id=ann_maker_id) - getstudents = ExtraInfo.objects.select_related('user') - recipients = User.objects.filter(extrainfo__in=getstudents) - - obj1, created = Announcements.objects.get_or_create(maker_id=user_info, - batch=batch, - programme=programme, - message=message, - upload_announcement=upload_announcement, - department = department, - ann_date=ann_date) - department_notif(usrnm, recipients , message) - - elif form_type == 'form2' : - - email = request.POST.get('email', '') - phone_number = request.POST.get('contact_number', '') - facilites = request.POST.get('facilities', '') - labs = request.POST.get('labs', '') - department_id = user_departmentid - - # Check if a row with the specified department_id already exists - try: - department_info = Information.objects.get(department_id=department_id) - # If row exists, update the values - department_info.email = email - department_info.phone_number_number = phone_number - department_info.facilites = facilites - department_info.labs = labs - department_info.save() - except Information.DoesNotExist: - # If row does not exist, create a new one - department_info = Information.objects.create( - department_id=department_id, - email=email, - phone_number=phone_number, - facilites=facilites, - labs=labs - ) - + batch = request.POST.get('batch', '') + programme = request.POST.get('programme', '') + message = request.POST.get('announcement', '') + upload_announcement = request.FILES.get('upload_announcement') + department = request.POST.get('department') + ann_date = date.today() + user_info = ExtraInfo.objects.all().select_related('user','department').get(id=ann_maker_id) + getstudents = ExtraInfo.objects.select_related('user') + recipients = User.objects.filter(extrainfo__in=getstudents) + + obj1, created = Announcements.objects.get_or_create(maker_id=user_info, + batch=batch, + programme=programme, + message=message, + upload_announcement=upload_announcement, + department = department, + ann_date=ann_date) + # department_notif(usrnm, recipients , message) context = browse_announcements() - - - department_templates = { - 51: 'department/csedep_request.html', - 30: 'department/ecedep_request.html', - 37: 'department/medep_request.html', - 53: 'department/smdep_request.html', - - } - default_template = 'department/dep_request.html' - - desig=request.session.get('currentDesignationSelected', 'default_value') - if desig=='deptadmin_cse': - template_name = 'department/admin_cse.html' - - return render(request, template_name, { - "user_designation": user_info.user_type, - "announcements": context, - "request_to": requests_received, - "fac_list": context_f, - "department_info": department_context - }) - elif desig=='deptadmin_ece': - template_name = 'department/admin_ece.html' - return render(request, template_name, { - "user_designation": user_info.user_type, - "announcements": context, - "request_to": requests_received, - "fac_list": context_f, - "department_info": department_context - }) - elif desig=='deptadmin_me': - template_name = 'department/admin_me.html' - return render(request, template_name, { - "user_designation": user_info.user_type, - "announcements": context, - "request_to": requests_received, - "fac_list": context_f, - "department_info": department_context - }) - elif desig=='deptadmin_sm': - template_name = 'department/admin_sm.html' - return render(request, template_name, { - "user_designation": user_info.user_type, - "announcements": context, - "request_to": requests_received, - "fac_list": context_f, - "department_info": department_context - }) - - # if desig == 'deptadmin_cse': - # return render(request, 'admin_cse.html') - # elif desig == 'deptadmin_ece': - # return render(request, 'admin_ece.html') - # elif desig == 'deptadmin_sm': - # return render(request, 'admin_sm.html') - # elif desig == 'deptadmin_me': - # return render(request, 'admin_me.html') - # else: - # return render(request, 'default.html') - - template_name = department_templates.get(user_departmentid, default_template) - return render(request, template_name, { - "user_designation": user_info.user_type, - "announcements": context, - "request_to": requests_received, - "fac_list": context_f, - "department_info": department_context - }) - - + return render(request, 'department/dep_request.html', {"user_designation":user_info.user_type, + "announcements":context, + "request_to":requests_received + }) @login_required(login_url='/accounts/login') - -def all_students(request, bid): +def all_students(request,bid): """ This function is used to Return data of Faculties Department-Wise. @@ -400,62 +245,284 @@ def all_students(request, bid): student_list - Stores data pagewise """ + if int(bid)==1: + student_list1=Student.objects.order_by('id').filter(programme='B.Tech', + batch=2021, + id__user_type='student', + id__department__name='CSE').select_related('id') + paginator=Paginator(student_list1,25,orphans=5) + page_number=request.GET.get('page') + student_list=paginator.get_page(page_number) + id_dict={'student_list':student_list,} + return render(request, 'department/AllStudents.html',context=id_dict) + elif int(bid)==11: + student_list1=Student.objects.order_by('id').filter(programme='B.Tech', + batch=2020, + id__user_type='student', + id__department__name='CSE').select_related('id') + paginator=Paginator(student_list1,25,orphans=5) + page_number=request.GET.get('page') + student_list=paginator.get_page(page_number) + id_dict={'student_list':student_list,} + return render(request, 'department/AllStudents.html',context=id_dict) + elif int(bid)==111: + student_list1=Student.objects.order_by('id').filter(programme='B.Tech', + batch=2019, + id__user_type='student', + id__department__name='CSE').select_related('id') + paginator=Paginator(student_list1,25,orphans=5) + page_number=request.GET.get('page') + student_list=paginator.get_page(page_number) + id_dict={'student_list':student_list,} + return render(request, 'department/AllStudents.html',context=id_dict) + elif int(bid)==1111: + student_list1=Student.objects.order_by('id').filter(programme='B.Tech', + batch=2018, + id__user_type='student', + id__department__name='CSE').select_related('id') + paginator=Paginator(student_list1,25,orphans=5) + page_number=request.GET.get('page') + student_list=paginator.get_page(page_number) + id_dict={'student_list':student_list,} + return render(request, 'department/AllStudents.html',context=id_dict) + elif int(bid)==11111: + student_list1=Student.objects.order_by('id').filter(programme='M.Tech', + batch=2021, + id__user_type='student', + id__department__name='CSE').select_related('id') + paginator=Paginator(student_list1,25,orphans=5) + page_number=request.GET.get('page') + student_list=paginator.get_page(page_number) + id_dict={'student_list':student_list,} + return render(request, 'department/AllStudents.html',context=id_dict) + elif int(bid)==111111: + student_list1=Student.objects.order_by('id').filter(programme='M.Tech', + batch=2020, + id__user_type='student', + id__department__name='CSE').select_related('id') + paginator=Paginator(student_list1,25,orphans=5) + page_number=request.GET.get('page') + student_list=paginator.get_page(page_number) + id_dict={'student_list':student_list,} + return render(request, 'department/AllStudents.html',context=id_dict) + elif int(bid)==1111111: + student_list1=Student.objects.order_by('id').filter(programme='PhD', + id__user_type='student', + id__department__name='CSE').select_related('id') + paginator=Paginator(student_list1,25,orphans=5) + page_number=request.GET.get('page') + student_list=paginator.get_page(page_number) + id_dict={'student_list':student_list,} + return render(request, 'department/AllStudents.html',context=id_dict) + elif int(bid)==2: + student_list1=Student.objects.order_by('id').filter(programme='B.Tech', + batch=2021, + id__user_type='student', + id__department__name='ECE').select_related('id') + paginator=Paginator(student_list1,25,orphans=5) + page_number=request.GET.get('page') + student_list=paginator.get_page(page_number) + id_dict={'student_list':student_list,} + return render(request, 'department/AllStudents.html',context=id_dict) + elif int(bid)==21: + student_list1=Student.objects.order_by('id').filter(programme='B.Tech', + batch=2020, + id__user_type='student', + id__department__name='ECE').select_related('id') + paginator=Paginator(student_list1,25,orphans=5) + page_number=request.GET.get('page') + student_list=paginator.get_page(page_number) + id_dict={'student_list':student_list,} + return render(request, 'department/AllStudents.html',context=id_dict) + elif int(bid)==211: + student_list1=Student.objects.order_by('id').filter(programme='B.Tech', + batch=2019, + id__user_type='student', + id__department__name='ECE').select_related('id') + paginator=Paginator(student_list1,25,orphans=5) + page_number=request.GET.get('page') + student_list=paginator.get_page(page_number) + id_dict={'student_list':student_list,} + return render(request, 'department/AllStudents.html',context=id_dict) + elif int(bid)==2111: + student_list1=Student.objects.order_by('id').filter(programme='B.Tech', + batch=2018, + id__user_type='student', + id__department__name='ECE').select_related('id') + paginator=Paginator(student_list1,25,orphans=5) + page_number=request.GET.get('page') + student_list=paginator.get_page(page_number) + id_dict={'student_list':student_list,} + return render(request, 'department/AllStudents.html',context=id_dict) + elif int(bid)==21111: + student_list1=Student.objects.order_by('id').filter(programme='M.Tech', + batch=2021, + id__user_type='student', + id__department__name='ECE').select_related('id') + paginator=Paginator(student_list1,25,orphans=5) + page_number=request.GET.get('page') + student_list=paginator.get_page(page_number) + id_dict={'student_list':student_list,} + return render(request, 'department/AllStudents.html',context=id_dict) + elif int(bid)==211111: + student_list1=Student.objects.order_by('id').filter(programme='M.Tech', + batch=2020, + id__user_type='student', + id__department__name='ECE').select_related('id') + paginator=Paginator(student_list1,25,orphans=5) + page_number=request.GET.get('page') + student_list=paginator.get_page(page_number) + id_dict={'student_list':student_list,} + return render(request, 'department/AllStudents.html',context=id_dict) + elif int(bid)==2111111: + student_list1=Student.objects.order_by('id').filter(programme='PhD', + id__user_type='student', + id__department__name='ECE').select_related('id') + paginator=Paginator(student_list1,25,orphans=5) + page_number=request.GET.get('page') + student_list=paginator.get_page(page_number) + id_dict={'student_list':student_list,} + return render(request, 'department/AllStudents.html',context=id_dict) + elif int(bid)==3: + student_list1=Student.objects.order_by('id').filter(programme='B.Tech', + batch=2021, + id__user_type='student', + id__department__name='ME').select_related('id') + paginator=Paginator(student_list1,25,orphans=5) + page_number=request.GET.get('page') + student_list=paginator.get_page(page_number) + id_dict={'student_list':student_list,} + return render(request, 'department/AllStudents.html',context=id_dict) + elif int(bid)==31: + student_list1=Student.objects.order_by('id').filter(programme='B.Tech', + batch=2020, + id__user_type='student', + id__department__name='ME').select_related('id') + paginator=Paginator(student_list1,25,orphans=5) + page_number=request.GET.get('page') + student_list=paginator.get_page(page_number) + id_dict={'student_list':student_list,} + return render(request, 'department/AllStudents.html',context=id_dict) + elif int(bid)==311: + student_list1=Student.objects.order_by('id').filter(programme='B.Tech', + batch=2019, + id__user_type='student', + id__department__name='ME').select_related('id') + paginator=Paginator(student_list1,25,orphans=5) + page_number=request.GET.get('page') + student_list=paginator.get_page(page_number) + id_dict={'student_list':student_list,} + return render(request, 'department/AllStudents.html',context=id_dict) + elif int(bid)==3111: + student_list1=Student.objects.order_by('id').filter(programme='B.Tech', + batch=2018, + id__user_type='student', + id__department__name='ME').select_related('id') + paginator=Paginator(student_list1,25,orphans=5) + page_number=request.GET.get('page') + student_list=paginator.get_page(page_number) + id_dict={'student_list':student_list,} + return render(request, 'department/AllStudents.html',context=id_dict) + elif int(bid)==31111: + student_list1=Student.objects.order_by('id').filter(programme='M.Tech', + batch=2021, + id__user_type='student', + id__department__name='ME').select_related('id') + paginator=Paginator(student_list1,25,orphans=5) + page_number=request.GET.get('page') + student_list=paginator.get_page(page_number) + id_dict={'student_list':student_list,} + return render(request, 'department/AllStudents.html',context=id_dict) + elif int(bid)==311111: + student_list1=Student.objects.order_by('id').filter(programme='M.Tech', + batch=2020, + id__user_type='student', + id__department__name='ME').select_related('id') + paginator=Paginator(student_list1,25,orphans=5) + page_number=request.GET.get('page') + student_list=paginator.get_page(page_number) + id_dict={'student_list':student_list,} + return render(request, 'department/AllStudents.html',context=id_dict) + elif int(bid)==3111111: + student_list1=Student.objects.order_by('id').filter(programme='PhD', + id__user_type='student', + id__department__name='ME').select_related('id') + paginator=Paginator(student_list1,25,orphans=5) + page_number=request.GET.get('page') + student_list=paginator.get_page(page_number) + id_dict={'student_list':student_list,} + return render(request, 'department/AllStudents.html',context=id_dict) + elif int(bid)==4: + student_list1=Student.objects.order_by('id').filter(programme='B.Tech', + batch=2021, + id__user_type='student', + id__department__name='SM').select_related('id') + paginator=Paginator(student_list1,25,orphans=5) + page_number=request.GET.get('page') + student_list=paginator.get_page(page_number) + id_dict={'student_list':student_list,} + return render(request, 'department/AllStudents.html',context=id_dict) + elif int(bid)==41: + student_list1=Student.objects.order_by('id').filter(programme='B.Tech', + batch=2020, + id__user_type='student', + id__department__name='SM').select_related('id') + paginator=Paginator(student_list1,25,orphans=5) + page_number=request.GET.get('page') + student_list=paginator.get_page(page_number) + id_dict={'student_list':student_list,} + return render(request, 'department/AllStudents.html',context=id_dict) + elif int(bid)==411: + student_list1=Student.objects.order_by('id').filter(programme='B.Tech', + batch=2019, + id__user_type='student', + id__department__name='SM').select_related('id') + paginator=Paginator(student_list1,25,orphans=5) + page_number=request.GET.get('page') + student_list=paginator.get_page(page_number) + id_dict={'student_list':student_list,} + return render(request, 'department/AllStudents.html',context=id_dict) + elif int(bid)==4111: + student_list1=Student.objects.order_by('id').filter(programme='B.Tech', + batch=2018, + id__user_type='student', + id__department__name='SM').select_related('id') + paginator=Paginator(student_list1,25,orphans=5) + page_number=request.GET.get('page') + student_list=paginator.get_page(page_number) + id_dict={'student_list':student_list,} + return render(request, 'department/AllStudents.html',context=id_dict) + elif int(bid)==41111: + student_list1=Student.objects.order_by('id').filter(programme='M.Tech', + batch=2021, + id__user_type='student', + id__department__name='SM').select_related('id') + paginator=Paginator(student_list1,25,orphans=5) + page_number=request.GET.get('page') + student_list=paginator.get_page(page_number) + id_dict={'student_list':student_list,} + return render(request, 'department/AllStudents.html',context=id_dict) + elif int(bid)==411111: + student_list1=Student.objects.order_by('id').filter(programme='M.Tech', + batch=2020, + id__user_type='student', + id__department__name='SM').select_related('id') + paginator=Paginator(student_list1,25,orphans=5) + page_number=request.GET.get('page') + student_list=paginator.get_page(page_number) + id_dict={'student_list':student_list,} + return render(request, 'department/AllStudents.html',context=id_dict) + elif int(bid)==4111111: + student_list1=Student.objects.order_by('id').filter(programme='PhD', + id__user_type='student', + id__department__name='SM').select_related('id') + paginator=Paginator(student_list1,25,orphans=5) + page_number=request.GET.get('page') + student_list=paginator.get_page(page_number) + id_dict={'student_list':student_list,} + return render(request, 'department/AllStudents.html',context=id_dict) - def decode_bid(bid): - """Decodes the bid structure into programme, batch, and department (if applicable).""" - - try: - department_code = bid[0] - programme = { - '1': 'B.Tech', - '2': 'M.Tech', - '3': 'PhD', - - }[department_code] - batch = 2021 - len(bid) + 1 - return {'programme': programme, 'batch': batch} - except (IndexError, KeyError): - return None # Handle malformed bid values - # Get sort parameter from the request - sort_by = request.GET.get('sort_by', None) # No default sort - last_sort = request.session.get('last_sort', None) - - # Decode bid into filter criteria - filter_criteria = decode_bid(bid) - if not filter_criteria: - return HttpResponseBadRequest("Invalid bid value") - - # Apply additional department filter since it seems fixed - filter_criteria['id__department__name'] = 'CSE' - - # Apply sort parameter to the queryset - if sort_by: - if last_sort == sort_by: - sort_by = '-' + sort_by # Reverse the order - try: - student_list1 = Student.objects.order_by(sort_by).filter( - id__user_type='student', - **filter_criteria - ).select_related('id') - except: - # If the sort field doesn't exist or isn't sortable, ignore the sort parameter - student_list1 = Student.objects.filter( - id__user_type='student', - **filter_criteria - ).select_related('id') - request.session['last_sort'] = sort_by # Save the sort parameter for the next request - else: - student_list1 = Student.objects.filter( - id__user_type='student', - **filter_criteria - ).select_related('id') - - paginator = Paginator(student_list1, 25, orphans=5) - page_number = request.GET.get('page') - student_list = paginator.get_page(page_number) - id_dict = {'student_list': student_list} - return render(request, 'department/AllStudents.html', context=id_dict) - + def faculty(): """ @@ -487,35 +554,8 @@ def faculty(): } - # print(cse_f) return context_f - -def alumni(request): - """ - This function is used to Return data of Alumni Department-Wise. - - @variables: - cse_a - Stores data of alumni from CSE Department - ece_a - Stores data of alumni from ECE Department - me_a - Stores data of alumni from ME Department - sm_a - Stores data of alumni from ME Department - context_a - Stores all above variables in Dictionary - - """ - cse_a=ExtraInfo.objects.filter(department__name='CSE',user_type='alumni') - ece_a=ExtraInfo.objects.filter(department__name='ECE',user_type='alumni') - me_a=ExtraInfo.objects.filter(department__name='ME',user_type='alumni') - sm_a=ExtraInfo.objects.filter(department__name='SM',user_type='alumni') - - context_a = { - "cse_a" : cse_a, - "ece_a" : ece_a, - "me_a" : me_a, - "sm_a" : sm_a - } - return render(request, 'department/alumni.html', context_a) - def approved(request): """ This function is used to approve requests. @@ -532,7 +572,6 @@ def approved(request): request.method = '' return redirect('/dep/facView/') - def deny(request): """ This function is used to deny requests. diff --git a/FusionIIIT/applications/eis/migrations/0001_initial.py b/FusionIIIT/applications/eis/migrations/0001_initial.py index 265b34ac7..2fd2e65d3 100644 --- a/FusionIIIT/applications/eis/migrations/0001_initial.py +++ b/FusionIIIT/applications/eis/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 3.1.5 on 2024-07-16 15:44 +# Generated by Django 3.1.5 on 2023-03-15 18:53 import datetime from django.conf import settings @@ -11,8 +11,8 @@ class Migration(migrations.Migration): initial = True dependencies = [ - ('auth', '0012_alter_user_first_name_max_length'), migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('auth', '0012_alter_user_first_name_max_length'), ] operations = [ @@ -49,7 +49,7 @@ class Migration(migrations.Migration): name='emp_techtransfer', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('pf_no', models.CharField(max_length=20)), + ('pf_no', models.IntegerField()), ('details', models.CharField(default=' ', max_length=500)), ('date_entry', models.DateField(blank=True, default=datetime.datetime.now, null=True)), ('start_date', models.DateField(blank=True, null=True)), @@ -61,7 +61,7 @@ class Migration(migrations.Migration): name='emp_session_chair', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('pf_no', models.CharField(max_length=20)), + ('pf_no', models.IntegerField()), ('name', models.CharField(default=' ', max_length=500)), ('event', models.TextField(default=' ', max_length=2500)), ('s_year', models.IntegerField(blank=True, choices=[(1995, 1995), (1996, 1996), (1997, 1997), (1998, 1998), (1999, 1999), (2000, 2000), (2001, 2001), (2002, 2002), (2003, 2003), (2004, 2004), (2005, 2005), (2006, 2006), (2007, 2007), (2008, 2008), (2009, 2009), (2010, 2010), (2011, 2011), (2012, 2012), (2013, 2013), (2014, 2014), (2015, 2015), (2016, 2016), (2017, 2017)], null=True, verbose_name='year')), @@ -77,7 +77,7 @@ class Migration(migrations.Migration): name='emp_research_projects', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('pf_no', models.CharField(max_length=20)), + ('pf_no', models.IntegerField()), ('ptype', models.CharField(default='Research', max_length=100)), ('pi', models.CharField(default=' ', max_length=1000)), ('co_pi', models.CharField(default=' ', max_length=1500)), @@ -96,7 +96,7 @@ class Migration(migrations.Migration): name='emp_research_papers', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('pf_no', models.CharField(max_length=20)), + ('pf_no', models.IntegerField()), ('rtype', models.CharField(choices=[('Journal', 'Journal'), ('Conference', 'Conference')], default='Conference', max_length=500)), ('authors', models.CharField(blank=True, max_length=2500, null=True)), ('co_authors', models.CharField(blank=True, max_length=2500, null=True)), @@ -113,7 +113,7 @@ class Migration(migrations.Migration): ('end_date', models.DateField(blank=True, null=True)), ('date_acceptance', models.DateField(blank=True, null=True)), ('date_publication', models.DateField(blank=True, null=True)), - ('year', models.CharField(blank=True, choices=[(1995, 1995), (1996, 1996), (1997, 1997), (1998, 1998), (1999, 1999), (2000, 2000), (2001, 2001), (2002, 2002), (2003, 2003), (2004, 2004), (2005, 2005), (2006, 2006), (2007, 2007), (2008, 2008), (2009, 2009), (2010, 2010), (2011, 2011), (2012, 2012), (2013, 2013), (2014, 2014), (2015, 2015), (2016, 2016), (2017, 2017), (2018, 2018), (2019, 2019), (2020, 2020), (2021, 2021), (2022, 2022), (2023, 2023), (2024, 2024)], max_length=10, null=True)), + ('year', models.CharField(blank=True, choices=[(1995, 1995), (1996, 1996), (1997, 1997), (1998, 1998), (1999, 1999), (2000, 2000), (2001, 2001), (2002, 2002), (2003, 2003), (2004, 2004), (2005, 2005), (2006, 2006), (2007, 2007), (2008, 2008), (2009, 2009), (2010, 2010), (2011, 2011), (2012, 2012), (2013, 2013), (2014, 2014), (2015, 2015), (2016, 2016), (2017, 2017), (2018, 2018), (2019, 2019), (2020, 2020), (2021, 2021), (2022, 2022), (2023, 2023)], max_length=10, null=True)), ('a_month', models.CharField(blank=True, choices=[(1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6), (7, 7), (8, 8), (9, 9), (10, 10), (11, 11), (12, 12)], default=1, max_length=500, null=True)), ('doc_id', models.CharField(blank=True, max_length=50, null=True)), ('doc_description', models.CharField(blank=True, max_length=1000, null=True)), @@ -128,11 +128,11 @@ class Migration(migrations.Migration): name='emp_published_books', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('pf_no', models.CharField(max_length=20)), + ('pf_no', models.IntegerField()), ('p_type', models.CharField(choices=[('Book', 'Book'), ('Monograph', 'Monograph'), ('Book Chapter', 'Book Chapter'), ('Handbook', 'Handbook'), ('Technical Report', 'Technical Report')], max_length=16)), ('title', models.CharField(default=' ', max_length=2500)), ('publisher', models.CharField(default=' ', max_length=2500)), - ('pyear', models.IntegerField(blank=True, choices=[(1995, 1995), (1996, 1996), (1997, 1997), (1998, 1998), (1999, 1999), (2000, 2000), (2001, 2001), (2002, 2002), (2003, 2003), (2004, 2004), (2005, 2005), (2006, 2006), (2007, 2007), (2008, 2008), (2009, 2009), (2010, 2010), (2011, 2011), (2012, 2012), (2013, 2013), (2014, 2014), (2015, 2015), (2016, 2016), (2017, 2017), (2018, 2018), (2019, 2019), (2020, 2020), (2021, 2021), (2022, 2022), (2023, 2023), (2024, 2024)], null=True, verbose_name='year')), + ('pyear', models.IntegerField(blank=True, choices=[(1995, 1995), (1996, 1996), (1997, 1997), (1998, 1998), (1999, 1999), (2000, 2000), (2001, 2001), (2002, 2002), (2003, 2003), (2004, 2004), (2005, 2005), (2006, 2006), (2007, 2007), (2008, 2008), (2009, 2009), (2010, 2010), (2011, 2011), (2012, 2012), (2013, 2013), (2014, 2014), (2015, 2015), (2016, 2016), (2017, 2017), (2018, 2018), (2019, 2019), (2020, 2020), (2021, 2021), (2022, 2022), (2023, 2023)], null=True, verbose_name='year')), ('authors', models.CharField(default=' ', max_length=250)), ('date_entry', models.DateField(blank=True, default=datetime.datetime.now, null=True)), ('publication_date', models.DateField(blank=True, null=True)), @@ -143,12 +143,12 @@ class Migration(migrations.Migration): name='emp_patents', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('pf_no', models.CharField(max_length=20)), + ('pf_no', models.IntegerField()), ('p_no', models.CharField(max_length=150)), ('title', models.CharField(max_length=1500)), ('earnings', models.IntegerField(default=0)), ('status', models.CharField(choices=[('Filed', 'Filed'), ('Granted', 'Granted'), ('Published', 'Published'), ('Owned', 'Owned')], max_length=15)), - ('p_year', models.IntegerField(blank=True, choices=[(1995, 1995), (1996, 1996), (1997, 1997), (1998, 1998), (1999, 1999), (2000, 2000), (2001, 2001), (2002, 2002), (2003, 2003), (2004, 2004), (2005, 2005), (2006, 2006), (2007, 2007), (2008, 2008), (2009, 2009), (2010, 2010), (2011, 2011), (2012, 2012), (2013, 2013), (2014, 2014), (2015, 2015), (2016, 2016), (2017, 2017), (2018, 2018), (2019, 2019), (2020, 2020), (2021, 2021), (2022, 2022), (2023, 2023), (2024, 2024)], null=True, verbose_name='year')), + ('p_year', models.IntegerField(blank=True, choices=[(1995, 1995), (1996, 1996), (1997, 1997), (1998, 1998), (1999, 1999), (2000, 2000), (2001, 2001), (2002, 2002), (2003, 2003), (2004, 2004), (2005, 2005), (2006, 2006), (2007, 2007), (2008, 2008), (2009, 2009), (2010, 2010), (2011, 2011), (2012, 2012), (2013, 2013), (2014, 2014), (2015, 2015), (2016, 2016), (2017, 2017), (2018, 2018), (2019, 2019), (2020, 2020), (2021, 2021), (2022, 2022), (2023, 2023)], null=True, verbose_name='year')), ('a_month', models.IntegerField(blank=True, choices=[(1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6), (7, 7), (8, 8), (9, 9), (10, 10), (11, 11), (12, 12)], default=1, null=True, verbose_name='Month')), ('date_entry', models.DateField(blank=True, default=datetime.datetime.now, null=True)), ('start_date', models.DateField(blank=True, null=True)), @@ -160,14 +160,14 @@ class Migration(migrations.Migration): name='emp_mtechphd_thesis', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('pf_no', models.CharField(max_length=20)), + ('pf_no', models.IntegerField()), ('degree_type', models.IntegerField(default=1)), ('title', models.CharField(max_length=250)), ('supervisors', models.CharField(max_length=250)), ('co_supervisors', models.CharField(blank=True, max_length=250, null=True)), ('rollno', models.CharField(max_length=200)), ('s_name', models.CharField(max_length=5000)), - ('s_year', models.IntegerField(blank=True, choices=[(1995, 1995), (1996, 1996), (1997, 1997), (1998, 1998), (1999, 1999), (2000, 2000), (2001, 2001), (2002, 2002), (2003, 2003), (2004, 2004), (2005, 2005), (2006, 2006), (2007, 2007), (2008, 2008), (2009, 2009), (2010, 2010), (2011, 2011), (2012, 2012), (2013, 2013), (2014, 2014), (2015, 2015), (2016, 2016), (2017, 2017), (2018, 2018), (2019, 2019), (2020, 2020), (2021, 2021), (2022, 2022), (2023, 2023), (2024, 2024)], null=True, verbose_name='year')), + ('s_year', models.IntegerField(blank=True, choices=[(1995, 1995), (1996, 1996), (1997, 1997), (1998, 1998), (1999, 1999), (2000, 2000), (2001, 2001), (2002, 2002), (2003, 2003), (2004, 2004), (2005, 2005), (2006, 2006), (2007, 2007), (2008, 2008), (2009, 2009), (2010, 2010), (2011, 2011), (2012, 2012), (2013, 2013), (2014, 2014), (2015, 2015), (2016, 2016), (2017, 2017), (2018, 2018), (2019, 2019), (2020, 2020), (2021, 2021), (2022, 2022), (2023, 2023)], null=True, verbose_name='year')), ('a_month', models.IntegerField(blank=True, choices=[(1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6), (7, 7), (8, 8), (9, 9), (10, 10), (11, 11), (12, 12)], default=1, null=True, verbose_name='Month')), ('date_entry', models.DateField(blank=True, default=datetime.datetime.now, null=True)), ('start_date', models.DateField(blank=True, null=True)), @@ -181,14 +181,14 @@ class Migration(migrations.Migration): name='emp_keynote_address', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('pf_no', models.CharField(max_length=20)), + ('pf_no', models.IntegerField()), ('type', models.CharField(choices=[('Keynote', 'Keynote'), ('Plenary Address', 'Plenary Address')], default='Keynote', max_length=140)), ('title', models.CharField(max_length=1000)), ('name', models.CharField(max_length=1000)), ('venue', models.CharField(max_length=1000)), ('page_no', models.CharField(max_length=100)), ('isbn_no', models.CharField(max_length=200)), - ('k_year', models.IntegerField(blank=True, choices=[(1995, 1995), (1996, 1996), (1997, 1997), (1998, 1998), (1999, 1999), (2000, 2000), (2001, 2001), (2002, 2002), (2003, 2003), (2004, 2004), (2005, 2005), (2006, 2006), (2007, 2007), (2008, 2008), (2009, 2009), (2010, 2010), (2011, 2011), (2012, 2012), (2013, 2013), (2014, 2014), (2015, 2015), (2016, 2016), (2017, 2017), (2018, 2018), (2019, 2019), (2020, 2020), (2021, 2021), (2022, 2022), (2023, 2023), (2024, 2024)], null=True, verbose_name='year')), + ('k_year', models.IntegerField(blank=True, choices=[(1995, 1995), (1996, 1996), (1997, 1997), (1998, 1998), (1999, 1999), (2000, 2000), (2001, 2001), (2002, 2002), (2003, 2003), (2004, 2004), (2005, 2005), (2006, 2006), (2007, 2007), (2008, 2008), (2009, 2009), (2010, 2010), (2011, 2011), (2012, 2012), (2013, 2013), (2014, 2014), (2015, 2015), (2016, 2016), (2017, 2017), (2018, 2018), (2019, 2019), (2020, 2020), (2021, 2021), (2022, 2022), (2023, 2023)], null=True, verbose_name='year')), ('a_month', models.IntegerField(blank=True, choices=[(1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6), (7, 7), (8, 8), (9, 9), (10, 10), (11, 11), (12, 12)], default=1, null=True, verbose_name='Month')), ('start_date', models.DateField(blank=True, null=True)), ('end_date', models.DateField(blank=True, null=True)), @@ -200,12 +200,12 @@ class Migration(migrations.Migration): name='emp_expert_lectures', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('pf_no', models.CharField(max_length=20)), + ('pf_no', models.IntegerField()), ('l_type', models.CharField(choices=[('Expert Lecture', 'Expert Lecture'), ('Invited Talk', 'Invited Talk')], default='Expert Lecture', max_length=14)), ('title', models.CharField(max_length=1000)), ('place', models.CharField(max_length=1000)), ('l_date', models.DateField(blank=True, null=True)), - ('l_year', models.IntegerField(blank=True, choices=[(1995, 1995), (1996, 1996), (1997, 1997), (1998, 1998), (1999, 1999), (2000, 2000), (2001, 2001), (2002, 2002), (2003, 2003), (2004, 2004), (2005, 2005), (2006, 2006), (2007, 2007), (2008, 2008), (2009, 2009), (2010, 2010), (2011, 2011), (2012, 2012), (2013, 2013), (2014, 2014), (2015, 2015), (2016, 2016), (2017, 2017), (2018, 2018), (2019, 2019), (2020, 2020), (2021, 2021), (2022, 2022), (2023, 2023), (2024, 2024)], null=True, verbose_name='year')), + ('l_year', models.IntegerField(blank=True, choices=[(1995, 1995), (1996, 1996), (1997, 1997), (1998, 1998), (1999, 1999), (2000, 2000), (2001, 2001), (2002, 2002), (2003, 2003), (2004, 2004), (2005, 2005), (2006, 2006), (2007, 2007), (2008, 2008), (2009, 2009), (2010, 2010), (2011, 2011), (2012, 2012), (2013, 2013), (2014, 2014), (2015, 2015), (2016, 2016), (2017, 2017), (2018, 2018), (2019, 2019), (2020, 2020), (2021, 2021), (2022, 2022), (2023, 2023)], null=True, verbose_name='year')), ('a_month', models.IntegerField(blank=True, choices=[(1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6), (7, 7), (8, 8), (9, 9), (10, 10), (11, 11), (12, 12)], default=1, null=True, verbose_name='Month')), ('date_entry', models.DateField(blank=True, default=datetime.datetime.now, null=True)), ('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), @@ -215,7 +215,7 @@ class Migration(migrations.Migration): name='emp_event_organized', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('pf_no', models.CharField(max_length=20)), + ('pf_no', models.IntegerField()), ('type', models.CharField(choices=[('Training Program', 'Training Program'), ('Seminar', 'Seminar'), ('Short Term Program', 'Short Term Program'), ('Workshop', 'Workshop'), ('Any Other', 'Any Other')], max_length=180)), ('name', models.CharField(max_length=1000)), ('sponsoring_agency', models.CharField(max_length=150)), @@ -231,7 +231,7 @@ class Migration(migrations.Migration): name='emp_consultancy_projects', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('pf_no', models.CharField(max_length=20)), + ('pf_no', models.IntegerField()), ('consultants', models.CharField(max_length=150)), ('title', models.CharField(max_length=1000)), ('client', models.CharField(max_length=1000)), @@ -249,10 +249,10 @@ class Migration(migrations.Migration): name='emp_confrence_organised', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('pf_no', models.CharField(max_length=20)), + ('pf_no', models.IntegerField()), ('name', models.CharField(max_length=500)), ('venue', models.CharField(max_length=500)), - ('k_year', models.IntegerField(blank=True, choices=[(1995, 1995), (1996, 1996), (1997, 1997), (1998, 1998), (1999, 1999), (2000, 2000), (2001, 2001), (2002, 2002), (2003, 2003), (2004, 2004), (2005, 2005), (2006, 2006), (2007, 2007), (2008, 2008), (2009, 2009), (2010, 2010), (2011, 2011), (2012, 2012), (2013, 2013), (2014, 2014), (2015, 2015), (2016, 2016), (2017, 2017), (2018, 2018), (2019, 2019), (2020, 2020), (2021, 2021), (2022, 2022), (2023, 2023), (2024, 2024)], null=True, verbose_name='year')), + ('k_year', models.IntegerField(blank=True, choices=[(1995, 1995), (1996, 1996), (1997, 1997), (1998, 1998), (1999, 1999), (2000, 2000), (2001, 2001), (2002, 2002), (2003, 2003), (2004, 2004), (2005, 2005), (2006, 2006), (2007, 2007), (2008, 2008), (2009, 2009), (2010, 2010), (2011, 2011), (2012, 2012), (2013, 2013), (2014, 2014), (2015, 2015), (2016, 2016), (2017, 2017), (2018, 2018), (2019, 2019), (2020, 2020), (2021, 2021), (2022, 2022), (2023, 2023)], null=True, verbose_name='year')), ('a_month', models.IntegerField(blank=True, choices=[(1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6), (7, 7), (8, 8), (9, 9), (10, 10), (11, 11), (12, 12)], default=1, null=True, verbose_name='Month')), ('start_date', models.DateField(blank=True, null=True)), ('end_date', models.DateField(blank=True, null=True)), @@ -266,12 +266,12 @@ class Migration(migrations.Migration): name='emp_achievement', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('pf_no', models.CharField(max_length=20)), + ('pf_no', models.IntegerField()), ('a_type', models.CharField(choices=[('Award', 'Award'), ('Honour', 'Honour'), ('Prize', 'Prize'), ('Other', 'Other')], default='Other', max_length=180)), ('details', models.TextField(default=' ', max_length=1550)), ('a_day', models.IntegerField(blank=True, choices=[(1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6), (7, 7), (8, 8), (9, 9), (10, 10), (11, 11), (12, 12), (13, 13), (14, 14), (15, 15), (16, 16), (17, 17), (18, 18), (19, 19), (20, 20), (21, 21), (22, 22), (23, 23), (24, 24), (25, 25), (26, 26), (27, 27), (28, 28), (29, 29), (30, 30), (31, 31)], null=True, verbose_name='Day')), ('a_month', models.IntegerField(blank=True, choices=[(1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6), (7, 7), (8, 8), (9, 9), (10, 10), (11, 11), (12, 12)], null=True, verbose_name='Month')), - ('a_year', models.IntegerField(blank=True, choices=[(1995, 1995), (1996, 1996), (1997, 1997), (1998, 1998), (1999, 1999), (2000, 2000), (2001, 2001), (2002, 2002), (2003, 2003), (2004, 2004), (2005, 2005), (2006, 2006), (2007, 2007), (2008, 2008), (2009, 2009), (2010, 2010), (2011, 2011), (2012, 2012), (2013, 2013), (2014, 2014), (2015, 2015), (2016, 2016), (2017, 2017), (2018, 2018), (2019, 2019), (2020, 2020), (2021, 2021), (2022, 2022), (2023, 2023), (2024, 2024)], null=True, verbose_name='year')), + ('a_year', models.IntegerField(blank=True, choices=[(1995, 1995), (1996, 1996), (1997, 1997), (1998, 1998), (1999, 1999), (2000, 2000), (2001, 2001), (2002, 2002), (2003, 2003), (2004, 2004), (2005, 2005), (2006, 2006), (2007, 2007), (2008, 2008), (2009, 2009), (2010, 2010), (2011, 2011), (2012, 2012), (2013, 2013), (2014, 2014), (2015, 2015), (2016, 2016), (2017, 2017), (2018, 2018), (2019, 2019), (2020, 2020), (2021, 2021), (2022, 2022), (2023, 2023)], null=True, verbose_name='year')), ('date_entry', models.DateField(default=datetime.datetime.now)), ('achievment_date', models.DateField(blank=True, null=True)), ('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), diff --git a/FusionIIIT/applications/eis/migrations/0002_auto_20250201_2228.py b/FusionIIIT/applications/eis/migrations/0002_auto_20250201_2228.py deleted file mode 100644 index c58ceb17b..000000000 --- a/FusionIIIT/applications/eis/migrations/0002_auto_20250201_2228.py +++ /dev/null @@ -1,53 +0,0 @@ -# Generated by Django 3.1.5 on 2025-02-01 22:28 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('eis', '0001_initial'), - ] - - operations = [ - migrations.AlterField( - model_name='emp_achievement', - name='a_year', - field=models.IntegerField(blank=True, choices=[(1995, 1995), (1996, 1996), (1997, 1997), (1998, 1998), (1999, 1999), (2000, 2000), (2001, 2001), (2002, 2002), (2003, 2003), (2004, 2004), (2005, 2005), (2006, 2006), (2007, 2007), (2008, 2008), (2009, 2009), (2010, 2010), (2011, 2011), (2012, 2012), (2013, 2013), (2014, 2014), (2015, 2015), (2016, 2016), (2017, 2017), (2018, 2018), (2019, 2019), (2020, 2020), (2021, 2021), (2022, 2022), (2023, 2023), (2024, 2024), (2025, 2025)], null=True, verbose_name='year'), - ), - migrations.AlterField( - model_name='emp_confrence_organised', - name='k_year', - field=models.IntegerField(blank=True, choices=[(1995, 1995), (1996, 1996), (1997, 1997), (1998, 1998), (1999, 1999), (2000, 2000), (2001, 2001), (2002, 2002), (2003, 2003), (2004, 2004), (2005, 2005), (2006, 2006), (2007, 2007), (2008, 2008), (2009, 2009), (2010, 2010), (2011, 2011), (2012, 2012), (2013, 2013), (2014, 2014), (2015, 2015), (2016, 2016), (2017, 2017), (2018, 2018), (2019, 2019), (2020, 2020), (2021, 2021), (2022, 2022), (2023, 2023), (2024, 2024), (2025, 2025)], null=True, verbose_name='year'), - ), - migrations.AlterField( - model_name='emp_expert_lectures', - name='l_year', - field=models.IntegerField(blank=True, choices=[(1995, 1995), (1996, 1996), (1997, 1997), (1998, 1998), (1999, 1999), (2000, 2000), (2001, 2001), (2002, 2002), (2003, 2003), (2004, 2004), (2005, 2005), (2006, 2006), (2007, 2007), (2008, 2008), (2009, 2009), (2010, 2010), (2011, 2011), (2012, 2012), (2013, 2013), (2014, 2014), (2015, 2015), (2016, 2016), (2017, 2017), (2018, 2018), (2019, 2019), (2020, 2020), (2021, 2021), (2022, 2022), (2023, 2023), (2024, 2024), (2025, 2025)], null=True, verbose_name='year'), - ), - migrations.AlterField( - model_name='emp_keynote_address', - name='k_year', - field=models.IntegerField(blank=True, choices=[(1995, 1995), (1996, 1996), (1997, 1997), (1998, 1998), (1999, 1999), (2000, 2000), (2001, 2001), (2002, 2002), (2003, 2003), (2004, 2004), (2005, 2005), (2006, 2006), (2007, 2007), (2008, 2008), (2009, 2009), (2010, 2010), (2011, 2011), (2012, 2012), (2013, 2013), (2014, 2014), (2015, 2015), (2016, 2016), (2017, 2017), (2018, 2018), (2019, 2019), (2020, 2020), (2021, 2021), (2022, 2022), (2023, 2023), (2024, 2024), (2025, 2025)], null=True, verbose_name='year'), - ), - migrations.AlterField( - model_name='emp_mtechphd_thesis', - name='s_year', - field=models.IntegerField(blank=True, choices=[(1995, 1995), (1996, 1996), (1997, 1997), (1998, 1998), (1999, 1999), (2000, 2000), (2001, 2001), (2002, 2002), (2003, 2003), (2004, 2004), (2005, 2005), (2006, 2006), (2007, 2007), (2008, 2008), (2009, 2009), (2010, 2010), (2011, 2011), (2012, 2012), (2013, 2013), (2014, 2014), (2015, 2015), (2016, 2016), (2017, 2017), (2018, 2018), (2019, 2019), (2020, 2020), (2021, 2021), (2022, 2022), (2023, 2023), (2024, 2024), (2025, 2025)], null=True, verbose_name='year'), - ), - migrations.AlterField( - model_name='emp_patents', - name='p_year', - field=models.IntegerField(blank=True, choices=[(1995, 1995), (1996, 1996), (1997, 1997), (1998, 1998), (1999, 1999), (2000, 2000), (2001, 2001), (2002, 2002), (2003, 2003), (2004, 2004), (2005, 2005), (2006, 2006), (2007, 2007), (2008, 2008), (2009, 2009), (2010, 2010), (2011, 2011), (2012, 2012), (2013, 2013), (2014, 2014), (2015, 2015), (2016, 2016), (2017, 2017), (2018, 2018), (2019, 2019), (2020, 2020), (2021, 2021), (2022, 2022), (2023, 2023), (2024, 2024), (2025, 2025)], null=True, verbose_name='year'), - ), - migrations.AlterField( - model_name='emp_published_books', - name='pyear', - field=models.IntegerField(blank=True, choices=[(1995, 1995), (1996, 1996), (1997, 1997), (1998, 1998), (1999, 1999), (2000, 2000), (2001, 2001), (2002, 2002), (2003, 2003), (2004, 2004), (2005, 2005), (2006, 2006), (2007, 2007), (2008, 2008), (2009, 2009), (2010, 2010), (2011, 2011), (2012, 2012), (2013, 2013), (2014, 2014), (2015, 2015), (2016, 2016), (2017, 2017), (2018, 2018), (2019, 2019), (2020, 2020), (2021, 2021), (2022, 2022), (2023, 2023), (2024, 2024), (2025, 2025)], null=True, verbose_name='year'), - ), - migrations.AlterField( - model_name='emp_research_papers', - name='year', - field=models.CharField(blank=True, choices=[(1995, 1995), (1996, 1996), (1997, 1997), (1998, 1998), (1999, 1999), (2000, 2000), (2001, 2001), (2002, 2002), (2003, 2003), (2004, 2004), (2005, 2005), (2006, 2006), (2007, 2007), (2008, 2008), (2009, 2009), (2010, 2010), (2011, 2011), (2012, 2012), (2013, 2013), (2014, 2014), (2015, 2015), (2016, 2016), (2017, 2017), (2018, 2018), (2019, 2019), (2020, 2020), (2021, 2021), (2022, 2022), (2023, 2023), (2024, 2024), (2025, 2025)], max_length=10, null=True), - ), - ] diff --git a/FusionIIIT/applications/eis/models.py b/FusionIIIT/applications/eis/models.py index b43ae974e..e55712ab9 100644 --- a/FusionIIIT/applications/eis/models.py +++ b/FusionIIIT/applications/eis/models.py @@ -26,7 +26,7 @@ def get_absolute_url(self): class emp_techtransfer(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE, blank=True,null=True) - pf_no = models.CharField(max_length=20) + pf_no = models.IntegerField() details = models.CharField(max_length=500, default=" ") date_entry = models.DateField(null=True, blank=True, default=datetime.datetime.now) start_date = models.DateField(null=True,blank=True) @@ -35,7 +35,7 @@ class emp_techtransfer(models.Model): class emp_session_chair(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE, blank=True,null=True) - pf_no = models.CharField(max_length=20) + pf_no = models.IntegerField() name = models.CharField(max_length=500, default=" ") event = models.TextField(max_length=2500, default=" ") YEAR_CHOICES = [] @@ -57,7 +57,7 @@ def __str__(self): class emp_research_projects(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE, blank=True,null=True) - pf_no = models.CharField(max_length=20) + pf_no = models.IntegerField() ptype = models.CharField(max_length=100, default="Research") pi = models.CharField(max_length=1000, default=" ") co_pi = models.CharField(max_length=1500, default=" ") @@ -82,7 +82,7 @@ def __str__(self): class emp_research_papers(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE, blank=True,null=True) - pf_no = models.CharField(max_length=20) + pf_no = models.IntegerField() R_TYPE_CHOICES = ( ('Journal', 'Journal'), ('Conference', 'Conference'), @@ -133,7 +133,7 @@ def __str__(self): class emp_published_books(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE, blank=True,null=True) - pf_no = models.CharField(max_length=20) + pf_no = models.IntegerField() PTYPE_TYPE_CHOICES = ( ('Book', 'Book'), ('Monograph', 'Monograph'), @@ -158,7 +158,7 @@ def __str__(self): class emp_patents(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE, blank=True,null=True) - pf_no = models.CharField(max_length=20) + pf_no = models.IntegerField() p_no = models.CharField(max_length=150) title = models.CharField(max_length=1500) earnings = models.IntegerField(default=0) @@ -186,7 +186,7 @@ def __str__(self): class emp_mtechphd_thesis(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE, blank=True,null=True) - pf_no = models.CharField(max_length=20) + pf_no = models.IntegerField() degree_type = models.IntegerField(default=1) title = models.CharField(max_length=250) supervisors = models.CharField(max_length=250) @@ -219,7 +219,7 @@ def __str__(self): class emp_keynote_address(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE, blank=True,null=True) - pf_no = models.CharField(max_length=20) + pf_no = models.IntegerField() KEYNOTE_TYPE_CHOICES = ( ('Keynote', 'Keynote'), ('Plenary Address', 'Plenary Address'), @@ -248,7 +248,7 @@ def __str__(self): class emp_expert_lectures(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE, blank=True,null=True) - pf_no = models.CharField(max_length=20) + pf_no = models.IntegerField() LECTURE_TYPE_CHOICES = ( ('Expert Lecture', 'Expert Lecture'), ('Invited Talk', 'Invited Talk'), @@ -273,7 +273,7 @@ def __str__(self): class emp_event_organized(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE, blank=True,null=True) - pf_no = models.CharField(max_length=20) + pf_no = models.IntegerField() TYPE_CHOICES = ( ('Training Program', 'Training Program'), ('Seminar', 'Seminar'), @@ -301,7 +301,7 @@ def __str__(self): class emp_consultancy_projects(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE, blank=True,null=True) - pf_no = models.CharField(max_length=20) + pf_no = models.IntegerField() consultants = models.CharField(max_length=150) title = models.CharField(max_length=1000) client = models.CharField(max_length=1000) @@ -323,7 +323,7 @@ def __str__(self): class emp_confrence_organised(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE, blank=True,null=True) - pf_no = models.CharField(max_length=20) + pf_no = models.IntegerField() name = models.CharField(max_length=500) venue = models.CharField(max_length=500) YEAR_CHOICES = [] @@ -353,7 +353,7 @@ def __str__(self): class emp_achievement(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE, blank=True,null=True) - pf_no = models.CharField(max_length=20) + pf_no = models.IntegerField() A_TYPE_CHOICES = ( ('Award', 'Award'), ('Honour', 'Honour'), diff --git a/FusionIIIT/applications/eis/views.py b/FusionIIIT/applications/eis/views.py index 656eee099..76a4532c2 100644 --- a/FusionIIIT/applications/eis/views.py +++ b/FusionIIIT/applications/eis/views.py @@ -275,12 +275,10 @@ # Main profile landing view def profile(request, username=None): - print("eis",username) user = get_object_or_404(User, username=username) if username else request.user - extra_info = get_object_or_404(ExtraInfo, user=user) -# if extra_info.user_type != 'fx': -# return redirect('/') + if extra_info.user_type != 'faculty': + return redirect('/') pf = extra_info.id form = ConfrenceForm() diff --git a/FusionIIIT/applications/establishment/migrations/0001_initial.py b/FusionIIIT/applications/establishment/migrations/0001_initial.py index 51caaf80b..e1449d3b3 100644 --- a/FusionIIIT/applications/establishment/migrations/0001_initial.py +++ b/FusionIIIT/applications/establishment/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 3.1.5 on 2024-07-16 15:44 +# Generated by Django 3.1.5 on 2023-03-15 18:53 from django.conf import settings from django.db import migrations, models @@ -10,9 +10,9 @@ class Migration(migrations.Migration): initial = True dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), ('globals', '0001_initial'), ('auth', '0012_alter_user_first_name_max_length'), - migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] operations = [ diff --git a/FusionIIIT/applications/estate_module/migrations/0001_initial.py b/FusionIIIT/applications/estate_module/migrations/0001_initial.py index 7d80b342e..478a93b83 100644 --- a/FusionIIIT/applications/estate_module/migrations/0001_initial.py +++ b/FusionIIIT/applications/estate_module/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 3.1.5 on 2024-07-16 15:44 +# Generated by Django 3.1.5 on 2023-03-15 18:53 from django.conf import settings from django.db import migrations, models diff --git a/FusionIIIT/applications/estate_module/static/estate_module/sidebar.js b/FusionIIIT/applications/estate_module/static/estate_module/sidebar.js index 449a7f5c8..462ebb004 100644 --- a/FusionIIIT/applications/estate_module/static/estate_module/sidebar.js +++ b/FusionIIIT/applications/estate_module/static/estate_module/sidebar.js @@ -36,7 +36,7 @@ $(document).ready(function () { .sidebar('setting', 'transition', 'overlay'); }); function newNotification() { - // console.log('A new Notification added!'); + console.log('A new Notification added!'); var notifactionName = document.createTextNode('Kanishka Munshi'); var notificationEmail = document.createTextNode('gmail@zlatan.com'); diff --git a/FusionIIIT/applications/examination/admin.py b/FusionIIIT/applications/examination/admin.py deleted file mode 100644 index c44d2718b..000000000 --- a/FusionIIIT/applications/examination/admin.py +++ /dev/null @@ -1,8 +0,0 @@ -from django.contrib import admin -from .models import hidden_grades,authentication,grade - -# Register your models here. - -admin.site.register(hidden_grades) -admin.site.register(authentication) -admin.site.register(grade) \ No newline at end of file diff --git a/FusionIIIT/applications/examination/api/serializers.py b/FusionIIIT/applications/examination/api/serializers.py deleted file mode 100644 index 5f3ca03b4..000000000 --- a/FusionIIIT/applications/examination/api/serializers.py +++ /dev/null @@ -1,22 +0,0 @@ -from rest_framework.authtoken.models import Token -from rest_framework import serializers -from applications.department.models import(Announcements) - - -from applications.academic_procedures.models import (course_registration) - -class CourseRegistrationSerializer(serializers.ModelSerializer): - class Meta: - model = course_registration - fields = ('__all__') - - -class AnnouncementsSerializer(serializers.ModelSerializer): - class Meta: - model = Announcements - fields = ['maker_id', 'ann_date', 'message', 'batch', 'department', 'programme', 'upload_announcement'] - -class AuthenticationSerializer(serializers.ModelSerializer): - class Meta: - model = Announcements - fields = '__all__' \ No newline at end of file diff --git a/FusionIIIT/applications/examination/api/urls.py b/FusionIIIT/applications/examination/api/urls.py deleted file mode 100644 index 4f007bb1f..000000000 --- a/FusionIIIT/applications/examination/api/urls.py +++ /dev/null @@ -1,40 +0,0 @@ - -from django.conf.urls import url -from . import views - - -urlpatterns = [ - - url(r'^exam_view/', views.exam_view, name='exam_view'), - url(r'^download_template/', views.download_template, name='download_template'), - url(r'^check_course_students/', views.check_course_students, name='check_course_students'), - url(r'^submitGrades/', views.SubmitGradesView.as_view(), name='submitGrades'), - url(r'^upload_grades/', views.UploadGradesAPI.as_view(), name='upload_grades'), - url(r'^update_grades/', views.UpdateGradesAPI.as_view(), name='update_grades'), - url(r'^update_enter_grades/', views.UpdateEnterGradesAPI.as_view(), name='update_enter_grades'), - url(r'^moderate_student_grades/', views.ModerateStudentGradesAPI.as_view(), name='moderate_student_grades'), - url(r'^generate_transcript/', views.GenerateTranscript.as_view(), name='generate_transcript'), - url(r'^generate_transcript_form/', views.GenerateTranscriptForm.as_view(), name='generate_transcript_form'), - url(r'^generate_result/', views.GenerateResultAPI.as_view(), name='generate_result'), - url(r'^submit/', views.SubmitAPI.as_view(), name='submit'), - url(r'^download_excel/', views.DownloadExcelAPI.as_view(), name='download_excel'), - url(r'^submitGradesProf/', views.SubmitGradesProfAPI.as_view(), name='submitGradesProf'), - url(r'^upload_grades_prof/', views.UploadGradesProfAPI.as_view(), name='upload_grades_prof'), - url(r'^generate_pdf/', views.GeneratePDFAPI.as_view(), name='generate_pdf'), - url(r'^generate_student_result_pdf/', views.GenerateStudentResultPDFAPI.as_view(), name='generate_student_result_pdf'), - url(r'^downloadGrades/', views.DownloadGradesAPI.as_view(), name='downloadGrades'), - url(r'verify_grades_dean/',views.VerifyGradesDeanView.as_view(),name='verify_grades_dean'), - url(r'update_enter_grades_dean/',views.UpdateEnterGradesDeanView.as_view(),name='update_enter_grades_dean'), - url(r'validate_dean/',views.ValidateDeanView.as_view(),name='validate_dean'), - url(r'validate_dean_submit/',views.ValidateDeanSubmitView.as_view(),name='validate_dean_submit'), - url(r'check_result/',views.CheckResultView.as_view(),name='check_result'), - url(r'preview_grades/',views.PreviewGradesAPI.as_view(),name='preview_grades'), - url(r'result-announcements/', views.ResultAnnouncementListAPI.as_view(), name="result-announcements"), - url(r'update-announcement/', views.UpdateAnnouncementAPI.as_view(), name="update-announcement"), - url(r'create-announcement/', views.CreateAnnouncementAPI.as_view(), name="create-announcement"), - url(r'unique-course-reg-years/', views.UniqueRegistrationYearsView.as_view(), name="unique-course-reg-years"), - url(r'unique-stu-grades-years/', views.UniqueStudentGradeYearsView.as_view(), name="unique-stu-grades-years"), - url(r'^student/result_semesters/$', views.StudentSemesterListView.as_view(), name='get_student_semesters'), - url(r'^grade_status/', views.GradeStatusAPI.as_view(), name='grade_status'), - url(r'^grade_summary/', views.GradeSummaryAPI.as_view(), name='grade_summary'), -] \ No newline at end of file diff --git a/FusionIIIT/applications/examination/api/views.py b/FusionIIIT/applications/examination/api/views.py deleted file mode 100644 index e5d9bda31..000000000 --- a/FusionIIIT/applications/examination/api/views.py +++ /dev/null @@ -1,3764 +0,0 @@ -from django.db.models.query_utils import Q -from django.http import HttpResponse -from django.shortcuts import get_object_or_404, HttpResponse -from django.http import HttpResponse -from django.http import JsonResponse -from django.db import transaction -from decimal import Decimal, ROUND_HALF_UP -from applications.academic_procedures.models import(course_registration, course_replacement) -from applications.programme_curriculum.models import Course as Courses , Batch, CourseInstructor -from applications.examination.models import(hidden_grades , ResultAnnouncement, authentication) -from applications.academic_information.models import(Student) -from applications.online_cms.models import(Student_grades) -from rest_framework import status -from rest_framework.permissions import IsAuthenticated -from rest_framework.decorators import api_view,permission_classes -from rest_framework.response import Response -import csv -from io import StringIO, BytesIO -from django.contrib.auth import get_user_model -from rest_framework.views import APIView -from django.db.models import IntegerField -from django.db.models.functions import Cast -from rest_framework.parsers import MultiPartParser, FormParser -from openpyxl import Workbook -from openpyxl.utils import get_column_letter -from openpyxl.styles import Alignment, Font, PatternFill, Border, Side -import traceback -from applications.academic_information.models import Course -from reportlab.lib import colors -from reportlab.lib.pagesizes import letter -from reportlab.platypus import SimpleDocTemplate, Paragraph, Table, TableStyle, Spacer, Image -from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle -from reportlab.lib.colors import HexColor -from reportlab.lib.units import inch -from django.core.exceptions import ObjectDoesNotExist -from collections import defaultdict -from django.db.models import Case, When, IntegerField - -grade_conversion = { - "O": 1.0, "A+": 1.0, "A": 0.9, "B+": 0.8, "B": 0.7, - "C+": 0.6, "C": 0.5, "D+": 0.4, "D": 0.3, "F": 0.2, "S": 0.0, - **{f"A{i}": Decimal(str(0.9 + i * 0.01)) for i in range(1, 11)}, - **{f"B{i}": Decimal(str(0.8 + i * 0.01)) for i in range(1, 11)}, - **{ - f"{x/10:.1f}": Decimal(f"{x/100:.2f}") - for x in range(20, 101) - } -} - -ALLOWED_GRADES = { - "O", "A+", "A", - "B+", "B", - "C+", "C", - "D+", "D", "F", - "CD", "S", "X" -} - -PBI_AND_BTP_ALLOWED_GRADES = { - f"{x:.1f}" for x in [i / 10 for i in range(20, 101)] -} - -# Helper function to format semester display for PDFs -def format_semester_display(semester_no, semester_type=None, semester_label=None): - if semester_label and 'summer' in semester_label.lower(): - return semester_label - if semester_type and 'summer' in semester_type.lower(): - if semester_no == 2: - return "Summer 1" - elif semester_no == 4: - return "Summer 2" - elif semester_no == 6: - return "Summer 3" - elif semester_no == 8: - return "Summer 4" - else: - return f"Summer {semester_no // 2}" - else: - return str(semester_no) - -def round_from_last_decimal(number, decimal_places=1): - d = Decimal(str(number)) - return Decimal(d).quantize(Decimal('0.1'), rounding=ROUND_HALF_UP) - # d = Decimal(str(number)) - # current_places = abs(d.as_tuple().exponent) - - # # Keep rounding from the last decimal place until we reach the desired one - # while current_places > decimal_places: - # quantize_str = '0.' + '0' * (current_places - 1) + '1' - # d = d.quantize(Decimal(quantize_str), rounding=ROUND_HALF_UP) - # current_places -= 1 - - # # Final rounding to target place - # final_quantize = '0.' + '0' * (decimal_places - 1) + '1' - # return float(d.quantize(Decimal(final_quantize), rounding=ROUND_HALF_UP)) - - -def calculate_spi_for_student(student, selected_semester, semester_type): - semester_unit = Decimal('0') - grades = ( - Student_grades.objects - .filter( - roll_no=student.id_id, - semester=selected_semester, - semester_type=semester_type - ) - .annotate( - semester_type_order=Case( - When(semester_type="Odd Semester", then=0), - When(semester_type="Even Semester", then=1), - When(semester_type="Summer Semester", then=2), - default=3, - output_field=IntegerField(), - ) - ) - .order_by('semester', 'semester_type_order') - ) - total_points = Decimal('0') - total_credits = Decimal('0') - for g in grades: - credit = Decimal(str(g.course_id.credit)) - factor = grade_conversion.get(g.grade.strip(), -1) - if factor >= 0: - if factor != 0: - factor = Decimal(str(factor)) - total_points += factor * credit - total_credits += credit - semester_unit += credit - return round_from_last_decimal(Decimal('10') * (total_points / total_credits)) if total_credits else 0, semester_unit, (total_points*10) - -def trace_registration(reg_id, mapping): - seen = set() - while reg_id in mapping and reg_id not in seen: - seen.add(reg_id) - reg_id = mapping[reg_id] - return reg_id - -def calculate_cpi_for_student(student, selected_semester, semester_type): - total_unit = Decimal('0') - if selected_semester % 2 == 0 and semester_type == 'Summer Semester': - grades = ( - Student_grades.objects - .filter(roll_no=student.id_id, semester__lte=selected_semester) - .annotate( - semester_type_order=Case( - When(semester_type="Odd Semester", then=0), - When(semester_type="Even Semester", then=1), - When(semester_type="Summer Semester", then=2), - default=3, - output_field=IntegerField(), - ) - ) - .order_by('semester', 'semester_type_order') - ) - registrations = ( - course_registration.objects - .select_related('course_id', 'semester_id') - .filter( - student_id=student, - semester_id__semester_no__lte=selected_semester, - ) - .annotate( - semester_type_order=Case( - When(semester_type="Odd Semester", then=0), - When(semester_type="Even Semester", then=1), - When(semester_type="Summer Semester", then=2), - default=3, - output_field=IntegerField(), - ) - ) - .order_by('semester_id__semester_no', 'semester_type_order') - ) - else : - grades = Student_grades.objects.filter( - roll_no=student.id_id, semester__lte=selected_semester, - ).exclude(semester_type = 'Summer Semester', semester = selected_semester) - - registrations = course_registration.objects.select_related('course_id', 'semester_id').filter( - student_id=student, - semester_id__semester_no__lte=selected_semester - ).exclude(semester_type = 'Summer Semester', semester_id__semester_no = selected_semester) - reg_mapping = {} - for reg in registrations: - key = (reg.course_id.code.strip(), reg.semester_id.semester_no, reg.semester_type) - reg_mapping[key] = reg.id - replacements = course_replacement.objects.filter( - Q(old_course_registration__student_id=student) | - Q(new_course_registration__student_id=student) - ).select_related('old_course_registration', 'new_course_registration') - reg_replacement_map = {} - for rep in replacements: - old_reg_id = rep.old_course_registration.id - new_reg_id = rep.new_course_registration.id - if new_reg_id != old_reg_id: - reg_replacement_map[new_reg_id] = old_reg_id - grade_groups = defaultdict(list) - for g in grades: - key = (g.course_id.code.strip(), g.semester, g.semester_type) - reg_id = reg_mapping.get(key) - if reg_id is None: - continue - original_reg_id = trace_registration(reg_id, reg_replacement_map) - grade_groups[original_reg_id].append(g) - total_points = Decimal('0') - total_credits = Decimal('0') - for orig_reg, g_list in grade_groups.items(): - best_record = max(g_list, key=lambda r: grade_conversion.get(r.grade.strip(), -1)) - grade_factor = grade_conversion.get(best_record.grade.strip(), -1) - credit = Decimal(str(getattr(best_record.course_id, 'credit', 3))) - if grade_factor >= 0: - if grade_factor != 0: - grade_factor = Decimal(str(grade_factor)) - total_points += grade_factor * credit - total_credits += credit - total_unit += credit - return round_from_last_decimal(Decimal('10') * (total_points / total_credits)) if total_credits else 0, total_unit, (total_points*10) - -def parse_academic_year(academic_year, semester_type): - """ - Parse academic_year string (e.g., "2024-25") and determine the working_year based on semester type. - For Odd Semester, working_year = first part (e.g., 2024). - For Even Semester, working_year = second part prefixed by '20' (e.g., 2025 if academic_year is "2024-25"). - The session is set to the academic_year string. - """ - parts = academic_year.split("-") - if len(parts) != 2: - raise ValueError("Invalid academic year format. Expected format like '2024-25'.") - first_year = parts[0].strip() - second_year = parts[1].strip() - if semester_type == "Odd Semester": - working_year = int(first_year) - elif semester_type == "Even Semester": - working_year = int("20" + second_year) - else: - # For any other semester type (e.g., Summer Semester) use the first year by default. - working_year = int("20"+second_year) - session = academic_year # Use the complete academic year string as session. - return working_year, session - -def is_valid_grade(grade: str, course_code: str) -> bool: - """ - Returns True if the grade is valid for the given course code. - Special grades apply to PR4001, PR4002 and BTP4001. - """ - if not grade or not course_code: - return False - - code = course_code.strip().upper() - grade = grade.strip().upper() - - if code in {"PR4001","PR4002", "BTP4001"}: - return grade in PBI_AND_BTP_ALLOWED_GRADES - return grade in ALLOWED_GRADES - - -def gather_related_registrations(initial_reg, max_semester): - """ - Using BFS, collect all course_registration objects related by replacements - up to the given semester, ignoring semester_type. - """ - related = set() - queue = [initial_reg] - while queue: - reg = queue.pop(0) - if reg.id in related: - continue - related.add(reg.id) - olds = course_replacement.objects.filter(old_course_registration=reg) - news = course_replacement.objects.filter(new_course_registration=reg) - for rep in list(olds) + list(news): - for neighbor in (rep.old_course_registration, rep.new_course_registration): - if (neighbor.student_id == initial_reg.student_id and - neighbor.semester_id.semester_no <= max_semester): - queue.append(neighbor) - return course_registration.objects.filter(id__in=related).exclude(id=initial_reg.id) - - -@api_view(['POST']) -@permission_classes([IsAuthenticated]) -def exam_view(request): - """ - API to differentiate roles and provide appropriate redirection links. - """ - role = request.data.get('Role') - - if not role: - return Response({"error": "Role parameter is required."}, status=status.HTTP_400_BAD_REQUEST) - - if role in ["Associate Professor", "Professor", "Assistant Professor"]: - return Response({"redirect_url": "/examination/submitGradesProf/"}) - elif role == "acadadmin": - return Response({"redirect_url": "/examination/updateGrades/"}) - elif role == "Dean Academic": - return Response({"redirect_url": "/examination/verifyGradesDean/"}) - else: - return Response({"redirect_url": "/dashboard/"}) - - -class UniqueStudentGradeYearsView(APIView): - """ - GET: Return all distinct academic_year values from Student_grades. - """ - permission_classes = [IsAuthenticated] - - def get(self, request): - years = ( - Student_grades.objects - .values_list('academic_year', flat=True) - .distinct() - .order_by('academic_year') - ) - return Response({'academic_years': list(years)}, status=200) - - -class UniqueRegistrationYearsView(APIView): - """ - GET: Return all distinct working_year values from course_registration. - """ - permission_classes = [IsAuthenticated] - - def get(self, request): - programme_type = request.GET.get('programme_type', None) - years_query = course_registration.objects.exclude(session__isnull=True) - - if programme_type: - if programme_type.upper() == 'UG': - programme_list = ['B.Tech', 'B.Des'] - elif programme_type.upper() == 'PG': - programme_list = ['M.Tech', 'M.Des', 'PhD'] - else: - programme_list = [] - - if programme_list: - from applications.academic_information.models import Student - student_ids_with_programme = Student.objects.filter( - programme__in=programme_list - ).values_list('id', flat=True) - - years_query = years_query.filter( - student_id__in=student_ids_with_programme - ) - - years = ( - years_query - .values_list('session', flat=True) - .distinct() - .order_by('session') - ) - return Response({'academic_years': list(years)}, status=200) - - -@api_view(['POST']) -@permission_classes([IsAuthenticated]) -def download_template(request): - """ - API to download a CSV template for a course based on the provided role, course, academic year, and semester type. - - Expected request data: - - Role: User role (only allowed roles can access, e.g., "acadadmin") - - course: Course ID - - year: Academic year (session) in the format "YYYY-YY" (e.g., "2023-24") - - semester_type: Semester type (e.g., "Odd Semester", "Even Semester", "Summer Semester") - """ - try: - role = request.data.get('Role') - course = request.data.get('course') - session_year = request.data.get('year') - semester_type = request.data.get('semester_type') - programme_type = request.data.get('programme_type') - - if not role: - return Response({"error": "Role parameter is required."}, status=status.HTTP_400_BAD_REQUEST) - if not course or not session_year or not semester_type: - return Response( - {"error": "Course, academic year, and semester type are required."}, - status=status.HTTP_400_BAD_REQUEST - ) - - allowed_roles = [ - "acadadmin", "Associate Professor", "Professor", - "Assistant Professor", "Dean Academic" - ] - if role not in allowed_roles: - return Response({"error": "Access denied."}, status=status.HTTP_403_FORBIDDEN) - - User = get_user_model() - - # Filter course_registration records using course, session (academic year), and semester_type. - course_info_query = course_registration.objects.filter( - course_id_id=course, - session=session_year, - semester_type=semester_type - ) - - # Apply programme type filter if specified - if programme_type: - if programme_type.upper() == 'UG': - programme_list = ['B.Tech', 'B.Des'] - elif programme_type.upper() == 'PG': - programme_list = ['M.Tech', 'M.Des', 'PhD'] - else: - return Response( - {"error": "Invalid programme_type. Must be 'UG' or 'PG'."}, - status=status.HTTP_400_BAD_REQUEST - ) - - from applications.academic_information.models import Student - student_ids_with_programme = Student.objects.filter( - programme__in=programme_list - ).values_list('id', flat=True) - - course_info_query = course_info_query.filter( - student_id__in=student_ids_with_programme - ) - - course_info = course_info_query.order_by("student_id_id") - - if not course_info.exists(): - if programme_type: - programme_name = "Undergraduate" if programme_type.upper() == 'UG' else "Postgraduate" - return Response( - {"error": f"No {programme_name} students found in this course for the selected academic year and semester."}, - status=status.HTTP_404_NOT_FOUND - ) - else: - return Response( - {"error": "No registration data found for the provided course, academic year, and semester type."}, - status=status.HTTP_404_NOT_FOUND - ) - - # Get course information from the first matched registration. - course_obj = course_info.first().course_id - response = HttpResponse(content_type="text/csv") - course_name_clean = course_obj.name.replace(' ', '_').replace('/', '-')[:50] - semester_type_clean = semester_type.replace(' ', '_') - filename = f"{course_name_clean}_{semester_type_clean}_{session_year}.csv" - response['Content-Disposition'] = f'attachment; filename="{filename}"' - - writer = csv.writer(response) - writer.writerow(["roll_no", "name", "branch", "grade", "remarks", "semester"]) - - # Write a CSV row for each student registration. - for entry in course_info: - student_entry = entry.student_id - student_user = User.objects.get(username=student_entry.id_id) - branch_acronym = "" - if student_entry.batch_id: - try: - branch_acronym = student_entry.batch_id.discipline.acronym - except AttributeError: - pass - if not branch_acronym and student_entry.id.department: - branch_acronym = student_entry.id.department.name - semester_no = "" - if entry.course_slot_id and entry.course_slot_id.semester: - semester_no = entry.course_slot_id.semester.semester_no - - writer.writerow([ - student_entry.id_id, - f"{student_user.first_name} {student_user.last_name}", - branch_acronym, - "", - "", - semester_no - ]) - - return response - - except Exception as e: - return Response({'error': f'An unexpected error occurred: {str(e)}'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) - - -@api_view(['POST']) -@permission_classes([IsAuthenticated]) -def check_course_students(request): - """ - Lightweight API to check if a course has students for a specific programme type. - Returns boolean result without generating CSV. - """ - try: - role = request.data.get('Role') - course = request.data.get('course') - session_year = request.data.get('year') - semester_type = request.data.get('semester_type') - programme_type = request.data.get('programme_type') - - if not role: - return Response({"error": "Role parameter is required."}, status=status.HTTP_400_BAD_REQUEST) - if not course or not session_year or not semester_type: - return Response( - {"error": "Course, academic year, and semester type are required."}, - status=status.HTTP_400_BAD_REQUEST - ) - - allowed_roles = [ - "acadadmin", "Associate Professor", "Professor", - "Assistant Professor", "Dean Academic" - ] - if role not in allowed_roles: - return Response({"error": "Access denied."}, status=status.HTTP_403_FORBIDDEN) - - course_info_query = course_registration.objects.filter( - course_id_id=course, - session=session_year, - semester_type=semester_type - ) - - if programme_type: - if programme_type.upper() == 'UG': - programme_list = ['B.Tech', 'B.Des'] - elif programme_type.upper() == 'PG': - programme_list = ['M.Tech', 'M.Des', 'PhD'] - else: - return Response( - {"error": "Invalid programme_type. Must be 'UG' or 'PG'."}, - status=status.HTTP_400_BAD_REQUEST - ) - - from applications.academic_information.models import Student - student_ids_with_programme = Student.objects.filter( - programme__in=programme_list - ).values_list('id', flat=True) - - course_info_query = course_info_query.filter( - student_id__in=student_ids_with_programme - ) - - has_students = course_info_query.exists() - student_count = course_info_query.count() if has_students else 0 - - return Response({ - "has_students": has_students, - "student_count": student_count, - "course_id": course, - "programme_type": programme_type - }, status=status.HTTP_200_OK) - - except Exception as e: - return Response({'error': f'An unexpected error occurred: {str(e)}'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) - - -class SubmitGradesView(APIView): - """ - API to retrieve course information for a given academic year session and semester type. - - If both academic_year (formatted as "YYYY-YY") and semester_type are provided in the request, - the API filters courses from course_registration records based on: - - session: must equal the provided academic_year, and - - semester_type: must match the provided semester type. - - Otherwise, if academic_year is not provided, it returns available sessions. - """ - permission_classes = [IsAuthenticated] - - def post(self, request): - designation = request.data.get("Role") - academic_year = request.data.get("academic_year") - semester_type = request.data.get("semester_type") - - # Only allow access to 'acadadmin' - if designation != "acadadmin": - return Response( - {"success": False, "error": "Access denied."}, - status=status.HTTP_403_FORBIDDEN - ) - - # If both academic_year and semester_type are provided, filter courses. - if academic_year and semester_type: - # Use academic_year to match the session field. - unique_course_ids = course_registration.objects.filter( - session=academic_year, - semester_type=semester_type - ).values("course_id").distinct() - - courses_info = Courses.objects.filter( - id__in=unique_course_ids.values_list("course_id", flat=True) - ).order_by("code") - - return Response( - {"courses": list(courses_info.values())}, - status=status.HTTP_200_OK - ) - - # If academic_year is not provided, return available sessions. - sessions = course_registration.objects.values("session").distinct() - return Response( - {"sessions": list(sessions)}, - status=status.HTTP_200_OK - ) - - -from django.db import transaction -class UploadGradesAPI(APIView): - permission_classes = [IsAuthenticated] - parser_classes = [MultiPartParser, FormParser] - - def post(self, request): - # Validate the role (only allow "acadadmin" in this example). - des = request.data.get("Role") - if des != "acadadmin": - return Response( - {"success": False, "error": "Access denied."}, - status=status.HTTP_403_FORBIDDEN, - ) - - csv_file = request.FILES.get("csv_file") - if not csv_file: - return Response( - {"error": "No file provided. Please upload a CSV file."}, - status=status.HTTP_400_BAD_REQUEST, - ) - if not csv_file.name.endswith(".csv"): - return Response( - {"error": "Invalid file format. Please upload a CSV file."}, - status=status.HTTP_400_BAD_REQUEST, - ) - - # Extract course_id, academic_year, and semester_type from the request. - course_id = request.data.get("course_id") - academic_year = request.data.get("academic_year") - semester_type = request.data.get("semester_type") - if not course_id or not academic_year or not semester_type: - return Response( - {"error": "Course ID, Academic Year, and Semester Type are required."}, - status=status.HTTP_400_BAD_REQUEST, - ) - - try: - # Parse academic_year to determine working_year and session. - working_year, session = parse_academic_year(academic_year, semester_type) - - # Fetch the course. - courses_info = Courses.objects.get(id=course_id) - - # Check if any student is registered for this course, working_year, and semester_type. - registrations = course_registration.objects.filter( - course_id=courses_info, - session = academic_year, - semester_type=semester_type - ) - if not registrations.exists(): - message = "NO STUDENTS REGISTERED IN THIS COURSE FOR THE SELECTED SEMESTER." - return Response( - {"error": message,}, - status=status.HTTP_400_BAD_REQUEST, - ) - - # Check if grades already exist and cannot be resubmitted. - existing_grades = Student_grades.objects.filter( - course_id=courses_info.id, academic_year = academic_year, semester_type = semester_type - ) - if existing_grades.exists() and not existing_grades.first().reSubmit: - message = "THIS COURSE HAS ALREADY BEEN SUBMITTED." - return Response( - {"error": message}, - status=status.HTTP_400_BAD_REQUEST, - ) - - # Parse the CSV file."20" - decoded_file = csv_file.read().decode("utf-8").splitlines() - reader = csv.DictReader(decoded_file) - required_columns = ["roll_no", "grade", "remarks"] - if not all(column in reader.fieldnames for column in required_columns): - return Response( - { - "error": "CSV file must contain the following columns: roll_no, grade, remarks." - }, - status=status.HTTP_400_BAD_REQUEST, - ) - - errors = [] # To track errors for each CSV row. - allowed_list = ", ".join(sorted(ALLOWED_GRADES)) - # Wrap the upload process in an atomic transaction. - with transaction.atomic(): - for index, row in enumerate(reader, start=1): - roll_no = row.get("roll_no") - grade = row.get("grade") - remarks = row.get("remarks", "") - semester = row.get("semester", None) - - # Validate student existence. - try: - stud = Student.objects.get(id_id=roll_no) - except Student.DoesNotExist: - errors.append(f"Row {index}: Student with roll_no {roll_no} does not exist.") - continue - - # Check that the student is registered for the course. - registration_exists = course_registration.objects.filter( - student_id=stud, - course_id=courses_info, - semester_type=semester_type, - session = academic_year - ).exists() - if not registration_exists: - errors.append( - f"Row {index}: Student with roll_no {roll_no} is not registered for this course in the selected semester." - ) - continue - - - if not is_valid_grade(grade, courses_info.code): - errors.append( - f"Row {index}: Invalid grade '{grade}' for roll_no {roll_no}. " - f"Allowed grades are: {allowed_list}." - ) - continue - - # Determine the semester for the grade (use the provided value or fall back to student's current semester). - semester = semester or stud.curr_semester_no - batch = stud.batch - reSubmit = False - - # Create or update the grade record. - try: - Student_grades.objects.update_or_create( - roll_no=roll_no, - course_id_id=course_id, - year=working_year, # stored as academic year string - semester=semester, - batch=batch, - academic_year = academic_year, - semester_type = semester_type, - defaults={ - 'grade': grade, - 'remarks': remarks, - 'reSubmit': reSubmit, - 'academic_year': session, - 'semester_type': semester_type, - } - ) - except Exception as create_err: - errors.append( - f"Row {index}: Error creating/updating grade for student with roll_no {roll_no} - {str(create_err)}" - ) - continue - - # If errors were encountered in any row, rollback and return error summary. - if errors: - error_summary = "\n".join(f"- {msg}" for msg in errors) - raise Exception(error_summary) - - return Response( - {"message": "Grades uploaded successfully."}, - status=status.HTTP_200_OK, - ) - - except Courses.DoesNotExist: - return Response( - {"error": "Invalid course ID."}, status=status.HTTP_400_BAD_REQUEST - ) - except Exception as e: - return Response( - {"error": f"An error occurred: {str(e)}"}, - status=status.HTTP_400_BAD_REQUEST, - ) - -""" -API to fetch courses with unverified grades along with unique academic years. - -- Only users with the role of 'acadadmin' can access this endpoint. -- Retrieves courses where at least one student's grades are unverified. -- Fetches the academic years associated with unverified grades. - -Expected Request: -Headers: - Authorization: Token - -Body (JSON): - { - "Role": "acadadmin" - } - -Response: - 200 OK - { - "courses_info": [{"id": 1, "course_name": "Data Structures", ...}], - "unique_year_ids": [{"year": "2024"}, {"year": "2025"}] - } - 403 Forbidden - {"success": false, "error": "Access denied."} -""" - -class UpdateGradesAPI(APIView): - permission_classes = [IsAuthenticated] - - def post(self, request): - role = request.data.get("Role") - academic_year = request.data.get("academic_year") - semester_type = request.data.get("semester_type") - - if role != "acadadmin": - return Response( - {"success": False, "error": "Access denied."}, - status=403, - ) - - if not academic_year or not semester_type: - return Response( - {"success": False, "error": "Academic year and semester type are required."}, - status=400, - ) - - # Filter unverified grades based on academic_year and semester_type, then get unique course IDs. - unique_course_ids = ( - Student_grades.objects.filter( - verified=False, - academic_year=academic_year, - semester_type=semester_type, - ) - .values("course_id") - .distinct() - .annotate(course_id_int=Cast("course_id", IntegerField())) - ) - - # Retrieve courses that are linked to these student grades. - courses_info = Courses.objects.filter( - id__in=unique_course_ids.values_list("course_id_int", flat=True) - ) - - # Optionally, get unique year values for the selected filters. - unique_year_ids = Student_grades.objects.filter( - academic_year=academic_year, - semester_type=semester_type - ).values("year").distinct() - - return Response( - { - "courses_info": list(courses_info.values()), - "unique_year_ids": list(unique_year_ids), - }, - status=200, - ) - - -""" -API to check if grades have been submitted and are verified for a given course and academic year. - -- Only users with the role of 'acadadmin' can access this endpoint. -- Verifies if grades exist for the requested course and year. -- Returns student grades if unverified, else indicates if already verified. - -Expected Request: -Headers: - Authorization: Token - -Body (JSON): - { - "Role": "acadadmin", - "course": , - "year": - } - -Response: - 200 OK - If grades exist but are unverified: - { - "registrations": [ - {"id": 1, "roll_no": "CS101001", "grade": "A", ...} - ] - } - 200 OK - If already verified: - {"message": "This course is already verified."} - 400 Bad Request - If required fields are missing: - {"error": "Both 'course' and 'year' are required."} - 404 Not Found - If no grades exist: - {"message": "This course is not submitted by the instructor."} - 403 Forbidden - If access is denied: - {"success": false, "error": "Access denied."} -""" - -class UpdateEnterGradesAPI(APIView): - permission_classes = [IsAuthenticated] - - def post(self, request): - des = request.data.get("Role") - - - if des != "acadadmin": - return Response( - {"success": False, "error": "Access denied."}, - status=status.HTTP_403_FORBIDDEN, - ) - - # Get course_id and year from request body - course_id = request.data.get("course") - year = request.data.get("year") - semester_type = request.data.get("semester_type") - - # Validate course_id and year - if not course_id or not year: - return Response( - {"error": "Both 'course' and 'year' are required."}, - status=status.HTTP_400_BAD_REQUEST, - ) - - # Check if the course exists with grades for the given year - course_present = Student_grades.objects.filter(course_id=course_id, academic_year=year, semester_type=semester_type) - - if not course_present.exists(): - return Response( - {"message": "This course is not submitted by the instructor."}, - status=status.HTTP_404_NOT_FOUND, - ) - - # Check if the course is already verified - verification = course_present.first().verified - if verification: - return Response( - {"message": "This course is already verified."}, - status=status.HTTP_200_OK, - ) - - - registrations = course_present.values() - - return Response( - {"registrations": list(registrations)}, - status=status.HTTP_200_OK, - ) - - - -""" -API for moderating student grades (updating, verifying, or creating hidden grades). - -- Only users with the roles 'acadadmin' or 'Dean Academic' can access this endpoint. -- Updates grades for students based on course and semester. -- Marks grades as verified and supports resubmission if enabled. -- If a student grade doesn't exist, it creates a hidden grade record. -- Returns the updated grades in a downloadable CSV file. - -Expected Request: -Headers: - Authorization: Token - -Body (JSON): - { - "Role": "acadadmin", - "student_ids": ["20231001", "20231002"], - "semester_ids": [5, 5], - "course_ids": [101, 101], - "grades": ["A", "B"], - "allow_resubmission": "YES" - } - -Response: - 200 OK - CSV file with updated grades - 403 Forbidden - If access is denied: - {"success": false, "error": "Access denied."} - 400 Bad Request - If required fields are missing or mismatched: - {"error": "Invalid or incomplete grade data provided."} - 500 Internal Server Error - If any unexpected error occurs: - {"error": "An error occurred: "} -""" - -class ModerateStudentGradesAPI(APIView): - permission_classes = [IsAuthenticated] - - def post(self, request): - - des = request.data.get("Role") - if des not in ["acadadmin", "Dean Academic"]: - return Response( - {"success": False, "error": "Access denied."}, - status=status.HTTP_403_FORBIDDEN, - ) - - # Extract data from the request - student_ids = request.data.get("student_ids", []) - semester_ids = request.data.get("semester_ids", []) - course_ids = request.data.get("course_ids", []) - grades = request.data.get("grades", []) - remarks=request.data.get("remarks",[]) - allow_resubmission = request.data.get("allow_resubmission", "NO") - - - if ( - not student_ids - or not semester_ids - or not course_ids - or not grades - or len(student_ids) != len(semester_ids) - or len(semester_ids) != len(course_ids) - or len(course_ids) != len(grades) - ): - return Response( - {"error": "Invalid or incomplete grade data provided."}, - status=status.HTTP_400_BAD_REQUEST, - ) - - # Update or create grades - for student_id, semester_id, course_id, grade,remark in zip( - student_ids, semester_ids, course_ids, grades,remarks - ): - try: - grade_of_student = Student_grades.objects.get( - course_id=course_id, roll_no=student_id, semester=semester_id - ) - grade_of_student.remarks=remark - grade_of_student.grade = grade - grade_of_student.verified = True - if allow_resubmission.upper() == "YES": - grade_of_student.reSubmit = True - grade_of_student.save() - except Student_grades.DoesNotExist: - # Create a new hidden grade if the student grade doesn't exist - hidden_grades.objects.create( - course_id=course_id, - student_id=student_id, - semester_id=semester_id, - grade=grade, - ) - - # Generate CSV file as the response - response = HttpResponse(content_type="text/csv") - response["Content-Disposition"] = 'attachment; filename="grades.csv"' - - writer = csv.writer(response) - writer.writerow(["Student ID", "Semester ID", "Course ID", "Grade"]) - for student_id, semester_id, course_id, grade in zip( - student_ids, semester_ids, course_ids, grades - ): - writer.writerow([student_id, semester_id, course_id, grade]) - - # Return the CSV response - return response - - - -""" -API to generate a student's academic transcript for a specific semester. - -- Only users with the role 'acadadmin' can access this endpoint. -- Fetches the courses and grades of the student for the requested semester. -- Also retrieves all courses registered by the student up to that semester. -- If a grade is unavailable for a course, it returns "Grading not done yet". - -Expected Request: -Headers: - Authorization: Token - -Body (JSON): - { - "Role": "acadadmin", - "student": "20231001", - "semester": 3 - } - -Response: - 200 OK - Transcript data - 403 Forbidden - If access is denied: - {"error": "Access denied."} - 400 Bad Request - If required fields are missing: - {"error": "Student ID and Semester are required."} - 500 Internal Server Error - If any unexpected error occurs: - {"error": "An error occurred: "} -""" - -import json -class GenerateTranscript(APIView): - permission_classes = [IsAuthenticated] - - def post(self, request): - des = request.data.get("Role") - student_id = request.data.get("student") - semester = request.data.get("semester") - semester = json.loads(semester) - semester_number = semester.get('no') - semester_type = semester.get('type') - - if des != "acadadmin": - return Response({"error": "Access denied."}, status=status.HTTP_403_FORBIDDEN) - - if not student_id or not semester: - return Response({"error": "Student ID and Semester are required."}, status=status.HTTP_400_BAD_REQUEST) - - try: - student = Student.objects.get(id_id=student_id) - cpi, tu, _ = calculate_cpi_for_student(student, semester_number, semester_type) - spi, su, _ = calculate_spi_for_student(student, semester_number, semester_type) - except: - return Response({"error": "Student ID does not exist."}, status=status.HTTP_400_BAD_REQUEST) - - course_grades = {} - courses_registered = Student_grades.objects.filter(roll_no=student_id, semester=semester_number, semester_type = semester_type) - - # Get academic year from the first grade record - academic_year = None - if courses_registered.exists(): - academic_year = courses_registered.first().academic_year - - for reg in courses_registered: - course = reg.course_id - course_grades[course.id] = { - "course_name": course.name, - "course_code": course.code, - "credit": course.credit, - "grade": reg.grade, - "points": Decimal(str(grade_conversion.get(reg.grade, 0) * 10)).quantize(Decimal('0.1'), rounding=ROUND_HALF_UP), - } - - # Add complete student information like CheckResultView - student_info = { - "name": f"{student.id.user.first_name} {student.id.user.last_name}".strip(), - "student_name": f"{student.id.user.first_name} {student.id.user.last_name}".strip(), - "rollNumber": student.id.user.username, - "roll_number": student.id.user.username, - "programme": student.programme, - "batch": str(student.batch_id) if student.batch_id else str(student.batch), - "branch": student.id.department.name if student.id.department else "", - "department": student.id.department.name if student.id.department else "", - "semester": student.curr_semester_no, - "academicYear": academic_year or "", - "academic_year": academic_year or "" - } - - response_data = { - "name": f"{student.id.user.first_name} {student.id.user.last_name}".strip(), - "student_name": f"{student.id.user.first_name} {student.id.user.last_name}".strip(), - "roll_number": student.id.user.username, - "programme": student.programme, - "department": student.id.department.name if student.id.department else "", - "branch": student.id.department.name if student.id.department else "", - "academic_year": academic_year or "", - "student_info": student_info, - "courses_grades": course_grades, - "spi": spi, - "cpi": cpi, - "tu": tu, - "su": su, - } - - return Response(response_data, status=status.HTTP_200_OK) - - -""" -API to fetch available academic details and retrieve students for generating transcripts. - -- Only users with the role 'acadadmin' can access this endpoint. -- GET: Retrieves the list of available programmes, batches, and specializations. -- POST: Fetches students based on programme, batch, specialization (optional), and semester. - -Expected Requests: -1. GET /api/generate-transcript-form/ - Headers: - Authorization: Token - X-User-Role: acadadmin - Response: - 200 OK - List of programmes, batches, and specializations - 403 Forbidden - If access is denied: - {"error": "Access denied. Invalid or missing role."} - -2. POST /api/generate-transcript-form/ - Headers: - Authorization: Token - X-User-Role: acadadmin - Body (JSON): - { - "programme": "B.Tech", - "batch": 2021, - "specialization": "AI & ML", - "semester": 5 - } - Response: - 200 OK - List of students in the given filters - 403 Forbidden - If access is denied: - {"error": "Access denied. Invalid or missing role."} - 400 Bad Request - If required fields are missing: - {"error": "Programme, batch, and semester are required fields."} -""" - -class GenerateTranscriptForm(APIView): - permission_classes = [IsAuthenticated] - - def get(self, request): - role = request.GET.get("role") - if not role or role != "acadadmin": - return Response( - {"error": "Access denied. Invalid or missing role."}, - status=status.HTTP_403_FORBIDDEN - ) - - # Query only running batches from the Batch table. - batches_queryset = Batch.objects.filter(running_batch=True) - # Create a display label that combines batch name, discipline, and year. - batch_list = [ - { - "id": batch.id, - "label": f"{batch.name} - {batch.discipline} {batch.year}" - } - for batch in batches_queryset - ] - - # Get programmes from Student table. - programmes = Student.objects.values_list('programme', flat=True).distinct() - # Get unique, non-null (and non-empty) specializations. - specializations = Student.objects.exclude(specialization__isnull=True)\ - .exclude(specialization__exact="")\ - .values_list('specialization', flat=True)\ - .distinct() - return Response({ - "programmes": list(programmes), - "batches": batch_list, - "specializations": list(specializations), - }, status=status.HTTP_200_OK) - - def post(self, request): - role = request.data.get("Role") - if not role or role != "acadadmin": - return Response( - {"error": "Access denied. Invalid or missing role."}, - status=status.HTTP_403_FORBIDDEN - ) - - batch = request.data.get('batch') - specialization = request.data.get('specialization') - semester = request.data.get('semester') - - if not batch or not semester: - return Response( - {"error": "batch, and semester are required fields."}, - status=status.HTTP_400_BAD_REQUEST - ) - - # Use the batch from the Batch table (passed as ID). - if specialization: - students = Student.objects.filter( - batch_id=batch, specialization=specialization - ).order_by('id') - else: - students = Student.objects.filter( - batch_id=batch - ).order_by('id') - - return Response({ - "students": list(students.values()), - "semester": semester - }, status=status.HTTP_200_OK) - - -class GenerateResultAPI(APIView): - """ - API endpoint to generate an Excel file containing student grades with SPI and CPI. - - Request Body (JSON): - - Role: must be "acadadmin" - - semester: integer (selected semester) - - specialization: string (branch acronym, e.g., "CSE") [optional] - - batch: integer (Batch primary key) - - Response: - - An Excel file (student_grades.xlsx) in the required format. - """ - permission_classes = [IsAuthenticated] - - def post(self, request): - try: - role = request.data.get("Role") - if role != "acadadmin": - return Response({"error": "Access denied."}, status=403) - - semester = request.data.get("semester") - branch = request.data.get("specialization") # optional now - batch_id = request.data.get("batch") - semester_type = request.data.get("semester_type") - - if not semester or not batch_id: - return Response({"error": "Semester and Batch are required."}, status=400) - - # Get the Batch record using its primary key. - batch_obj = Batch.objects.filter(id=batch_id).first() - if not batch_obj: - return Response({"error": "Batch not found."}, status=404) - - # Fetch all students for this Batch. - if branch: - students = Student.objects.filter(batch_id=batch_id, specialization=branch).order_by('id') - else: - students = Student.objects.filter(batch_id=batch_id).order_by('id') - - # Fetch course_ids for which the grade is not empty. - course_ids = Student_grades.objects.filter( - batch=batch_obj.year, - semester=semester, - roll_no__in = students, - semester_type = semester_type - ).exclude(grade__isnull=True).exclude(grade="").values_list('course_id_id', flat=True).distinct() - - courses = Courses.objects.filter(id__in=course_ids) - courses_map = {course.id: course.credit for course in courses} - - wb = Workbook() - ws = wb.active - ws.title = "Student Grades" - - # Define a fill style for header cells: light grey background. - header_fill = PatternFill(start_color="D3D3D3", end_color="D3D3D3", fill_type="solid") - thin_border = Border( - left=Side(style="thin"), right=Side(style="thin"), - top=Side(style="thin"), bottom=Side(style="thin") - ) - - - # Setup header rows: S. No, Roll No and Name in columns A, B and C. - ws["A1"] = "S. No" - ws["B1"] = "Roll No" - ws["C1"] = "Name" - for col in ("A", "B"): - cell = ws[col + "1"] - cell.alignment = Alignment(horizontal="center", vertical="center") - cell.font = Font(bold=True) - cell.fill = header_fill - cell = ws["C1"] - cell.alignment = Alignment(horizontal="center", vertical="center") - cell.font = Font(bold=True) - cell.fill = header_fill - ws.column_dimensions[get_column_letter(1)].width = 12 - ws.column_dimensions[get_column_letter(2)].width = 18 - ws.column_dimensions[get_column_letter(3)].width = 30 - - # Starting from column 4, add headers for each course (each course uses 2 columns for Grade and Remarks). - col_idx = 4 - for course in courses: - # Merge cells for the course code header. - ws.merge_cells(start_row=1, start_column=col_idx, end_row=1, end_column=col_idx+1) - cell = ws.cell(row=1, column=col_idx) - cell.value = course.code - cell.alignment = Alignment(horizontal="center", vertical="center") - cell.font = Font(bold=True) - cell.fill = header_fill - - ws.merge_cells(start_row=2, start_column=col_idx, end_row=2, end_column=col_idx+1) - cell = ws.cell(row=2, column=col_idx) - cell.value = course.name - cell.alignment = Alignment(horizontal="center", vertical="center") - cell.font = Font(bold=True) - cell.fill = header_fill - - ws.merge_cells(start_row=3, start_column=col_idx, end_row=3, end_column=col_idx+1) - cell = ws.cell(row=3, column=col_idx) - cell.value = course.credit - cell.alignment = Alignment(horizontal="center", vertical="center") - cell.font = Font(bold=True) - cell.fill = header_fill - - cell = ws.cell(row=4, column=col_idx) - cell.value = "Grade" - cell.alignment = Alignment(horizontal="center", vertical="center") - cell.font = Font(bold=True) - cell.fill = header_fill - - cell = ws.cell(row=4, column=col_idx+1) - cell.value = "Remarks" - cell.alignment = Alignment(horizontal="center", vertical="center") - cell.font = Font(bold=True) - cell.fill = header_fill - - ws.column_dimensions[get_column_letter(col_idx)].width = 25 - ws.column_dimensions[get_column_letter(col_idx+1)].width = 25 - col_idx += 2 - - # Append headers for SPI and CPI. - cell = ws.cell(row=1, column=col_idx) - cell.value = "SPI" - cell.alignment = Alignment(horizontal="center", vertical="center") - cell.font = Font(bold=True) - cell.fill = header_fill - - cell = ws.cell(row=1, column=col_idx+1) - cell.value = "CPI" - cell.alignment = Alignment(horizontal="center", vertical="center") - cell.font = Font(bold=True) - cell.fill = header_fill - - cell = ws.cell(row=1, column=col_idx+2) - cell.value = "SU" - cell.alignment = Alignment(horizontal="center", vertical="center") - cell.font = Font(bold=True) - cell.fill = header_fill - - cell = ws.cell(row=1, column=col_idx+3) - cell.value = "TU" - cell.alignment = Alignment(horizontal="center", vertical="center") - cell.font = Font(bold=True) - cell.fill = header_fill - - cell = ws.cell(row=1, column=col_idx+4) - cell.value = "SP" - cell.alignment = Alignment(horizontal="center", vertical="center") - cell.font = Font(bold=True) - cell.fill = header_fill - - cell = ws.cell(row=1, column=col_idx+5) - cell.value = "TP" - cell.alignment = Alignment(horizontal="center", vertical="center") - cell.font = Font(bold=True) - cell.fill = header_fill - - # Ensure full header rows (1 to 4) are highlighted. - max_col = ws.max_column - for row in range(1, 5): - for col in range(1, max_col + 1): - cell = ws.cell(row=row, column=col) - cell.fill = header_fill - cell.border = thin_border - - # Fill in student rows, starting from row 5. - row_idx = 5 - User = get_user_model() - for idx, student in enumerate(students, start=1): - ws.cell(row=row_idx, column=1).value = idx - ws.cell(row=row_idx, column=2).value = student.id_id - - try: - student_user = User.objects.get(username=student.id_id) - student_name = f"{student_user.first_name} {student_user.last_name}".strip() or student_user.username - except: - student_name = student.id_id - - ws.cell(row=row_idx, column=3).value = student_name - ws.cell(row=row_idx, column=3).alignment = Alignment(horizontal="left", vertical="center") - - # Get the student’s grade records for the current semester. - student_grades = Student_grades.objects.filter( - roll_no=student.id_id, - course_id_id__in=course_ids, - semester_type=semester_type, - semester=semester - ) - grades_map = {g.course_id_id: g for g in student_grades} - col_ptr = 4 - for course in courses: - grade_entry = grades_map.get(course.id) - grade_val = grade_entry.grade if grade_entry else '-' - - remark = '-' - if grade_entry: - reg = course_registration.objects.filter( - student_id=student, - course_id=course, - semester_id__semester_no=semester, - semester_type=semester_type, - session=grade_entry.academic_year, - ).first() - if reg: - related_regs = gather_related_registrations(reg, semester) - attempts = [] - for r in related_regs: - g = Student_grades.objects.filter( - roll_no=student.id_id, - course_id__code=r.course_id.code, - semester=r.semester_id.semester_no, - semester_type = r.semester_type, - academic_year = r.session - ).order_by('-semester').first() - if g: - attempts.append((r.course_id.code, g.grade)) - - if len(attempts) >= 1: - scored = sorted( - attempts, - key=lambda x: grade_conversion.get(x[1], -1), - reverse=True - ) - first_code, first_grade = scored[0] - if first_grade == 'F' or first_grade == 'X': - remark = 'R(BL)' if first_code == course.code else 'S(BL)' - else: - remark = 'R(IM)' if first_code == course.code else 'S(IM)' - ws.cell(row=row_idx, column=col_ptr).value = grade_val - ws.cell(row=row_idx, column=col_ptr+1).value = remark - for c in [col_ptr, col_ptr+1]: - ws.cell(row=row_idx, column=c).alignment = Alignment(horizontal="center", vertical="center") - col_ptr += 2 - - # Calculate SPI and CPI. - spi_val, SU, SP = calculate_spi_for_student(student, semester, semester_type) - cpi_val, TU, TP = calculate_cpi_for_student(student, semester, semester_type) - ws.cell(row=row_idx, column=col_ptr).value = spi_val - ws.cell(row=row_idx, column=col_ptr+1).value = cpi_val - ws.cell(row=row_idx, column=col_ptr+2).value = SU - ws.cell(row=row_idx, column=col_ptr+3).value = TU - ws.cell(row=row_idx, column=col_ptr+4).value = SP - ws.cell(row=row_idx, column=col_ptr+5).value = TP - for c in [col_ptr, col_ptr+1]: - ws.cell(row=row_idx, column=c).alignment = Alignment(horizontal="center", vertical="center") - row_idx += 1 - - response = HttpResponse(content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') - response['Content-Disposition'] = 'attachment; filename="student_grades.xlsx"' - wb.save(response) - return response - - except Exception as e: - traceback.print_exc() - return Response({'error': str(e)}, status=500) - - -class SubmitAPI(APIView): - - """ - API endpoint to fetch the list of unique courses available for submission. - Accessible only by users with the 'acadadmin' or 'Dean Academic' role. - - ### Headers: - - **Authorization**: `Token ` (Required, for authentication) - - ### Request Body (JSON): - - **Role** (string, required) → User role (must be `"acadadmin"` or `"Dean Academic"`) - - ### Response: - - **Success (200 OK)**: Returns a JSON object with `courses_info`, which contains course details. - - **Errors**: - - `403 Forbidden`: If the user does not have `"acadadmin"` or `"Dean Academic"` role. - - `500 Internal Server Error`: For any unexpected errors. - """ - - permission_classes = [IsAuthenticated] - - def post(self, request): - role = request.data.get("Role") - - if role not in ["acadadmin", "Dean Academic"]: - return Response( - {"error": "Access denied."}, - status=status.HTTP_403_FORBIDDEN, - ) - - # Get unique course IDs - unique_course_ids = ( - course_registration.objects.values("course_id") - .distinct() - .annotate(course_id_int=Cast("course_id", IntegerField())) - ) - - courses_info = Course.objects.filter( - id__in=unique_course_ids.values_list("course_id_int", flat=True) - ) - - return Response( - {"courses_info": list(courses_info.values())}, - status=status.HTTP_200_OK, - ) - - -class DownloadExcelAPI(APIView): - - """ - API endpoint to generate and download a CSV file containing student grades. - - ### Headers: - - **Authorization**: `Token ` (Required, for authentication) - - ### Request Body (JSON): - - **student_ids** (list, required) → List of student IDs. - - **semester_ids** (list, required) → Corresponding semester IDs. - - **course_ids** (list, required) → Corresponding course IDs. - - **grades** (list, required) → Corresponding grades for the students. - - ### Response: - - **Success (200 OK)**: Returns a downloadable CSV file with student grades. - - **Errors**: - - `400 Bad Request`: If input data is missing or inconsistent in length. - - `500 Internal Server Error`: For any unexpected errors. - """ - - permission_classes = [IsAuthenticated] - - def post(self, request): - student_ids = request.data.get("student_ids", []) - semester_ids = request.data.get("semester_ids", []) - course_ids = request.data.get("course_ids", []) - grades = request.data.get("grades", []) - - if ( - not student_ids - or not semester_ids - or not course_ids - or not grades - or len(student_ids) != len(semester_ids) - or len(semester_ids) != len(course_ids) - or len(course_ids) != len(grades) - ): - return Response( - {"error": "Invalid or incomplete grade data provided."}, - status=status.HTTP_400_BAD_REQUEST, - ) - - response = HttpResponse(content_type="text/csv") - response["Content-Disposition"] = 'attachment; filename="grades.csv"' - - writer = csv.writer(response) - writer.writerow(["Student ID", "Semester ID", "Course ID", "Grade"]) - for student_id, semester_id, course_id, grade in zip( - student_ids, semester_ids, course_ids, grades - ): - writer.writerow([student_id, semester_id, course_id, grade]) - - return response - - -class SubmitGradesProfAPI(APIView): - - """ - API endpoint to fetch courses assigned to a professor and available academic years. - - ### Headers: - - **Authorization**: `Token ` (Required for authentication) - - ### Request Body (JSON): - - **Role** (string, required) → User's role (Must be one of `Associate Professor`, `Professor`, or `Assistant Professor`). - - - """ - - permission_classes = [IsAuthenticated] - - def post(self, request): - - role = request.data.get("Role") - academic_year = request.data.get("academic_year") - semester_type = request.data.get("semester_type") - programme_type = request.data.get("programme_type") - - if role not in ["Associate Professor", "Professor", "Assistant Professor"]: - return Response( - {"success": False, "error": "Access denied."}, - status=status.HTTP_403_FORBIDDEN, - ) - - if not academic_year or not semester_type: - return Response( - {"success": False, "error": "Academic year and semester type are required."}, - status=400, - ) - - instructor_id = request.user.username - - working_year, _ = parse_academic_year(academic_year=academic_year, semester_type=semester_type) - - unique_course_ids = ( - CourseInstructor.objects.filter(instructor_id_id=instructor_id, year = working_year, semester_type=semester_type) - .values("course_id_id") - .distinct() - .annotate(course_id_int=Cast("course_id_id", IntegerField())) - ) - - # Retrieve course details with programme type filtering - courses_query = Courses.objects.filter( - id__in=unique_course_ids.values_list("course_id_int", flat=True) - ) - - student_ids_with_programme = None - if programme_type: - if programme_type.upper() == 'UG': - programme_list = ['B.Tech', 'B.Des'] - elif programme_type.upper() == 'PG': - programme_list = ['M.Tech', 'M.Des', 'PhD'] - else: - programme_list = [] - - if programme_list: - from applications.academic_information.models import Student - student_ids_with_programme = Student.objects.filter( - programme__in=programme_list - ).values_list('id', flat=True) - - course_ids_with_programme = course_registration.objects.filter( - course_id__in=courses_query.values_list('id', flat=True), - student_id__in=student_ids_with_programme, - session=academic_year, - semester_type=semester_type - ).values_list('course_id', flat=True).distinct() - - courses_query = courses_query.filter(id__in=course_ids_with_programme) - - courses_info = courses_query - - courses_data = [] - for course in courses_info.values(): - course_registrations = course_registration.objects.filter( - course_id=course['id'], - session=academic_year, - semester_type=semester_type - ) - - # Apply programme filter for counting - if programme_type and student_ids_with_programme is not None: - course_registrations = course_registrations.filter( - student_id__in=student_ids_with_programme - ) - - course['student_count'] = course_registrations.count() - course['has_students'] = course['student_count'] > 0 - course['programme_type'] = programme_type - courses_data.append(course) - - return Response( - { - "courses_info": courses_data, - }, - status=status.HTTP_200_OK, - ) - - -class UploadGradesProfAPI(APIView): - """ - Upload grades CSV by the assigned instructor. - - Role check - - File & header validation - - Registration & duplicate submit checks - - Instructor ownership check - - All or nothing: reset reSubmit and update/create rows in one atomic block - """ - permission_classes = [IsAuthenticated] - parser_classes = [MultiPartParser, FormParser] - - def post(self, request): - try: - # 1) ROLE CHECK - role = request.data.get("Role") - if role not in ["Associate Professor", "Professor", "Assistant Professor"]: - return Response({"error": "Access denied."}, - status=status.HTTP_403_FORBIDDEN) - - # 2) FILE CHECK - csv_file = request.FILES.get("csv_file") - if not csv_file: - return Response( - {"error": "No file provided. Please upload a CSV file."}, - status=status.HTTP_400_BAD_REQUEST - ) - if not csv_file.name.lower().endswith(".csv"): - return Response( - {"error": "Invalid file format. Please upload a CSV file."}, - status=status.HTTP_400_BAD_REQUEST - ) - - # 3) REQUIRED PARAMS - course_id = request.data.get("course_id") - academic_year = request.data.get("academic_year") - semester_type = request.data.get("semester_type") - programme_type = request.data.get("programme_type") - if not (course_id and academic_year and semester_type): - return Response( - {"error": "Course ID, Academic Year, and Semester Type are required."}, - status=status.HTTP_400_BAD_REQUEST - ) - - # 4) PARSE academic_year → working_year & session - try: - working_year, session = parse_academic_year(academic_year, semester_type) - except Exception: - return Response( - {"error": "Invalid academic year or semester type."}, - status=status.HTTP_400_BAD_REQUEST - ) - - # 5) FETCH COURSE - try: - course = Courses.objects.get(id=course_id) - except Courses.DoesNotExist: - return Response({"error": "Invalid course ID."}, - status=status.HTTP_400_BAD_REQUEST) - - # 6) CHECK STUDENT REGISTRATIONS & DETERMINE PROGRAMME FILTERING - regs = course_registration.objects.filter( - course_id=course, - working_year=working_year, - semester_type=semester_type - ) - - if not regs.exists(): - return Response( - {"error": "NO STUDENTS REGISTERED IN THIS COURSE FOR THE SELECTED SEMESTER."}, - status=status.HTTP_400_BAD_REQUEST - ) - - from applications.academic_information.models import Student - ug_programmes = ['B.Tech', 'B.Des'] - pg_programmes = ['M.Tech', 'M.Des', 'PhD'] - - ug_student_ids = Student.objects.filter(programme__in=ug_programmes).values_list('id', flat=True) - pg_student_ids = Student.objects.filter(programme__in=pg_programmes).values_list('id', flat=True) - - course_has_ug = regs.filter(student_id__in=ug_student_ids).exists() - course_has_pg = regs.filter(student_id__in=pg_student_ids).exists() - - if programme_type: - if programme_type.upper() == 'UG': - if not course_has_ug: - return Response( - {"error": "No UG students registered in this course."}, - status=status.HTTP_400_BAD_REQUEST - ) - regs = regs.filter(student_id__in=ug_student_ids) - elif programme_type.upper() == 'PG': - if not course_has_pg: - return Response( - {"error": "No PG students registered in this course."}, - status=status.HTTP_400_BAD_REQUEST - ) - regs = regs.filter(student_id__in=pg_student_ids) - else: - return Response( - {"error": "Invalid programme_type. Must be 'UG' or 'PG'."}, - status=status.HTTP_400_BAD_REQUEST - ) - else: - if course_has_ug and course_has_pg: - return Response( - { - "error": "This course has both UG and PG students. Please specify programme_type as 'UG' or 'PG'.", - "course_info": { - "course_code": course.code, - "course_name": course.name, - "has_ug": course_has_ug, - "has_pg": course_has_pg, - "total_registrations": regs.count() - } - }, - status=status.HTTP_400_BAD_REQUEST - ) - elif course_has_ug and not course_has_pg: - programme_type = 'UG' - regs = regs.filter(student_id__in=ug_student_ids) - elif course_has_pg and not course_has_ug: - programme_type = 'PG' - regs = regs.filter(student_id__in=pg_student_ids) - - existing_query = Student_grades.objects.filter( - course_id=course_id, - academic_year=academic_year, - semester_type=semester_type - ) - - if programme_type: - if programme_type.upper() == 'UG': - ug_student_rolls = [reg.student_id_id for reg in regs] - existing_ug_grades = existing_query.filter(roll_no__in=ug_student_rolls) - - if existing_ug_grades.exists(): - non_resubmit_ug = existing_ug_grades.filter(reSubmit=False) - if non_resubmit_ug.exists(): - return Response( - {"error": "THIS COURSE HAS ALREADY BEEN SUBMITTED FOR UG STUDENTS."}, - status=status.HTTP_400_BAD_REQUEST - ) - - elif programme_type.upper() == 'PG': - pg_student_rolls = [reg.student_id_id for reg in regs] - existing_pg_grades = existing_query.filter(roll_no__in=pg_student_rolls) - - if existing_pg_grades.exists(): - non_resubmit_pg = existing_pg_grades.filter(reSubmit=False) - if non_resubmit_pg.exists(): - return Response( - {"error": "THIS COURSE HAS ALREADY BEEN SUBMITTED FOR PG STUDENTS."}, - status=status.HTTP_400_BAD_REQUEST - ) - else: - existing = existing_query.first() - if existing and not existing.reSubmit: - return Response( - {"error": "THIS COURSE HAS ALREADY BEEN SUBMITTED."}, - status=status.HTTP_400_BAD_REQUEST - ) - - # 8) INSTRUCTOR‐OWNERSHIP CHECK - if not CourseInstructor.objects.filter( - course_id_id=course_id, - instructor_id_id=request.user.username, - year=working_year - ).exists(): - return Response( - {"error": "Access denied: you are not assigned as instructor for this course."}, - status=status.HTTP_403_FORBIDDEN - ) - - # 9) PARSE CSV HEADER - decoded = csv_file.read().decode("utf-8").splitlines() - reader = csv.DictReader(decoded) - required_cols = {"roll_no", "grade", "remarks"} - if not required_cols.issubset(reader.fieldnames or []): - return Response( - {"error": "CSV file must contain columns: roll_no, grade, remarks."}, - status=status.HTTP_400_BAD_REQUEST - ) - - # 10) ATOMIC PROCESSING - errors = [] - with transaction.atomic(): - # ─── Reset reSubmit flags for this course/year/semester/programme ─── - reset_query = Student_grades.objects.filter( - course_id_id=course_id, - academic_year=academic_year, - semester_type=semester_type - ) - - if programme_type: - if programme_type.upper() == 'UG': - ug_rolls = [reg.student_id_id for reg in regs] - reset_query = reset_query.filter(roll_no__in=ug_rolls) - elif programme_type.upper() == 'PG': - pg_rolls = [reg.student_id_id for reg in regs] - reset_query = reset_query.filter(roll_no__in=pg_rolls) - - reset_query.update(reSubmit=False) - - # ─── Process each CSV row ─── - for idx, row in enumerate(reader, start=1): - roll_no = row.get("roll_no", "").strip() - grade = row.get("grade", "").strip() - remarks = row.get("remarks", "").strip() - sem_csv = row.get("semester", "").strip() or None - - # a) STUDENT EXISTS? - try: - stud = Student.objects.get(id_id=roll_no) - except Student.DoesNotExist: - errors.append(f"Row {idx}: Student with roll_no {roll_no} does not exist.") - continue - - student_reg = course_registration.objects.filter( - student_id=stud, - course_id=course, - semester_type=semester_type, - session=academic_year - ) - - if not student_reg.exists(): - errors.append( - f"Row {idx}: Student {roll_no} not registered for this course/semester." - ) - continue - - # Check if student belongs to the specified programme type - if programme_type: - student_programme = stud.programme - if programme_type.upper() == 'UG' and student_programme not in ug_programmes: - errors.append( - f"Row {idx}: Student {roll_no} is not a UG student (programme: {student_programme})." - ) - continue - elif programme_type.upper() == 'PG' and student_programme not in pg_programmes: - errors.append( - f"Row {idx}: Student {roll_no} is not a PG student (programme: {student_programme})." - ) - continue - - # c) VALID GRADE? - if grade not in ALLOWED_GRADES: - allowed = ", ".join(sorted(ALLOWED_GRADES)) - errors.append( - f"Row {idx}: Invalid grade '{grade}'. Allowed: {allowed}." - ) - continue - - # d) DETERMINE SEMESTER & BATCH - semester = sem_csv or stud.curr_semester_no - batch = stud.batch - reSubmit = False # enforce false - - # e) UPSERT GRADE - try: - Student_grades.objects.update_or_create( - roll_no=roll_no, - course_id_id=course_id, - year=working_year, - batch = batch, - academic_year=academic_year, - semester_type=semester_type, - semester=semester, - defaults={ - "grade": grade, - "remarks": remarks, - "reSubmit": reSubmit, - } - ) - except Exception as exc: - errors.append( - f"Row {idx}: Error saving grade for {roll_no}: {str(exc)}" - ) - continue - - # If any row errors occurred, rollback & return them - if errors: - summary = "\n".join(f"- {e}" for e in errors) - raise Exception(f"Upload failed with the following errors:\n{summary}") - - # 11) SUCCESS RESPONSE - programme_msg = f" for {programme_type.upper()} students" if programme_type else "" - return Response( - {"message": f"Grades uploaded successfully{programme_msg}."}, - status=status.HTTP_200_OK - ) - - except Exception as e: - return Response( - {"error": str(e)}, - status=status.HTTP_400_BAD_REQUEST - ) - - -class DownloadGradesAPI(APIView): - permission_classes = [IsAuthenticated] - - def post(self, request): - try: - role = request.data.get("Role") - academic_year = request.data.get("academic_year") - semester_type = request.data.get("semester_type") - programme_type = request.data.get("programme_type") - - if role not in ["Associate Professor", "Professor", "Assistant Professor"]: - return Response({"error": "Access denied."}, status=status.HTTP_403_FORBIDDEN) - - if not academic_year or not semester_type: - return Response( - {"success": False, "error": "Academic year and semester type are required."}, - status=400, - ) - - working_year, _ = parse_academic_year(academic_year=academic_year, semester_type=semester_type) - instructor_id = request.user.username - - unique_course_ids = ( - CourseInstructor.objects - .filter(instructor_id_id=instructor_id, year=working_year, semester_type=semester_type) - .values("course_id_id") - .distinct() - .annotate(course_id_int=Cast("course_id_id", IntegerField())) - ) - - grades_qs = Student_grades.objects.filter( - academic_year = academic_year, - semester_type = semester_type, - course_id_id__in=unique_course_ids.values_list("course_id_int", flat=True) - ) - - # Apply programme type filter if specified - if programme_type: - if programme_type.upper() == 'UG': - programme_list = ['B.Tech', 'B.Des'] - elif programme_type.upper() == 'PG': - programme_list = ['M.Tech', 'M.Des', 'PhD'] - else: - return Response( - {"error": "Invalid programme_type. Must be 'UG' or 'PG'."}, - status=status.HTTP_400_BAD_REQUEST - ) - - student_ids_with_programme = Student.objects.filter( - programme__in=programme_list - ).values_list('id', flat=True) - - grades_qs = grades_qs.filter(roll_no__in=student_ids_with_programme) - - course_ids = grades_qs.values_list("course_id_id", flat=True).distinct() - courses_details = Courses.objects.filter(id__in=course_ids) - - return Response({"courses": list(courses_details.values())}, status=status.HTTP_200_OK) - - except Exception as e: - # Optionally log the exception here - return Response({"error": "Internal server error."}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) - - -class GeneratePDFAPI(APIView): - permission_classes = [IsAuthenticated] - - def post(self, request): - try: - # Check if this is a student result request (no Role field or Role is student) - role = request.data.get("Role") - - # If no role specified or role is student, handle as student result PDF - if not role or role.lower() == "student" or "student_info" in request.data: - # Delegate to student result PDF generation - return self.generate_student_result_pdf(request) - - # Faculty role check for course grade sheets - if role not in ["Associate Professor", "Professor", "Assistant Professor"]: - return Response({"error": "Access denied."}, status=status.HTTP_403_FORBIDDEN) - - # Existing faculty course grade sheet logic - course_id = request.data.get("course_id") - academic_year = request.data.get("academic_year") - semester_type = request.data.get("semester_type") - programme_type = request.data.get("programme_type") - - course_info = get_object_or_404(Courses, id=course_id) - working_year, _ = parse_academic_year(academic_year, semester_type) - - grades = Student_grades.objects.filter( - course_id_id=course_id, - academic_year=academic_year, - semester_type=semester_type - ) - - if programme_type: - if programme_type.upper() == 'UG': - programme_list = ['B.Tech', 'B.Des'] - elif programme_type.upper() == 'PG': - programme_list = ['M.Tech', 'M.Des', 'PhD'] - else: - return Response( - {"error": "Invalid programme_type. Must be 'UG' or 'PG'."}, - status=status.HTTP_400_BAD_REQUEST - ) - - student_ids_with_programme = Student.objects.filter( - programme__in=programme_list - ).values_list('id', flat=True) - - grades = grades.filter(roll_no__in=student_ids_with_programme) - - grades = grades.order_by("roll_no") - - ci = CourseInstructor.objects.filter( - course_id_id=course_id, - year=working_year, - semester_type=semester_type, - instructor_id_id=request.user.username - ) - if not ci.exists(): - return Response({"success": False, "error": "Course not found."}, status=404) - - # semester = ci.first().semester_no - instructor = f"{request.user.first_name} {request.user.last_name}" - - # count grades - all_grades = ["O","A+","A","B+","B","C+","C","D+","D","F","S","X","CD"] - grade_counts = {g: grades.filter(grade=g).count() for g in all_grades} - - # prepare PDF response - response = HttpResponse(content_type="application/pdf") - response["Content-Disposition"] = f'attachment; filename="{course_info.code}_grades.pdf"' - - # Create PDF metadata to fix "(anonymous)" title issue - pdf_title = f"Course Grades - {course_info.code} - {course_info.name}" - - # ↑ Increase topMargin to 2" for header + spaceBetween - doc = SimpleDocTemplate( - response, - pagesize=letter, - leftMargin=inch, - rightMargin=inch, - topMargin=2 * inch, - bottomMargin=inch, - title=pdf_title, - author="PDPM IIITDM Jabalpur", - subject=f"Course Grade Report - {course_info.code}", - creator="Fusion Academic System" - ) - - elements = [] - styles = getSampleStyleSheet() - - # keep your original field_label_style - field_label_style = ParagraphStyle( - "FieldLabelStyle", - parent=styles["Normal"], - fontSize=12, - textColor=colors.black, - spaceAfter=5, - ) - - # Title style for body (you can leave this or remove, it's not used in header now) - header_style = ParagraphStyle( - "HeaderStyle", - parent=styles["Heading1"], - fontName="Helvetica-Bold", - fontSize=16, - textColor=HexColor("#333333"), - spaceAfter=20, - alignment=1, - ) - # You can optionally remove the next line if you don't want "Grade Sheet" - # in the body—it's now drawn in the page header. - - # Main grades table (unchanged) - data = [["S.No.","Roll Number","Grade"]] - for i, g in enumerate(grades, 1): - data.append([i, g.roll_no, g.grade]) - tbl = Table(data, colWidths=[80, 300, 100]) - tbl.setStyle(TableStyle([ - ("BACKGROUND", (0,0), (-1,0), HexColor("#E0E0E0")), - ("TEXTCOLOR", (0,0), (-1,0), colors.black), - ("ALIGN", (0,0), (-1,0), "CENTER"), - ("FONTNAME", (0,0), (-1,0), "Helvetica-Bold"), - ("FONTSIZE", (0,0), (-1,0), 14), - ("BOTTOMPADDING", (0,0), (-1,0), 8), - ("GRID", (0,0), (-1,-1), 0.5, colors.grey), - ("ROWBACKGROUNDS", (0,1), (-1,-1), [HexColor("#F9F9F9"), colors.white]), - ("TEXTCOLOR", (0,1), (-1,-1), colors.black), - ("FONTNAME", (0,1), (-1,-1), "Helvetica"), - ("FONTSIZE", (0,1), (-1,-1), 12), - ("ALIGN", (0,1), (-1,-1), "CENTER"), - ])) - elements.append(tbl) - elements.append(Spacer(1, 20)) - - # Grade distribution 1 & 2 (unchanged)… - grade_data1 = [["O","A+","A","B+","B","C+","C","D+"]] - grade_data1.append([grade_counts[g] for g in grade_data1[0]]) - grade_table1 = Table(grade_data1, colWidths=[60]*8) - grade_table1.setStyle(TableStyle([ - ("BACKGROUND", (0,0), (-1,0), HexColor("#E0E0E0")), - ("TEXTCOLOR", (0,0), (-1,0), colors.black), - ("ALIGN", (0,0), (-1,-1), "CENTER"), - ("FONTNAME", (0,0), (-1,0), "Helvetica-Bold"), - ("FONTSIZE", (0,0), (-1,0), 12), - ("BOTTOMPADDING", (0,0), (-1,0), 8), - ("GRID", (0,0), (-1,-1), 0.5, colors.grey), - ])) - elements.append(grade_table1) - elements.append(Spacer(1, 10)) - - grade_data2 = [["D","F","S","X","CD"]] - grade_data2.append([grade_counts[g] for g in grade_data2[0]]) - grade_table2 = Table(grade_data2, colWidths=[80]*5) - grade_table2.setStyle(TableStyle([ - ("BACKGROUND", (0,0), (-1,0), HexColor("#E0E0E0")), - ("TEXTCOLOR", (0,0), (-1,0), colors.black), - ("ALIGN", (0,0), (-1,-1), "CENTER"), - ("FONTNAME", (0,0), (-1,0), "Helvetica-Bold"), - ("FONTSIZE", (0,0), (-1,0), 12), - ("BOTTOMPADDING", (0,0), (-1,0), 8), - ("GRID", (0,0), (-1,-1), 0.5, colors.grey), - ])) - elements.append(grade_table2) - elements.append(Spacer(1, 40)) - - # Verified statement (unchanged) - verified_style = ParagraphStyle( - "VerifiedStyle", - parent=styles["Normal"], - fontSize=13, - textColor=HexColor("#333333"), - spaceAfter=20, - ) - elements.append(Paragraph( - "I have carefully checked and verified the submitted grades. " - "The grade distribution and submitted grades are correct. " - "[Please mention any exception below.]", - verified_style - )) - - # ——— Page Header & Footer ——— - def draw_page(canvas, doc): - canvas.setTitle(f"Grade Sheet - {course_info.code}") - canvas.saveState() - width, height = letter - - # 1) Draw "Grade Sheet" at top center - p_title = Paragraph("Grade Sheet", header_style) - w, h = p_title.wrap(doc.width, doc.topMargin) - p_title.drawOn(canvas, doc.leftMargin, height - h) - course_details = f"L:{course_info.lecture_hours}, T:{course_info.tutorial_hours}, P:{course_info.project_hours}, C:{course_info.credit}" - # 2) Draw your five field_label-style lines below it - hdr_texts = [ - f"Session: {academic_year}", - f"Course Code: {course_info.code}", - f"Course Name: {course_info.name} ({course_details})", - f"Instructor: {instructor}", - ] - y = height - h - header_style.spaceAfter - for txt in hdr_texts: - p = Paragraph(txt, field_label_style) - w2, h2 = p.wrap(doc.width, doc.topMargin) - p.drawOn(canvas, doc.leftMargin, y - h2) - y -= (h2 + field_label_style.spaceAfter) - - # Footer: page number - canvas.setFont("Helvetica", 9) - canvas.drawRightString(width - inch, 0.3 * inch, f"Page {doc.page}") - - # Footer: date & signature placeholders - canvas.setFont("Helvetica", 12) - canvas.drawString(inch, 0.75 * inch, "") - canvas.drawString(inch, 0.5 * inch, "Date") - canvas.drawString(width - 4 * inch, 0.75 * inch, "") - canvas.drawString(width - 4 * inch, 0.5 * inch, "Course Instructor's Signature") - - canvas.restoreState() - - # Build PDF with header/footer on every page - doc.build(elements, onFirstPage=draw_page, onLaterPages=draw_page) - return response - - except Exception as e: - traceback.print_exc() - return Response({'error': str(e)}, status=500) - - def generate_student_result_pdf(self, request): - """Generate PDF for student examination results""" - try: - # Get data from request - data = request.data - student_info = data.get('student_info', {}) - courses = data.get('courses', []) - spi = float(data.get('spi', 0)) - cpi = float(data.get('cpi', 0)) - su = int(data.get('su', 0)) - tu = int(data.get('tu', 0)) - semester_no = data.get('semester_no', 1) - semester_type = data.get('semester_type', '') - semester_label = data.get('semester_label', '') - formatted_semester = format_semester_display(semester_no, semester_type, semester_label) - - # Create PDF buffer - buffer = BytesIO() - - # Create PDF metadata to fix "(anonymous)" title issue - pdf_title = f"Student Result - {student_info.get('name', student_info.get('rollNumber', 'Student'))} - {formatted_semester}" - - doc = SimpleDocTemplate( - buffer, - pagesize=letter, - rightMargin=0.5*inch, - leftMargin=0.5*inch, - topMargin=0.5*inch, - bottomMargin=0.5*inch, - title=pdf_title, - author="PDPM IIITDM Jabalpur", - subject=f"Student Result Report - {formatted_semester}", - creator="Fusion Academic System" - ) - - story = [] - styles = getSampleStyleSheet() - - # Header styles - title_style = ParagraphStyle( - 'Title', - parent=styles['Heading1'], - fontSize=14, - spaceAfter=8, - alignment=1, # Center - fontName='Times-Bold' - ) - - subtitle_style = ParagraphStyle( - 'Subtitle', - parent=styles['Heading2'], - fontSize=11, - spaceAfter=6, - alignment=1, # Center - fontName='Times-Bold' - ) - - # Institution Header - story.append(Paragraph("PDPM Indian Institute of Information Technology,", title_style)) - story.append(Paragraph("Design & Manufacturing, Jabalpur", title_style)) - story.append(Paragraph("(An Institute of National Importance under MoE, Govt. of India)", subtitle_style)) - story.append(Paragraph("Semester Grade Report / Marksheet", subtitle_style)) - story.append(Spacer(1, 20)) - - # Student Information Table - from reportlab.platypus import Paragraph - student_name = student_info.get('name') - if len(student_name) > 25: - student_name = Paragraph(student_name, ParagraphStyle('NameStyle', parent=styles['Normal'], fontSize=10, fontName='Times-Roman')) - - student_data = [ - ['Name of Student:', student_name, 'Roll No.:', student_info.get('roll_number', 'N/A')], - ['Programme:', student_info.get('programme', 'N/A'), 'Branch:', student_info.get('department', 'N/A')], - ['Semester:', formatted_semester, 'Academic Year:', student_info.get('academic_year', 'N/A')] - ] - - student_table = Table(student_data, colWidths=[1.75*inch, 1.75*inch, 1.75*inch, 1.75*inch]) - student_table.setStyle(TableStyle([ - ('FONTNAME', (0, 0), (-1, -1), 'Times-Roman'), - ('FONTSIZE', (0, 0), (-1, -1), 10), - ('GRID', (0, 0), (-1, -1), 1, colors.black), - ('VALIGN', (0, 0), (-1, -1), 'TOP'), - ('LEFTPADDING', (0, 0), (-1, -1), 5), - ('RIGHTPADDING', (0, 0), (-1, -1), 5), - ('TOPPADDING', (0, 0), (-1, -1), 4), - ('BOTTOMPADDING', (0, 0), (-1, -1), 4), - ])) - - story.append(student_table) - story.append(Spacer(1, 15)) - - # Courses Table - headers = ['S. No.', 'Course Code', 'Course Title', 'Credits', 'Grade', 'Grade Points'] - course_data = [headers] - - for i, course in enumerate(courses, 1): - course_data.append([ - str(i), - course.get('coursecode', ''), - course.get('coursename', ''), - str(course.get('credits', '')), - course.get('grade', ''), - str(course.get('points', '')) - ]) - - course_table = Table(course_data, colWidths=[0.5*inch, 1*inch, 3.2*inch, 0.7*inch, 0.6*inch, 1*inch]) - course_table.setStyle(TableStyle([ - ('FONTNAME', (0, 0), (-1, 0), 'Times-Bold'), # Header - ('FONTNAME', (0, 1), (-1, -1), 'Times-Roman'), # Data - ('FONTSIZE', (0, 0), (-1, -1), 9), - ('GRID', (0, 0), (-1, -1), 1, colors.black), - ('ALIGN', (0, 0), (-1, -1), 'CENTER'), - ('ALIGN', (2, 1), (2, -1), 'LEFT'), # Course title left aligned - ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'), - ('BACKGROUND', (0, 0), (-1, 0), colors.lightgrey), - ('LEFTPADDING', (0, 0), (-1, -1), 3), - ('RIGHTPADDING', (0, 0), (-1, -1), 3), - ('TOPPADDING', (0, 0), (-1, -1), 3), - ('BOTTOMPADDING', (0, 0), (-1, -1), 3), - ])) - - story.append(course_table) - story.append(Spacer(1, 15)) - - # Summary Table - summary_data = [ - ['Total Credits Registered:', str(tu), 'Semester Credits Earned:', str(su)], - ['SPI:', f"{spi:.1f}", 'CPI:', f"{cpi:.1f}"] - ] - - summary_table = Table(summary_data, colWidths=[2.2*inch, 0.8*inch, 2.2*inch, 0.8*inch]) - summary_table.setStyle(TableStyle([ - ('FONTNAME', (0, 0), (-1, -1), 'Times-Roman'), - ('FONTSIZE', (0, 0), (-1, -1), 10), - ('ALIGN', (0, 0), (-1, -1), 'LEFT'), - ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'), - ('LEFTPADDING', (0, 0), (-1, -1), 5), - ('RIGHTPADDING', (0, 0), (-1, -1), 5), - ('TOPPADDING', (0, 0), (-1, -1), 4), - ('BOTTOMPADDING', (0, 0), (-1, -1), 4), - ])) - - story.append(summary_table) - story.append(Spacer(1, 25)) - - # Footer - footer_style = ParagraphStyle( - 'Footer', - parent=styles['Normal'], - fontSize=8, - alignment=1, # Center - fontName='Times-Italic' - ) - - from datetime import datetime - current_date = datetime.now().strftime("%d/%m/%Y") - story.append(Paragraph(f"This is a computer-generated document. Generated on {current_date}", footer_style)) - - # Build PDF - doc.build(story) - - # Return PDF response - pdf_data = buffer.getvalue() - buffer.close() - - response = HttpResponse(pdf_data, content_type='application/pdf') - # Create filename with formatted semester for clarity - semester_suffix = formatted_semester.replace(' ', '_').replace(':', '').lower() - filename = f"result_{student_info.get('rollNumber', student_info.get('roll_number', 'student'))}_{semester_suffix}.pdf" - response['Content-Disposition'] = f'attachment; filename="{filename}"' - response['Content-Length'] = len(pdf_data) - - return response - - except Exception as e: - return JsonResponse({'error': f'PDF generation failed: {str(e)}'}, status=500) - - -""" -API for the Dean Academic to verify if a course has been submitted by the instructor. - -Only users with the role 'Dean Academic' can access this endpoint. - -POST: Retrieves courses that have been verified by the Dean Academic. -Expected Requests: -1. POST /api/verify-grades-dean/ - Headers: - Authorization: Token - X-User-Role: Dean Academic - Body (JSON): - { - "Role": "Dean Academic" - } - Response: - 200 OK - List of verified courses and unique years - { - "courses_info": [ - { - "id": 1, - "name": "Mathematics", - "code": "MATH101" - } - ], - "unique_year_ids": [ - {"year": 2024}, - {"year": 2023} - ] - } - 403 Forbidden - Access Denied - {"error": "Access denied. Invalid or missing role."} - 400 Bad Request - Missing Required Fields - {"error": "Role is required."} - 500 Internal Server Error - Unexpected Error - {"error": "An error occurred: "} -""" - - -class VerifyGradesDeanView(APIView): - """ - API for Dean Academic to verify student grades. - """ - permission_classes = [IsAuthenticated] - - def post(self, request): - role = request.data.get("Role") - academic_year = request.data.get("academic_year") - semester_type = request.data.get("semester_type") - - if role != "Dean Academic": - return Response({"error": "Access denied."}, status=status.HTTP_403_FORBIDDEN) - if not academic_year or not semester_type: - return Response({"error": "Both academic_year and semester_type are required."}, - status=status.HTTP_400_BAD_REQUEST) - - # Filter only verified grades matching year & semester_type - qs = Student_grades.objects.filter( - academic_year=academic_year, - semester_type=semester_type, - ) - course_ids = qs.values_list("course_id_id", flat=True).distinct() - courses = Courses.objects.filter(id__in=course_ids).order_by("code") - courses_info = list(courses.values("id", "code", "name")) - - return Response({"courses_info": courses_info}, status=status.HTTP_200_OK) - - -""" -API for the Dean Academic to verify if a course has been submitted by the instructor. - -Only users with the role 'Dean Academic' can access this endpoint. - -POST: Checks if a course has been submitted by the instructor. - If found, returns student registrations for that course. - If not found, returns a message that the grading is not submitted. -Expected Requests: -1. POST /api/update-enter-grades-dean/ - Headers: - Authorization: Token - X-User-Role: Dean Academic - Body (JSON): - { - "Role": "Dean Academic", - "course": "CS101", - "year": 2024 - } - Response: - 200 OK - Course Found & Student Registrations - { - "registrations": [ - { - "id": 1, - "course_id": "CS101", - "year": 2024, - "grade": "A" - } - ] - } - 404 Not Found - Course Not Submitted - {"message": "THIS COURSE IS NOT SUBMITTED BY THE INSTRUCTOR"} - 403 Forbidden - Access Denied - {"error": "Access denied. Invalid or missing role."} - 400 Bad Request - Missing Required Fields - {"error": "Role, course, and year are required fields."} - 500 Internal Server Error - Unexpected Error - {"error": "An error occurred: "} -""" - - -class UpdateEnterGradesDeanView(APIView): - """ - API for Dean Academic to fetch all student registrations for a course & year. - """ - permission_classes = [IsAuthenticated] - - def post(self, request): - role = request.data.get("Role") - course_id = request.data.get("course") - year = request.data.get("year") - semester_type = request.data.get("semester_type") - - if role != "Dean Academic": - return Response({"success": False, "error": "Access denied."}, status=status.HTTP_403_FORBIDDEN) - - qs = Student_grades.objects.filter(course_id=course_id, academic_year=year, semester_type = semester_type) - if not qs.exists(): - return Response({"message": "THIS COURSE IS NOT SUBMITTED BY THE INSTRUCTOR"}) - - return Response({"registrations": list(qs.values())}, status=status.HTTP_200_OK) - -""" -Only users with the role 'Dean Academic' can access this endpoint. - -POST: Returns a list of verified courses and available working years. -Expected Request: -1. POST /api/validate-dean/ - Headers: - Authorization: Token - X-User-Role: Dean Academic - Body (JSON): - { - "Role": "Dean Academic" - } - Response: - 200 OK - List of Verified Courses & Working Years - { - "courses_info": [ - { - "id": 1, - "name": "Computer Networks", - "code": "CN101" - } - ], - "working_years": [ - {"working_year": 2024}, - {"working_year": 2023} - ] - } - 403 Forbidden - Access Denied - {"error": "Access denied."} - 400 Bad Request - Missing Required Fields - {"error": "Role is required."} - 500 Internal Server Error - Unexpected Error - {"error": "An error occurred: "} - -""" -class ValidateDeanView(APIView): - """ - API for Dean Academic to validate courses and fetch working years & batches. - - - Only users with the role 'Dean Academic' can access this endpoint. - - Returns a list of verified courses, available working years, and batch details. - """ - - permission_classes = [IsAuthenticated] - - def post(self, request): - role = request.data.get("Role") - - if role != "Dean Academic": - return Response( - {"error": "Access denied."}, - status=status.HTTP_403_FORBIDDEN - ) - - # Fetch unique verified course IDs - unique_course_ids = Student_grades.objects.filter(verified=True).values("course_id").distinct() - unique_course_ids = unique_course_ids.annotate(course_id_int=Cast("course_id", IntegerField())) - - # Retrieve course names and codes - courses_info = Courses.objects.filter( - id__in=unique_course_ids.values_list("course_id_int", flat=True) - ) - - # Fetch available working years and batch IDs - working_years = course_registration.objects.values("working_year").distinct() - unique_batch_ids = Student_grades.objects.values("batch").distinct() - - return Response( - { - "courses_info": list(courses_info.values()), - "working_years": list(working_years), - # "unique_batch_ids": list(unique_batch_ids), - }, - status=status.HTTP_200_OK - ) - - -class ValidateDeanSubmitView(APIView): - """ - API for Dean Academic to submit and validate student grades from a CSV file. - - - Only users with the role 'Dean Academic' can access this endpoint. - - Accepts a CSV file containing roll numbers, grades, and remarks. - - Validates grades against the existing database records and finds mismatches. - """ - - permission_classes = [IsAuthenticated] - - def post(self, request): - role = request.data.get("Role") - - if role != "Dean Academic": - return Response( - {"error": "Access denied."}, - status=status.HTTP_403_FORBIDDEN - ) - - # Validate file existence - if "csv_file" not in request.FILES: - return Response( - {"error": "CSV file is required."}, - status=status.HTTP_400_BAD_REQUEST - ) - - csv_file = request.FILES["csv_file"] - - # Validate file type - if not csv_file.name.endswith(".csv"): - return Response( - {"error": "Please submit a valid CSV file."}, - status=status.HTTP_400_BAD_REQUEST - ) - - course_id = request.data.get("course") - academic_year = request.data.get("year") - - # Validate required fields - if not course_id or not academic_year: - return Response( - {"error": "Course and Academic Year are required."}, - status=status.HTTP_400_BAD_REQUEST - ) - - if not academic_year.isdigit(): - return Response( - {"error": "Academic Year must be a number."}, - status=status.HTTP_400_BAD_REQUEST - ) - - # Fetch students registered for the course - students = course_registration.objects.filter( - course_id_id=course_id, working_year=academic_year - ) - - try: - # Parse the CSV file - decoded_file = csv_file.read().decode("utf-8").splitlines() - reader = csv.DictReader(decoded_file) - - required_columns = ["roll_no", "grade", "remarks"] - if not all(column in reader.fieldnames for column in required_columns): - return Response( - {"error": "CSV file must contain the following columns: roll_no, grade, remarks."}, - status=status.HTTP_400_BAD_REQUEST - ) - - mismatches = [] - - for row in reader: - roll_no = row["roll_no"] - grade = row["grade"] - remarks = row["remarks"] - - try: - student = Student.objects.get(id_id=roll_no) - semester = student.curr_semester_no - batch = student.batch - - student_grade = Student_grades.objects.get( - roll_no=roll_no, - course_id_id=course_id, - year=academic_year, - batch=batch - ) - - if student_grade.grade != grade: - mismatches.append({ - "roll_no": roll_no, - "csv_grade": grade, - "db_grade": student_grade.grade, - "remarks": remarks, - "batch": batch, - "semester": semester, - "course_id": course_id - }) - except ObjectDoesNotExist: - return Response( - {"error": f"Student or grade record not found for roll number: {roll_no}"}, - status=status.HTTP_400_BAD_REQUEST - ) - - if not mismatches: - return Response( - {"message": "There are no mismatches."}, - status=status.HTTP_200_OK - ) - - return Response( - {"mismatches": mismatches}, - status=status.HTTP_200_OK - ) - - except Exception as e: - return Response( - {"error": f"An error occurred while processing the file: {str(e)}"}, - status=status.HTTP_500_INTERNAL_SERVER_ERROR - ) - - -class CheckResultView(APIView): - """ - API endpoint to retrieve student result information including grades and personal details. - - Returns: - JsonResponse: Contains student personal information, course grades, SPI, CPI, and credit units - - Response Structure: - { - "success": True, - "student_info": { - "name": "Student Full Name", - "roll_number": "Student Roll Number", - "programme": "Programme Name", - "batch": "Batch Information", - "department": "Department Name", - "semester": "Current Semester Number", - "academic_year": "Academic Year (e.g., 2023-24)" - }, - "courses": [ - { - "coursecode": "Course Code", - "courseid": "Course ID", - "coursename": "Course Name", - "credits": "Course Credits", - "grade": "Letter Grade", - "points": "Grade Points" - } - ], - "spi": "Semester Performance Index", - "cpi": "Cumulative Performance Index", - "su": "Semester Credits", - "tu": "Total Credits" - } - """ - permission_classes = [IsAuthenticated] - - def post(self, request, *args, **kwargs): - roll_number = request.user.username - semester_no = request.data.get('semester_no') - semester_type = request.data.get('semester_type') - - if semester_no is None or semester_type is None: - return JsonResponse( - {"success": False, "message": "semester_no and semester_type are required."}, - status=400, - ) - - try: - student = Student.objects.get(id_id=roll_number) - except Student.DoesNotExist: - return JsonResponse( - {"success": False, "message": "Student record not found."}, - status=404, - ) - - # Find the announcement for this batch, number and type - ann = ResultAnnouncement.objects.filter( - batch=student.batch_id, - semester=semester_no, - ).first() - - if not ann or not ann.announced: - return JsonResponse( - {"success": False, "message": "Results not announced yet."}, - status=200, - ) - - # Filter grades on both fields - grades_info = Student_grades.objects.filter( - roll_no=roll_number, - semester=semester_no, - semester_type=semester_type - ).select_related('course_id') - - # Get academic year from the first grade record - academic_year = None - if grades_info.exists(): - academic_year = grades_info.first().academic_year - else: - pass - - spi, su, _ = calculate_spi_for_student(student, semester_no, semester_type) - cpi, tu, _ = calculate_cpi_for_student(student, semester_no, semester_type) - - # Add student personal information to the response - student_info = { - "name": f"{student.id.user.first_name} {student.id.user.last_name}".strip(), - "rollNumber": student.id.user.username, # Frontend uses camelCase - "roll_number": student.id.user.username, # Backend uses snake_case - "programme": student.programme, - "batch": str(student.batch_id) if student.batch_id else str(student.batch), - "branch": student.id.department.name if student.id.department else "", # Frontend uses branch - "department": student.id.department.name if student.id.department else "", # Backend uses department - "semester": student.curr_semester_no, - "academicYear": academic_year or "", # Frontend uses camelCase - "academic_year": academic_year or "" # Backend uses snake_case - } - - response_data = { - "success": True, - "student_info": student_info, - "courses": [ - { - "coursecode": grade.course_id.code, - "courseid": grade.course_id.id, - "coursename": grade.course_id.name, - "credits": grade.course_id.credit, - "grade":grade.grade, - "points": Decimal(str(grade_conversion.get(grade.grade, 0) * 10)).quantize(Decimal('0.1'), rounding=ROUND_HALF_UP), - } - for grade in grades_info - ], - "spi": spi, - "cpi": cpi, - "su": su, - "tu": tu, - } - return JsonResponse(response_data) - - -class PreviewGradesAPI(APIView): - permission_classes = [IsAuthenticated] - parser_classes = [MultiPartParser, FormParser] - - def post(self, request): - # Validate user role - user_role = request.data.get("Role") - if user_role != "acadadmin" and user_role!='Assistant Professor' and user_role != 'Professor' and user_role!='Associate Professor': - return Response( - {"error": "Access denied."}, - status=status.HTTP_403_FORBIDDEN, - ) - - csv_file = request.FILES.get("csv_file") - if not csv_file: - return Response( - {"error": "No file provided. Please upload a CSV file."}, - status=status.HTTP_400_BAD_REQUEST, - ) - if not csv_file.name.endswith(".csv"): - return Response( - {"error": "Invalid file format. Please upload a CSV file."}, - status=status.HTTP_400_BAD_REQUEST, - ) - - # Fetch the parameters from the request - course_id = request.data.get("course_id") - academic_year = request.data.get("academic_year") # e.g., "2023-24" - semester_type = request.data.get("semester_type") - programme_type = request.data.get("programme_type") - if not course_id or not academic_year or not semester_type: - return Response( - {"error": "course_id, academic_year and semester_type are required."}, - status=status.HTTP_400_BAD_REQUEST, - ) - - # Extract the starting working year from academic_year. - try: - working_year = int(academic_year.split("-")[0]) - except Exception as e: - return Response( - {"error": "Invalid academic_year format. Expected format like 2023-24."}, - status=status.HTTP_400_BAD_REQUEST, - ) - - # Get course registration records for the given course and working_year and semester_type. - registrations = course_registration.objects.filter( - course_id=course_id, - session=academic_year, - semester_type=semester_type, - ) - - if programme_type: - if programme_type.upper() == 'UG': - programme_list = ['B.Tech', 'B.Des'] - elif programme_type.upper() == 'PG': - programme_list = ['M.Tech', 'M.Des', 'PhD'] - else: - return Response( - {"error": "Invalid programme_type. Must be 'UG' or 'PG'."}, - status=status.HTTP_400_BAD_REQUEST, - ) - - student_ids_with_programme = Student.objects.filter( - programme__in=programme_list - ).values_list('id', flat=True) - - registrations = registrations.filter(student_id__in=student_ids_with_programme) - - # Build a set of registered roll numbers for fast lookup. - registered_rollnos = set() - for reg in registrations.select_related("student_id"): - # Assume the Student model has a roll_no attribute. - if hasattr(reg.student_id, 'id_id'): - registered_rollnos.add(reg.student_id.id_id) - else: - # If the Student model uses a different field, adjust here. - registered_rollnos.add(str(reg.student_id_id)) - - # Parse CSV file - try: - decoded_file = csv_file.read().decode("utf-8") - io_string = StringIO(decoded_file) - reader = csv.DictReader(io_string) - except Exception as e: - return Response( - {"error": f"Error reading CSV file: {str(e)}"}, - status=status.HTTP_400_BAD_REQUEST, - ) - - # Validate required columns - required_columns = ["roll_no", "name", "grade", "remarks", "semester"] - if not all(column in reader.fieldnames for column in required_columns): - return Response( - {"error": f"CSV file must contain the following columns: {', '.join(required_columns)}"}, - status=status.HTTP_400_BAD_REQUEST, - ) - - preview_rows = [] - for row in reader: - roll_no = row["roll_no"] - # Check if the current roll number is registered. - is_registered = roll_no in registered_rollnos - - # Add additional data (e.g. name, grades, remarks, semester, branch) as in CSV - preview_rows.append({ - "roll_no": roll_no, - "name": row["name"], - "branch": row.get("branch", ""), - "grades": row["grade"], - "remarks": row["remarks"], - "semester": row["semester"], - "is_registered": is_registered - }) - - return Response({"preview": preview_rows}, status=status.HTTP_200_OK) - - - -class ResultAnnouncementListAPI(APIView): - """ - GET /api/result-announcements/?role=acadadmin - - Returns an object with: - - announcements: a list of announcement records with batch info. - - batches: a list of available batch options (computed label). - """ - permission_classes = [IsAuthenticated] - - def get(self, request): - role = request.query_params.get("role") - if role != "acadadmin" and role != "Dean Academic": - return Response({"error": "Access denied."}, status=status.HTTP_403_FORBIDDEN) - - # Get announcements sorted by creation date (most recent first) - announcements = ResultAnnouncement.objects.all().order_by("-created_at") - ann_data = [] - for ann in announcements: - # Compute the batch label. - batch = ann.batch - batch_label = f"{batch.name} - {batch.discipline.acronym} {batch.year}" - ann_data.append({ - "id": ann.id, - "batch": { - "id": batch.id, - "name": batch.name, - "discipline": batch.discipline.acronym, - "year": batch.year, - "label": batch_label - }, - "semester": ann.semester, - "announced": ann.announced, - "created_at": ann.created_at, - }) - - # Fetch available batches (running batches) - batch_objs = Batch.objects.filter(running_batch=True) - batch_options = [] - for b in batch_objs: - # Compute a label exactly as above. - label = f"{b.name} - {b.discipline.acronym} {b.year}" - batch_options.append({"id": b.id, "label": label}) - - return Response({"announcements": ann_data, "batches": batch_options}, status=status.HTTP_200_OK) - - -class UpdateAnnouncementAPI(APIView): - """ - POST /api/update-announcement/ - Request Body: - - id: announcement record ID - - announced: boolean value - - Role: must be "acadadmin" - """ - permission_classes = [IsAuthenticated] - - def post(self, request): - role = request.data.get("Role") - if role != "acadadmin" and role != "Dean Academic": - return Response({"error": "Access denied."}, status=status.HTTP_403_FORBIDDEN) - announcement_id = request.data.get("id") - announced = request.data.get("announced") - try: - ann = ResultAnnouncement.objects.get(id=announcement_id) - ann.announced = announced - ann.save() - return Response({"success": True}, status=status.HTTP_200_OK) - except ResultAnnouncement.DoesNotExist: - return Response({"error": "Announcement not found."}, status=status.HTTP_404_NOT_FOUND) - except Exception as e: - traceback.print_exc() - return Response({"error": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) - - -class CreateAnnouncementAPI(APIView): - """ - POST /api/create-announcement/ - Body (JSON): - - Role: must be "acadadmin" - - batch: integer (Batch PK) - - semester: integer - """ - permission_classes = [IsAuthenticated] - - def post(self, request): - try: - role = request.data.get("Role") - if role != "acadadmin" and role != "Dean Academic": - return Response({"error": "Access denied."}, status=status.HTTP_403_FORBIDDEN) - - batch_id = request.data.get("batch") - semester = request.data.get("semester") - if not batch_id or not semester: - return Response({"error": "Batch and Semester are required."}, status=status.HTTP_400_BAD_REQUEST) - - batch_obj = Batch.objects.filter(id=batch_id).first() - if not batch_obj: - return Response({"error": "Batch not found."}, status=status.HTTP_404_NOT_FOUND) - - # Try to get existing announcement, or create if missing - ann, created = ResultAnnouncement.objects.get_or_create( - batch=batch_obj, - semester=semester, - defaults={"announced": False} - ) - - batch_label = f"{batch_obj.name} - {batch_obj.discipline.acronym} {batch_obj.year}" - data = { - "id": ann.id, - "batch": {"id": batch_obj.id, "label": batch_label}, - "semester": ann.semester, - "announced": ann.announced, - "created_at": ann.created_at, - } - - if created: - return Response(data, status=status.HTTP_201_CREATED) - else: - # Already existed - return Response(data, status=status.HTTP_200_OK) - - except Exception as e: - traceback.print_exc() - return Response({"error": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) - -from collections import OrderedDict - - -def make_label(no: int, sem_type: str) -> str: - """ - - odd → "Semester " - - even & Even Semester → "Semester " - - even & Summer Semester → "Summer " - """ - if no % 2 == 1: - return f"Semester {no}" - if sem_type == "Summer Semester": - return f"Summer {no // 2}" - return f"Semester {no}" - -class StudentSemesterListView(APIView): - permission_classes = [IsAuthenticated] - - def get(self, request, *args, **kwargs): - roll_number = request.user.username - qs = (Student_grades.objects - .filter(roll_no=roll_number) - .values_list('semester', 'semester_type') - .distinct() - .order_by('semester')) - unique = OrderedDict() - for sem_no, sem_type in qs: - label = make_label(sem_no, sem_type or "") - unique[(sem_no, sem_type)] = label - - semesters = [ - {"semester_no": no, "semester_type": typ, "label": lbl} - for (no, typ), lbl in unique.items() - ] - - return JsonResponse({"success": True, "semesters": semesters}) - - -class GradeStatusAPI(APIView): - """ - API to get grade status for all courses in a given academic year and semester type. - Shows course information, professor name, and submission/verification status. - - Expected Request: - POST /api/examination/grade_status/ - Headers: - Authorization: Token - Body (JSON): - { - "Role": "acadadmin", - "academic_year": "2024-25", - "semester_type": "Odd Semester" - } - - Response: - 200 OK - List of courses with status information - 403 Forbidden - Access denied - 400 Bad Request - Missing required fields - """ - permission_classes = [IsAuthenticated] - - def post(self, request): - role = request.data.get("Role") - academic_year = request.data.get("academic_year") - semester_type = request.data.get("semester_type") - - # Role-based access control - if role not in ["acadadmin", "Dean Academic"]: - return Response( - {"success": False, "error": "Access denied."}, - status=status.HTTP_403_FORBIDDEN - ) - - # Validate required parameters - if not academic_year or not semester_type: - return Response( - {"error": "Academic year and semester type are required."}, - status=status.HTTP_400_BAD_REQUEST - ) - - try: - # Parse academic year to get working year - working_year, session = parse_academic_year(academic_year, semester_type) - - # Get all courses that have registrations for this academic year and semester type - # Use values_list with flat=True for better performance - course_ids = course_registration.objects.filter( - session=academic_year, - semester_type=semester_type - ).values_list('course_id', flat=True).distinct() - - # Fetch courses with select_related for better performance - courses = Courses.objects.filter(id__in=course_ids).order_by('code') - - # Bulk fetch all instructors to avoid N+1 queries - instructors_map = {} - instructors = CourseInstructor.objects.filter( - course_id__in=course_ids, - year=working_year, - semester_type=semester_type - ).select_related() - - for instructor in instructors: - instructors_map[instructor.course_id_id] = instructor - - # Bulk fetch professor names to avoid individual User queries - instructor_ids = [inst.instructor_id_id for inst in instructors] - users_map = {} - if instructor_ids: - users = get_user_model().objects.filter(username__in=instructor_ids) - users_map = { - user.username: f"{user.first_name} {user.last_name}".strip() - for user in users - } - - # Bulk fetch grade submission and verification status - submitted_courses = set( - Student_grades.objects.filter( - course_id__in=course_ids, - academic_year=academic_year, - semester_type=semester_type - ).values_list('course_id', flat=True).distinct() - ) - - verified_courses = set( - Student_grades.objects.filter( - course_id__in=course_ids, - academic_year=academic_year, - semester_type=semester_type, - verified=True - ).values_list('course_id', flat=True).distinct() - ) - - # Bulk fetch authentication records - auth_records_map = {} - auth_records = authentication.objects.filter( - course_id__in=course_ids, - course_year=working_year - ) - - for auth in auth_records: - auth_records_map[auth.course_id_id] = auth - - # Build response data efficiently - grade_status_list = [] - - for course in courses: - # Get instructor information from pre-fetched data - instructor = instructors_map.get(course.id) - professor_name = "Not Assigned" - - if instructor: - professor_name = users_map.get( - instructor.instructor_id_id, - instructor.instructor_id_id - ) - - # Determine status from pre-fetched sets - submitted = "Submitted" if course.id in submitted_courses else "Not Submitted" - verified = "Verified" if course.id in verified_courses else "Not Verified" - - # Check validation status - validated = "Not Validated" - if course.id in verified_courses: # Only check if verified - auth_record = auth_records_map.get(course.id) - if (auth_record and auth_record.authenticator_1 and - auth_record.authenticator_2 and auth_record.authenticator_3): - validated = "Validated" - - grade_status_list.append({ - "course_code": course.code, - "course_name": course.name, - "course_id": course.id, - "professor_name": professor_name, - "submitted": submitted, - "verified": verified, - "validated": validated, - "credits": course.credit, - "version": course.version - }) - - return Response({ - "success": True, - "grade_status": grade_status_list, - "academic_year": academic_year, - "semester_type": semester_type - }, status=status.HTTP_200_OK) - - except Exception as e: - return Response( - {"error": f"An error occurred: {str(e)}"}, - status=status.HTTP_500_INTERNAL_SERVER_ERROR - ) - -class GenerateStudentResultPDFAPI(APIView): - """ - API endpoint to generate PDF report for student examination results - """ - permission_classes = [IsAuthenticated] - - def post(self, request): - try: - # Get data from request - data = request.data - - # Check if student_info is provided, if not fetch it like CheckResultView - student_info = data.get('student_info', {}) - courses = data.get('courses', []) - - # If student_info or courses are not provided, fetch them from database - if not student_info or not courses: - # Fetch student data like CheckResultView does - roll_number = request.user.username - semester_no = data.get('semester_no') - semester_type = data.get('semester_type') - - if semester_no is None or semester_type is None: - return JsonResponse( - {"success": False, "message": "semester_no and semester_type are required."}, - status=400, - ) - - try: - student = Student.objects.get(id_id=roll_number) - except Student.DoesNotExist: - return JsonResponse( - {"success": False, "message": "Student record not found."}, - status=404, - ) - - ann = ResultAnnouncement.objects.filter( - batch=student.batch_id, - semester=semester_no, - ).first() - - if not ann or not ann.announced: - return JsonResponse( - {"success": False, "message": "Results not announced yet."}, - status=200, - ) - - grades_info = Student_grades.objects.filter( - roll_no=roll_number, - semester=semester_no, - semester_type=semester_type - ).select_related('course_id') - - academic_year = None - if grades_info.exists(): - academic_year = grades_info.first().academic_year - - spi, su, _ = calculate_spi_for_student(student, semester_no, semester_type) - cpi, tu, _ = calculate_cpi_for_student(student, semester_no, semester_type) - - student_info = { - "name": f"{student.id.user.first_name} {student.id.user.last_name}".strip(), - "rollNumber": student.id.user.username, - "roll_number": student.id.user.username, - "programme": student.programme, - "batch": str(student.batch_id) if student.batch_id else str(student.batch), - "branch": student.id.department.name if student.id.department else "", - "department": student.id.department.name if student.id.department else "", - "semester": student.curr_semester_no, - "academicYear": academic_year or "", - "academic_year": academic_year or "" - } - - # Build courses list like CheckResultView - from applications.academic_information.models import grade_conversion - from decimal import Decimal, ROUND_HALF_UP - - courses = [ - { - "coursecode": grade.course_id.code, - "courseid": grade.course_id.id, - "coursename": grade.course_id.name, - "credits": grade.course_id.credit, - "grade": grade.grade, - "points": Decimal(str(grade_conversion.get(grade.grade, 0) * 10)).quantize(Decimal('0.1'), rounding=ROUND_HALF_UP), - } - for grade in grades_info - ] - else: - # Use provided data - spi = float(data.get('spi', 0)) - cpi = float(data.get('cpi', 0)) - su = int(data.get('su', 0)) - tu = int(data.get('tu', 0)) - - semester_no = data.get('semester_no', 1) - semester_type = data.get('semester_type', '') - semester_label = data.get('semester_label', '') - - formatted_semester = format_semester_display(semester_no, semester_type, semester_label) - - # Create PDF buffer - buffer = BytesIO() - - # Create PDF metadata to fix "(anonymous)" title issue - is_transcript = data.get('is_transcript', False) or request.path.find('transcript') != -1 or data.get('document_type') == 'transcript' - doc_type = "Transcript" if is_transcript else "Student Result" - pdf_title = f"{doc_type} - {student_info.get('name', student_info.get('rollNumber', 'Student'))} - {formatted_semester}" - - doc = SimpleDocTemplate( - buffer, - pagesize=letter, - rightMargin=0.5*inch, - leftMargin=0.5*inch, - topMargin=0.5*inch, - bottomMargin=0.5*inch, - title=pdf_title, - author="PDPM IIITDM Jabalpur", - subject=f"{doc_type} Report - {formatted_semester}", - creator="Fusion Academic System" - ) - - story = [] - styles = getSampleStyleSheet() - - # Header styles - title_style = ParagraphStyle( - 'Title', - parent=styles['Heading1'], - fontSize=14, - spaceAfter=2, - leading=18, - alignment=1, # Center - fontName='Times-Bold' - ) - - subtitle_style = ParagraphStyle( - 'Subtitle', - parent=styles['Normal'], - fontSize=11, - spaceAfter=6, - alignment=1, # Center - fontName='Times-Roman' - ) - - # Header with logo on the left side - header_data = [] - - # Try to add logo - try: - from django.conf import settings - import os - logo_path = os.path.join(settings.MEDIA_ROOT, 'logo2.jpg') - if os.path.exists(logo_path): - # Make logo smaller to match website appearance - logo = Image(logo_path, width=0.8*inch, height=0.8*inch) - - # Create separate paragraphs for better text formatting - title_para = Paragraph("PDPM Indian Institute of Information Technology,", title_style) - subtitle1_para = Paragraph("Design & Manufacturing, Jabalpur", title_style) - subtitle2_para = Paragraph("(An Institute of National Importance under MoE, Govt. of India)", subtitle_style) - subtitle3_para = Paragraph("Semester Grade Report / Marksheet", subtitle_style) - - # Create header table with logo and text - header_table_data = [ - [logo, [title_para, subtitle1_para, subtitle2_para, subtitle3_para]] - ] - header_table = Table(header_table_data, colWidths=[1*inch, 6*inch]) - header_table.setStyle(TableStyle([ - ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'), - ('ALIGN', (0, 0), (0, 0), 'CENTER'), # Logo center aligned - ('ALIGN', (1, 0), (1, 0), 'CENTER'), # Text center aligned - ('LEFTPADDING', (0, 0), (-1, -1), 5), - ('RIGHTPADDING', (0, 0), (-1, -1), 5), - ('TOPPADDING', (0, 0), (-1, -1), 5), - ('BOTTOMPADDING', (0, 0), (-1, -1), 5), - ])) - story.append(header_table) - else: - # No logo, just text - story.append(Paragraph("PDPM Indian Institute of Information Technology,", title_style)) - story.append(Paragraph("Design & Manufacturing, Jabalpur", title_style)) - story.append(Paragraph("(An Institute of National Importance under MoE, Govt. of India)", subtitle_style)) - story.append(Paragraph("Semester Grade Report / Marksheet", subtitle_style)) - except Exception as e: - # If logo fails, continue with text only - story.append(Paragraph("PDPM Indian Institute of Information Technology,", title_style)) - story.append(Paragraph("Design & Manufacturing, Jabalpur", title_style)) - story.append(Paragraph("(An Institute of National Importance under MoE, Govt. of India)", subtitle_style)) - story.append(Paragraph("Semester Grade Report / Marksheet", subtitle_style)) - - story.append(Spacer(1, 12)) - - # Student Information Table - cell_style = ParagraphStyle( - 'CellStyle', - parent=styles['Normal'], - fontSize=10, - fontName='Times-Roman', - wordWrap='CJK' - ) - - student_data = [ - [Paragraph('Name of Student:', cell_style), Paragraph(student_info.get('name', 'N/A'), cell_style), Paragraph('Roll No.:', cell_style), Paragraph(student_info.get('rollNumber', student_info.get('roll_number', 'N/A')), cell_style)], - [Paragraph('Programme:', cell_style), Paragraph(student_info.get('programme', 'N/A'), cell_style), Paragraph('Branch:', cell_style), Paragraph(student_info.get('branch', student_info.get('department', 'N/A')), cell_style)], - [Paragraph('Semester:', cell_style), Paragraph(formatted_semester, cell_style), Paragraph('Academic Year:', cell_style), Paragraph(student_info.get('academicYear', student_info.get('academic_year', 'N/A')), cell_style)] - ] - - student_table = Table(student_data, colWidths=[1.14*inch, 3.56*inch, 1.3*inch, 1.0*inch]) - student_table.setStyle(TableStyle([ - ('FONTNAME', (0, 0), (-1, -1), 'Times-Roman'), - ('FONTSIZE', (0, 0), (-1, -1), 10), - ('GRID', (0, 0), (-1, -1), 1, colors.black), - ('VALIGN', (0, 0), (-1, -1), 'TOP'), # Changed from MIDDLE to TOP for better text wrapping - ('LEFTPADDING', (0, 0), (-1, -1), 6), - ('RIGHTPADDING', (0, 0), (-1, -1), 6), - ('TOPPADDING', (0, 0), (-1, -1), 6), - ('BOTTOMPADDING', (0, 0), (-1, -1), 6), - ])) - - story.append(student_table) - story.append(Spacer(1, 12)) - - # Courses Table - headers = ['S. No.', 'Course Code', 'Course Title', 'Credits', 'Grade', 'Grade Points'] - course_data = [headers] - - for i, course in enumerate(courses, 1): - course_data.append([ - str(i), - course.get('coursecode', ''), - course.get('coursename', ''), - str(course.get('credits', '')), - course.get('grade', ''), - str(course.get('points', '')) - ]) - - course_table = Table(course_data, colWidths=[0.5*inch, 1*inch, 3.2*inch, 0.7*inch, 0.6*inch, 1*inch]) - course_table.setStyle(TableStyle([ - ('FONTNAME', (0, 0), (-1, 0), 'Times-Bold'), # Header - ('FONTNAME', (0, 1), (-1, -1), 'Times-Roman'), # Data - ('FONTSIZE', (0, 0), (-1, -1), 9), - ('GRID', (0, 0), (-1, -1), 1, colors.black), - ('ALIGN', (0, 0), (-1, -1), 'CENTER'), - ('ALIGN', (2, 1), (2, -1), 'LEFT'), # Course title left aligned - ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'), - ('BACKGROUND', (0, 0), (-1, 0), colors.lightgrey), - ('LEFTPADDING', (0, 0), (-1, -1), 3), - ('RIGHTPADDING', (0, 0), (-1, -1), 3), - ('TOPPADDING', (0, 0), (-1, -1), 3), - ('BOTTOMPADDING', (0, 0), (-1, -1), 3), - ])) - - story.append(course_table) - story.append(Spacer(1, 15)) - - # Summary Table - summary_data = [ - ['Total Credits Registered:', str(tu), 'Semester Credits Earned:', str(su)], - ['SPI:', f"{spi:.1f}", 'CPI:', f"{cpi:.1f}"] - ] - - summary_table = Table(summary_data, colWidths=[2.2*inch, 0.8*inch, 2.2*inch, 0.8*inch]) - summary_table.setStyle(TableStyle([ - ('FONTNAME', (0, 0), (-1, -1), 'Times-Roman'), - ('FONTSIZE', (0, 0), (-1, -1), 10), - ('ALIGN', (0, 0), (-1, -1), 'LEFT'), - ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'), - ('LEFTPADDING', (0, 0), (-1, -1), 5), - ('RIGHTPADDING', (0, 0), (-1, -1), 5), - ('TOPPADDING', (0, 0), (-1, -1), 4), - ('BOTTOMPADDING', (0, 0), (-1, -1), 4), - ])) - - story.append(summary_table) - story.append(Spacer(1, 25)) - - # Footer - footer_style = ParagraphStyle( - 'Footer', - parent=styles['Normal'], - fontSize=8, - alignment=1, # Center - fontName='Times-Italic' - ) - - from datetime import datetime - current_date = datetime.now().strftime("%d/%m/%Y") - story.append(Paragraph(f"This is a computer-generated document. Generated on {current_date}", footer_style)) - - # Build PDF - doc.build(story) - - # Return PDF response - pdf_data = buffer.getvalue() - buffer.close() - - response = HttpResponse(pdf_data, content_type='application/pdf') - - # Check if this is a transcript request (could be determined by various factors) - # If called from transcript context, use 'transcript_' prefix, otherwise 'result_' - is_transcript = data.get('is_transcript', False) or request.path.find('transcript') != -1 or data.get('document_type') == 'transcript' - - # Create filename with formatted semester for clarity - semester_suffix = formatted_semester.replace(' ', '_').replace(':', '').lower() - - # Choose prefix based on document type - prefix = "transcript_" if is_transcript else "result_" - filename = f"{prefix}{student_info.get('rollNumber', student_info.get('roll_number', 'student'))}_{semester_suffix}.pdf" - response['Content-Disposition'] = f'attachment; filename="{filename}"' - response['Content-Length'] = len(pdf_data) - - return response - - except Exception as e: - return JsonResponse({'error': f'PDF generation failed: {str(e)}'}, status=500) - -class GradeSummaryAPI(APIView): - """ - API to get grade summary statistics for all courses in a given academic year and semester type. - Shows grade distribution (O, A+, A, B+, B, C+, C, D+, D, F, CD, S, X) for each course. - """ - permission_classes = [IsAuthenticated] - - def post(self, request): - role = request.data.get("Role") - academic_year = request.data.get("academic_year") - semester_type = request.data.get("semester_type") - - if role not in ["acadadmin", "Dean Academic"]: - return Response( - {"success": False, "error": "Access denied."}, - status=status.HTTP_403_FORBIDDEN - ) - - if not academic_year or not semester_type: - return Response( - {"error": "Academic year and semester type are required."}, - status=status.HTTP_400_BAD_REQUEST - ) - - try: - from django.db import connection - - with connection.cursor() as cursor: - query = """ - SELECT - ROW_NUMBER() OVER (ORDER BY pc.code) as sno, - pc.code as course_code, - pc.name as course_name, - STRING_AGG(DISTINCT TRIM(CONCAT(u.first_name, ' ', u.last_name)), ', ') as course_instructor, - COUNT(CASE WHEN sg.grade = 'O' THEN 1 END) as grade_o, - COUNT(CASE WHEN sg.grade = 'A+' THEN 1 END) as grade_a_plus, - COUNT(CASE WHEN sg.grade = 'A' THEN 1 END) as grade_a, - COUNT(CASE WHEN sg.grade = 'B+' THEN 1 END) as grade_b_plus, - COUNT(CASE WHEN sg.grade = 'B' THEN 1 END) as grade_b, - COUNT(CASE WHEN sg.grade = 'C+' THEN 1 END) as grade_c_plus, - COUNT(CASE WHEN sg.grade = 'C' THEN 1 END) as grade_c, - COUNT(CASE WHEN sg.grade = 'D+' THEN 1 END) as grade_d_plus, - COUNT(CASE WHEN sg.grade = 'D' THEN 1 END) as grade_d, - COUNT(CASE WHEN sg.grade = 'F' THEN 1 END) as grade_f, - COUNT(CASE WHEN sg.grade = 'CD' THEN 1 END) as grade_cd, - COUNT(CASE WHEN sg.grade = 'S' THEN 1 END) as grade_s, - COUNT(CASE WHEN sg.grade = 'X' THEN 1 END) as grade_x, - COUNT(sg.id) as total_students - FROM - online_cms_student_grades sg - INNER JOIN programme_curriculum_course pc ON sg.course_id_id = pc.id - LEFT JOIN programme_curriculum_courseinstructor ci ON ( - ci.course_id_id = pc.id - AND ci.year = sg.year - ) - LEFT JOIN auth_user u ON ci.instructor_id_id = u.username - WHERE - sg.academic_year = %s - AND sg.semester_type = %s - AND sg.grade IS NOT NULL - AND sg.grade <> '' - GROUP BY - pc.code, pc.name - HAVING - COUNT(sg.id) > 0 - ORDER BY - pc.code - """ - - cursor.execute(query, [academic_year, semester_type]) - columns = [col[0] for col in cursor.description] - results = [dict(zip(columns, row)) for row in cursor.fetchall()] - - return Response({ - "success": True, - "grade_summary": results, - "academic_year": academic_year, - "semester_type": semester_type, - "total_courses": len(results) - }, status=status.HTTP_200_OK) - - except Exception as e: - return Response( - {"error": f"An error occurred: {str(e)}"}, - status=status.HTTP_500_INTERNAL_SERVER_ERROR - ) diff --git a/FusionIIIT/applications/examination/apps.py b/FusionIIIT/applications/examination/apps.py deleted file mode 100644 index c7f2cb3f9..000000000 --- a/FusionIIIT/applications/examination/apps.py +++ /dev/null @@ -1,5 +0,0 @@ -from django.apps import AppConfig - - -class ExaminationConfig(AppConfig): - name = 'applications.examination' diff --git a/FusionIIIT/applications/examination/forms.py b/FusionIIIT/applications/examination/forms.py deleted file mode 100644 index 30314d400..000000000 --- a/FusionIIIT/applications/examination/forms.py +++ /dev/null @@ -1,4 +0,0 @@ -from django import forms - -class StudentGradeForm(forms.Form): - grades = forms.CharField(widget=forms.MultipleHiddenInput) diff --git a/FusionIIIT/applications/examination/migrations/0001_initial.py b/FusionIIIT/applications/examination/migrations/0001_initial.py deleted file mode 100644 index 4bc165c9d..000000000 --- a/FusionIIIT/applications/examination/migrations/0001_initial.py +++ /dev/null @@ -1,48 +0,0 @@ -# Generated by Django 3.1.5 on 2024-07-16 15:44 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - ('programme_curriculum', '0001_initial'), - ] - - operations = [ - migrations.CreateModel( - name='grade', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('student', models.CharField(max_length=20)), - ('curriculum', models.CharField(max_length=50)), - ('semester_id', models.CharField(default='', max_length=10)), - ('grade', models.CharField(default='B', max_length=5)), - ], - ), - migrations.CreateModel( - name='hidden_grades', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('student_id', models.CharField(max_length=20)), - ('course_id', models.CharField(max_length=50)), - ('semester_id', models.CharField(max_length=10)), - ('grade', models.CharField(max_length=5)), - ], - ), - migrations.CreateModel( - name='authentication', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('authenticator_1', models.BooleanField(default=False)), - ('authenticator_2', models.BooleanField(default=False)), - ('authenticator_3', models.BooleanField(default=False)), - ('year', models.DateField(auto_now_add=True)), - ('course_year', models.IntegerField(default=2024)), - ('course_id', models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='programme_curriculum.course')), - ], - ), - ] diff --git a/FusionIIIT/applications/examination/migrations/0002_resultannouncement.py b/FusionIIIT/applications/examination/migrations/0002_resultannouncement.py deleted file mode 100644 index 3057d5343..000000000 --- a/FusionIIIT/applications/examination/migrations/0002_resultannouncement.py +++ /dev/null @@ -1,24 +0,0 @@ -# Generated by Django 3.1.5 on 2025-04-16 06:29 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('examination', '0001_initial'), - ] - - operations = [ - migrations.CreateModel( - name='ResultAnnouncement', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('semester', models.PositiveIntegerField()), - ('announced', models.BooleanField(default=False)), - ('created_at', models.DateTimeField(auto_now_add=True)), - ('batch', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='programme_curriculum.batch')), - ], - ), - ] diff --git a/FusionIIIT/applications/examination/models.py b/FusionIIIT/applications/examination/models.py deleted file mode 100644 index 52de41c9c..000000000 --- a/FusionIIIT/applications/examination/models.py +++ /dev/null @@ -1,49 +0,0 @@ -from django.db import models -from applications.academic_procedures.models import (course_registration) -from applications.online_cms.models import (Student_grades) -from applications.academic_information.models import Course -from applications.programme_curriculum.models import Course as Courses, CourseInstructor, Batch -# Create your models here. - - -class hidden_grades(models.Model): - student_id = models.CharField(max_length=20) - course_id = models.CharField(max_length=50) - semester_id = models.CharField(max_length=10) - grade = models.CharField(max_length=5) - - def __str__(self): - return f"{self.student_id}, {self.course_id}" - - -class authentication(models.Model): - authenticator_1 = models.BooleanField(default=False) - authenticator_2 = models.BooleanField(default=False) - authenticator_3 = models.BooleanField(default=False) - year = models.DateField(auto_now_add=True) - course_id = models.ForeignKey(Courses, on_delete=models.CASCADE, default=1) - course_year = models.IntegerField(default=2024) - - @property - def working_year(self): - return self.year.year - - def __str__(self): - return f"{self.course_id} , {self.course_year}" - - -class grade(models.Model): - student = models.CharField(max_length=20) - curriculum = models.CharField(max_length=50) - semester_id = models.CharField(max_length=10, default='') - grade = models.CharField(max_length=5, default="B") - -class ResultAnnouncement(models.Model): - batch = models.ForeignKey(Batch, on_delete=models.CASCADE) - semester = models.PositiveIntegerField() - announced = models.BooleanField(default=False) - created_at = models.DateTimeField(auto_now_add=True) - - def __str__(self): - status = "Announced" if self.announced else "Not Announced" - return f"{self.batch.label} - Sem {self.semester} - {status}" diff --git a/FusionIIIT/applications/examination/tests.py b/FusionIIIT/applications/examination/tests.py deleted file mode 100644 index 7ce503c2d..000000000 --- a/FusionIIIT/applications/examination/tests.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.test import TestCase - -# Create your tests here. diff --git a/FusionIIIT/applications/examination/urls.py b/FusionIIIT/applications/examination/urls.py deleted file mode 100644 index dffe4ba57..000000000 --- a/FusionIIIT/applications/examination/urls.py +++ /dev/null @@ -1,69 +0,0 @@ - -from django.conf.urls import url -from django.urls import path, include -from . import views -from django.contrib import admin -from .views import update_authentication -from .views import DownloadExcelView, updateGrades -app_name = 'examination' - -urlpatterns = [ - url(r'^api/', include('applications.examination.api.urls')), - url(r'^$', views.exam, name='exam'), - - - url(r'submit/', views.submit, name='submit'),#old - # url(r'verify/', views.verify, name='verify'),#old -# url(r'publish/', views.publish, name='publish'),#old -# url(r'notReady_publish/', views.notReady_publish, name='notReady_publish'),#old -# url(r'timetable/', views.timetable, name='timetable'),#old - # entering and updataing grade -# path('entergrades/', views.entergrades, name='entergrades'),#old -# path('update_hidden_grades_multiple/', views.Updatehidden_gradesMultipleView.as_view(), - # name='update_hidden_grades_multiple'),#old - # path('verifygrades/', views.verifygrades, name='verifygrades'),#old -# path('update_hidden_grades_multiple/', views.Updatehidden_gradesMultipleView.as_view(), -# name='update_hidden_grades_multiple'),#old -# path('submit_hidden_grades_multiple/', views.Submithidden_gradesMultipleView.as_view(), -# name='submit_hidden_grades_multiple'),#old - path('download_excel/', DownloadExcelView.as_view(), name='download_excel'),#old - - #new - url(r'submitGrades/', views.submitGrades.as_view(), name='submitGrades'),#new -# url(r'submitEntergrades/', views.submitEntergrades, name='submitEntergrades'),#new -# path('submitEntergradesStoring/', views.submitEntergradesStoring.as_view(),#new -# name='submitEntergradesStoring'), - #new - url(r'updateGrades/', views.updateGrades, name='updateGrades'),#new - path('updateEntergrades/', views.updateEntergrades, name='updateEntergrades'),#new - path('moderate_student_grades/', views.moderate_student_grades.as_view(),#new - name='moderate_student_grades'), - # authenticate new -# path('authenticate/', views.authenticate, name='authenticate'), #new -# path('authenticategrades/', views.authenticategrades, -# name='authenticategrades'),#new -# path('update_authentication/', update_authentication.as_view(), -# name='update_authentication'),#new - # generate transcript new - path('generate_transcript/', views.generate_transcript, - name='generate_transcript'), #new - path('generate_transcript_form/', views.generate_transcript_form, - name='generate_transcript_form'),#new - # Announcement - url(r'announcement/', views.announcement, name='announcement'),#new - path('upload_grades/',views.upload_grades,name='upload_grades'), - path('message/',views.show_message,name='message'), - path('submitGradesProf/',views.submitGradesProf,name='submitGradesProf'), - path('download_template/',views.download_template,name='download_template'), - path('verifyGradesDean/',views.verifyGradesDean,name='verifyGradesDean'), - path('updateEntergradesDean/',views.updateEntergradesDean,name='updateEnterGradesDean'), - path('upload_grades_prof/',views.upload_grades_prof,name='upload_grades_prof'), - path('validateDean/',views.validateDean,name='validateDean'), - path('validateDeanSubmit/',views.validateDeanSubmit,name='validateDeanSubmit'), - path('downloadGrades/',views.downloadGrades,name='downloadGrades'), - path('generate_pdf/',views.generate_pdf,name='generate_pdf'), - path('generate-result/',views.generate_result,name='generate_pdf'), -# path('get_courses/',views.get_courses,name='get_courses'), -# path('checkresult/',views.checkresult,name='checkresult'), -# path('grades_report/',views.grades_report,name='grades_report'), -] diff --git a/FusionIIIT/applications/examination/views.py b/FusionIIIT/applications/examination/views.py deleted file mode 100644 index 101c98d9b..000000000 --- a/FusionIIIT/applications/examination/views.py +++ /dev/null @@ -1,1953 +0,0 @@ -from notifications.signals import notify -from django.views import View -from django.views.generic import View -import traceback -from django.http import HttpResponse -from django.conf import settings -from django.contrib.auth import get_user_model -import csv -import json -from openpyxl import Workbook -from openpyxl.styles import Alignment, Font -from openpyxl.utils import get_column_letter -from io import BytesIO,StringIO -from django.db.models import IntegerField -from django.db.models.functions import Cast -from django.db.models.query_utils import Q -from django.http import request, HttpResponse -from django.shortcuts import get_object_or_404, render, HttpResponse, redirect -from django.http import HttpResponse, HttpResponseRedirect -import itertools -from django.contrib import messages -from django.contrib.auth.decorators import login_required -from django.contrib.auth.models import User -from datetime import date -import requests -from django.db.models import Q -from django.shortcuts import get_object_or_404, render, redirect -from django.contrib.auth.models import User -from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger -from applications.academic_information.models import Spi, Student, Curriculum -from applications.globals.models import ( - Designation, - ExtraInfo, - HoldsDesignation, - Faculty, -) -from applications.eis.models import faculty_about, emp_research_projects -from applications.academic_information.models import Course -from applications.academic_procedures.models import course_registration, Register,Semester -from applications.programme_curriculum.filters import CourseFilter -from notification.views import examination_notif -from applications.department.models import SpecialRequest, Announcements -from applications.globals.models import ( - DepartmentInfo, - Designation, - ExtraInfo, - Faculty, - HoldsDesignation, -) -from jsonschema import validate -from jsonschema.exceptions import ValidationError -from django.shortcuts import render, redirect, HttpResponse -from rest_framework.views import APIView -from rest_framework.response import Response -from rest_framework import status -from .models import hidden_grades, grade -from .forms import StudentGradeForm -from rest_framework.views import APIView -from rest_framework.response import Response -from rest_framework import status -from .models import hidden_grades, authentication -from rest_framework.permissions import AllowAny -from applications.online_cms.models import Student_grades -from django.http import JsonResponse -import csv -from applications.programme_curriculum.models import Course as Courses, CourseInstructor,Discipline,Batch, CourseSlot -from django.urls import reverse -from reportlab.pdfgen import canvas -from reportlab.lib.pagesizes import letter -from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle -from reportlab.platypus import SimpleDocTemplate, Table, TableStyle, Paragraph, Spacer -from reportlab.lib import colors -from reportlab.lib.colors import HexColor -from reportlab.lib.units import inch -@login_required(login_url="/accounts/login") -def exam(request): - """ - This function is used to Differenciate acadadmin and all other user. - - @param: - request - contains metadata about the requested page - - @variables: - user_details - Gets the information about the logged in user. - des - Gets the designation about the looged in user. - #""" - user_details = ExtraInfo.objects.get(user=request.user) - des = request.session.get("currentDesignationSelected") - if ( - str(des) == "Associate Professor" - or str(des) == "Professor" - or str(des) == "Assistant Professor" - ): - return HttpResponseRedirect("/examination/submitGradesProf/") - elif request.session.get("currentDesignationSelected") == "acadadmin": - return HttpResponseRedirect("/examination/updateGrades/") - elif request.session.get("currentDesignationSelected") == "Dean Academic": - return HttpResponseRedirect("/examination/verifyGradesDean/") - # elif request.session.get("currentDesignationSelected") == "student": - # return HttpResponseRedirect("/examination/checkresult/") - return HttpResponseRedirect("/dashboard/") - - -@login_required(login_url='/accounts/login') -def submit(request): - des = request.session.get("currentDesignationSelected") - if des == "acadadmin" or des=="Dean Academic" : - pass - else: - return HttpResponseRedirect('/dashboard/') - unique_course_ids = course_registration.objects.values( - 'course_id').distinct() - - # Cast the course IDs to integers - unique_course_ids = unique_course_ids.annotate( - course_id_int=Cast('course_id', IntegerField())) - - # Retrieve course names and course codes based on unique course IDs - courses_info = Course.objects.filter( - id__in=unique_course_ids.values_list('course_id_int', flat=True)) - - return render(request, '../templates/examination/submit.html', {'courses_info': courses_info}) - - -@login_required(login_url='/accounts/login') -def verify(request): - unique_course_ids = hidden_grades.objects.values('course_id').distinct() - - unique_course_ids = unique_course_ids.annotate( - course_id_int=Cast('course_id', IntegerField())) - - courses_info = Course.objects.filter( - id__in=unique_course_ids.values_list('course_id_int', flat=True)) - - return render(request, '../templates/examination/verify.html', {'courses_info': courses_info}) - - -@login_required(login_url='/accounts/login') -def publish(request): - return render(request, '../templates/examination/publish.html', {}) - - -@login_required(login_url='/accounts/login') -def notReady_publish(request): - return render(request, '../templates/examination/notReady_publish.html', {}) - - -@login_required(login_url='/accounts/login') -def timetable(request): - return render(request, '../templates/examination/timetable.html', {}) - - -def browse_announcements(): - """ - This function is used to browse Announcements Department-Wise - made by different faculties and admin. - - @variables: - cse_ann - Stores CSE Department Announcements - ece_ann - Stores ECE Department Announcements - me_ann - Stores ME Department Announcements - sm_ann - Stores SM Department Announcements - all_ann - Stores Announcements intended for all Departments - context - Dictionary for storing all above data - - """ - cse_ann = Announcements.objects.filter(department="CSE") - ece_ann = Announcements.objects.filter(department="ECE") - me_ann = Announcements.objects.filter(department="ME") - sm_ann = Announcements.objects.filter(department="SM") - all_ann = Announcements.objects.filter(department="ALL") - print(cse_ann) - context = { - "cse": cse_ann, - "ece": ece_ann, - "me": me_ann, - "sm": sm_ann, - "all": all_ann - } - - return context - - -def get_to_request(username): - """ - This function is used to get requests for the receiver - - @variables: - req - Contains request queryset - - """ - req = SpecialRequest.objects.filter(request_receiver=username) - return req - - -def entergrades(request): - course_id = request.GET.get('course') - semester_id = request.GET.get('semester') - - course_present = hidden_grades.objects.filter( - course_id=course_id, semester_id=semester_id) - - if (course_present): - return render(request, 'examination/all_course_grade_filled.html', {}) - - registrations = course_registration.objects.filter( - course_id__id=course_id, semester_id=semester_id) - - context = { - 'registrations': registrations - } - - return render(request, 'examination/entergrades.html', context) - - -def verifygrades(request): - course_id = request.GET.get('course') - semester_id = request.GET.get('semester') - - registrations = hidden_grades.objects.filter( - course_id=course_id, semester_id=semester_id) - - context = { - 'registrations': registrations - } - - return render(request, 'examination/verifygrades.html', context) - - -def authenticate(request): # new - # Retrieve unique course IDs from hidden_grades - unique_course_ids = Student_grades.objects.values('course_id').distinct() - - # Cast the course IDs to integers - unique_course_ids = unique_course_ids.annotate( - course_id_int=Cast('course_id', IntegerField())) - - # Retrieve course names and course codes based on unique course IDs - courses_info = Courses.objects.filter( - id__in=unique_course_ids.values_list('course_id_int', flat=True)) - working_years = Student_grades.objects.values( - 'year').distinct() - context = { - 'courses_info': courses_info, - 'working_years':working_years - - } - print(working_years) - return render(request, '../templates/examination/authenticate.html', context) - - -@login_required(login_url='/accounts/login') -def authenticategrades(request): # new - course_id = request.GET.get('course') - year = request.GET.get('year') - - print(course_id) - print(year) - - course_instance = Courses.objects.get(id=course_id) - registrations = authentication.objects.filter( - course_id=course_instance, course_year=year) - - if registrations: - # Registrations exist, pass them to the template context - context = { - 'registrations': registrations, - 'year': year - } - else: - course_instance = Courses.objects.get(id=course_id) - course_present = Student_grades.objects.filter( - course_id=course_id, year=year) - if (course_present): - authentication_object = authentication.objects.create( - course_id=course_instance, course_year=year) - registrations = authentication.objects.filter( - course_id=course_instance, course_year=year) - - context = { - 'registrations': registrations, - 'course_year': year, - } - - context = { - 'registrations': registrations, - 'year': year - } - - return render(request, 'examination/authenticategrades.html', context) - - -# def examination_notif(sender, recipient, type): -# try: -# url = 'examination:examination' -# module = 'examination' -# verb = type -# flag = "examination" - -# notify.send(sender=sender, -# recipient=recipient, -# url=url, -# module=module, -# verb=verb, -# flag=flag) - -# except Exception as e: -# print("Error sending notification:", e) - - -@login_required(login_url='/accounts/login') -def announcement(request): - des = request.session.get("currentDesignationSelected") - if des == "acadadmin" : - pass - else: - return HttpResponseRedirect('/dashboard/') - """ - This function is contains data for Requests and Announcement Related methods. - Data is added to Announcement Table using this function. - - @param: - request - contains metadata about the requested page - - @variables: - usrnm, user_info, ann_maker_id - Stores data needed for maker - batch, programme, message, upload_announcement, - department, ann_date, user_info - Gets and store data from FORM used for Announcements for Students. - - """ - try: - usrnm = get_object_or_404(User, username=request.user.username) - user_info = usrnm.extrainfo - ann_maker_id = user_info.id - requests_received = get_to_request(usrnm) - - if request.method == 'POST': - batch = request.POST.get('batch', '') - programme = request.POST.get('programme', '') - message = request.POST.get('announcement', '') - upload_announcement = request.FILES.get('upload_announcement') - department = request.POST.get('department') - ann_date = date.today() - - obj1 = Announcements.objects.get_or_create( - maker_id=user_info, - batch=batch, - programme=programme, - message=message, - upload_announcement=upload_announcement, - department=department, - ann_date=ann_date - ) - - recipients = User.objects.all() # Modify this query as per your requirements - examination_notif(sender=usrnm, recipient=recipients, type=message) - return render(request,'department/browse_announcements_staff.html') - print(user_info.user_type) - context = browse_announcements() - return render(request, 'examination/announcement_req.html', { - "user_designation": user_info.user_type, - "announcements": context, - "request_to": requests_received - }) - except Exception as e: - - return render(request, 'examination/announcement_req.html', {"error_message": "An error occurred. Please try again later."}) - - -class Updatehidden_gradesMultipleView(APIView): - permission_classes = [AllowAny] - - def post(self, request): - student_ids = request.POST.getlist('student_ids[]') - semester_ids = request.POST.getlist('semester_ids[]') - course_ids = request.POST.getlist('course_ids[]') - grades = request.POST.getlist('grades[]') - - if len(student_ids) != len(semester_ids) != len(course_ids) != len(grades): - return Response({'error': 'Invalid grade data provided'}, status=status.HTTP_400_BAD_REQUEST) - - for student_id, semester_id, course_id, grade in zip(student_ids, semester_ids, course_ids, grades): - # Create an instance of hidden_grades model and save the data - - try: - hidden_grade = hidden_grades.objects.get( - course_id=course_id, student_id=student_id, semester_id=semester_id) - hidden_grade.grade = grade - hidden_grade.save() - except hidden_grades.DoesNotExist: - # If the grade doesn't exist, create a new one - hidden_grade = hidden_grades.objects.create( - course_id=course_id, student_id=student_id, semester_id=semester_id, grade=grade) - hidden_grade.save() - - hidden_grade.save() - - response = HttpResponse(content_type='text/csv') - response['Content-Disposition'] = 'attachment; filename="grades.csv"' - - # Write data to CSV - writer = csv.writer(response) - writer.writerow(['Student ID', 'Semester ID', 'Course ID', 'Grade']) - for student_id, semester_id, course_id, grade in zip(student_ids, semester_ids, course_ids, grades): - writer.writerow([student_id, semester_id, course_id, grade]) - - return response - return render(request, '../templates/examination/grades_updated.html', {}) - - -class Submithidden_gradesMultipleView(APIView): - permission_classes = [AllowAny] - - def post(self, request): - student_ids = request.POST.getlist('student_ids[]') - semester_ids = request.POST.getlist('semester_ids[]') - course_ids = request.POST.getlist('course_ids[]') - grades = request.POST.getlist('grades[]') - - if len(student_ids) != len(semester_ids) != len(course_ids) != len(grades): - return Response({'error': 'Invalid grade data provided'}, status=status.HTTP_400_BAD_REQUEST) - - for student_id, semester_id, course_id, grade in zip(student_ids, semester_ids, course_ids, grades): - # Create an instance of hidden_grades model and save the data - - try: - hidden_grade = hidden_grades.objects.get( - course_id=course_id, student_id=student_id, semester_id=semester_id) - hidden_grade.grade = grade - hidden_grade.save() - except hidden_grades.DoesNotExist: - # If the grade doesn't exist, create a new one - hidden_grade = hidden_grades.objects.create( - course_id=course_id, student_id=student_id, semester_id=semester_id, grade=grade) - hidden_grade.save() - - hidden_grade.save() - - return render(request, '../templates/examination/grades_updated.html', {}) - - -class update_authentication(View): - def post(self, request, *args, **kwargs): - # Extract data from the POST request - course = request.POST.get('course') - course_year = request.POST.get('course_year') - authenticator1 = request.POST.get('authenticator1') - authenticator2 = request.POST.get('authenticator2') - authenticator3 = request.POST.get('authenticator3') - - # Retrieve the registration object - try: - - course_instance = Courses.objects.get(id=course) - registration = authentication.objects.get( - course_id=course_instance, course_year=course_year) - except authentication.DoesNotExist: - # Redirect if registration does not exist - return redirect('examination:submitGrades') - - # Update authenticators if the values have changed - if authenticator1 is not None: - registration.authenticator_1 = (authenticator1 == '1') - else: - registration.authenticator_1 = 0 - if authenticator2 is not None: - registration.authenticator_2 = (authenticator2 == '1') - else: - registration.authenticator_2 = 0 - if authenticator3 is not None: - registration.authenticator_3 = (authenticator3 == '1') - else: - registration.authenticator_3 = 0 - - # Save the changes - registration.save() - - # Redirect to the appropriate page - return redirect('examination:authenticate') - - -class DownloadExcelView(View): - def post(self, request, *args, **kwargs): - # Retrieve form data - student_ids = request.POST.getlist('student_ids[]') - semester_ids = request.POST.getlist('semester_ids[]') - course_ids = request.POST.getlist('course_ids[]') - grades = request.POST.getlist('grades[]') - - # Create a CSV response - response = HttpResponse(content_type='text/csv') - response['Content-Disposition'] = 'attachment; filename="grades.csv"' - - # Write data to CSV - writer = csv.writer(response) - writer.writerow(['Student ID', 'Semester ID', 'Course ID', 'Grade']) - for student_id, semester_id, course_id, grade in zip(student_ids, semester_ids, course_ids, grades): - writer.writerow([student_id, semester_id, course_id, grade]) - - return response - -@login_required(login_url="/accounts/login") -def generate_transcript(request): - des = request.session.get("currentDesignationSelected") - if des == "acadadmin" : - pass - else: - return HttpResponseRedirect('/dashboard/') - - - student_id = request.GET.get('student') - semester = request.GET.get('semester') - courses_registered = Student_grades.objects.filter( - roll_no=student_id, semester=semester) - - # Initialize a dictionary to store course grades - course_grades = {} - - total_course_registered = Student_grades.objects.filter( - roll_no=student_id, semester__lte=semester) - # for each_course in total_course_registered: - # course_name = Curriculum.objects.filter( - # curriculum_id=each_course.curr_id_id) - - - - for course in courses_registered: - try: - # Attempt to fetch the grade for the course from Student_grades - grade = Student_grades.objects.get( - roll_no=student_id, course_id=course.course_id) - - # course_detail = Curriculum.objects.get( - # course_id=course.course_id, batch=grade.batch) - course_instance = Courses.objects.get(id=course.course_id_id) - # check_authentication_object = authentication.objects.filter( - # course_id=course_instance, course_year=grade.year) - # all_authenticators_true = True - - # if check_authentication_object: - # # Iterate over each authentication object - # for auth_obj in check_authentication_object: - # # Check if all authenticators are true - # if not (auth_obj.authenticator_1 and auth_obj.authenticator_2 and auth_obj.authenticator_3): - # all_authenticators_true = False - # break # No need to check further if any authenticator is False - # else: - # Create authentication object if it doesn't exist - # authentication_object = authentication.objects.create( - # course_id=course_instance, course_year=grade.year) - # Get all registrations for the course and year - # registrations = authentication.objects.filter( - # course_id=course_instance, course_year=grade.year) - # all_authenticators_true = False - - course_grades[course_instance] = { - 'grade': grade, - # 'all_authenticators_true': all_authenticators_true - } # Store the grade - except Student_grades.DoesNotExist: - # Grade not available - course_grades[course] = "Grading not done yet" - - context = { - 'courses_grades': course_grades, - 'total_course_registered':total_course_registered - } - # print(context) - - return render(request, 'examination/generate_transcript.html', context) - -# new - -@login_required(login_url="/accounts/login") -def generate_transcript_form(request): - des = request.session.get("currentDesignationSelected") - if des == "acadadmin" : - pass - else: - return HttpResponseRedirect('/dashboard/') - if request.method == 'POST': - programme = request.POST.get('programme') - batch = request.POST.get('batch') - specialization = request.POST.get('specialization') - semester = request.POST.get('semester') - - if specialization == None: - students = Student.objects.filter( - programme=programme, batch=batch) - else: - students = Student.objects.filter( - programme=programme, batch=batch, specialization=specialization) - - # Pass the filtered students to the template - context = { - 'students': students, - 'semester': semester - } - return render(request, 'examination/generate_transcript_students.html', context) - else: - programmes = Student.objects.values_list( - 'programme', flat=True).distinct() - specializations = Student.objects.exclude( - specialization__isnull=True).values_list('specialization', flat=True).distinct() - batches = Student.objects.values_list('batch', flat=True).distinct() - context = { - 'programmes': programmes, - 'batches': batches, - 'specializations': specializations, - } - - return render(request, 'examination/generate_transcript_form.html', context) - - -@login_required(login_url="/accounts/login") -def updateGrades(request): - des = request.session.get("currentDesignationSelected") - if des == "acadadmin": - pass - else: - if request.is_ajax(): - return JsonResponse({"success": False, "error": "Access denied."}, status=403) - else: # For non-AJAX requests - return HttpResponseRedirect('/dashboard/') - unique_course_ids = Student_grades.objects.filter(verified=False).values("course_id").distinct() - - # Cast the course IDs to integers - unique_course_ids = unique_course_ids.annotate( - course_id_int=Cast("course_id", IntegerField()) - ) - - # Retrieve course names and course codes based on unique course IDs - - # print(unique_course_ids) - courses_info = Courses.objects.filter( - id__in=unique_course_ids.values_list("course_id_int", flat=True) - ) - - unique_year_ids = Student_grades.objects.values('year').distinct() - # print(unique_year_ids) - context = { - "courses_info": courses_info, - "unique_year_ids": unique_year_ids, - } - - return render(request, "../templates/examination/submitGrade.html", context) - -@login_required(login_url="/accounts/login") -def updateEntergrades(request): - des = request.session.get("currentDesignationSelected") - if des == "acadadmin": - pass - else: - if request.is_ajax(): # For AJAX or JSON requests - return JsonResponse({"success": False, "error": "Access denied."}, status=403) - else: # For non-AJAX requests - return HttpResponseRedirect('/dashboard/') - course_id = request.GET.get("course") - - year = request.GET.get("year") - # print(course_id,semester_id ,year) - course_present = Student_grades.objects.filter( - course_id=course_id, year=year - ) - - if not course_present: - context = {"message": "THIS COURSE IS NOT SUBMITTED BY THE INSTRUCTOR"} - return render(request, "../templates/examination/message.html", context) - - verification = course_present.first().verified - print(verification) - if verification: - context = {"message": "THIS COURSE IS VERIFIED"} - return render(request, "../templates/examination/message.html", context) - - context = {"registrations": course_present} - - return render(request, "../templates/examination/updateEntergrades.html", context) - -class moderate_student_grades(APIView): - permission_classes = [AllowAny] - - def post(self, request): - des = request.session.get("currentDesignationSelected") - if des == "acadadmin" or des=="Dean Academic": - pass - else: - if request.is_ajax(): # For AJAX or JSON requests - return JsonResponse({"success": False, "error": "Access denied."}, status=403) - else: # For non-AJAX requests - return HttpResponseRedirect('/dashboard/') - student_ids = request.POST.getlist('student_ids[]') - semester_ids = request.POST.getlist('semester_ids[]') - course_ids = request.POST.getlist('course_ids[]') - grades = request.POST.getlist('grades[]') - allow_resubmission = request.POST.get('allow_resubmission', 'NO') - - if len(student_ids) != len(semester_ids) != len(course_ids) != len(grades): - return Response({'error': 'Invalid grade data provided'}, status=status.HTTP_400_BAD_REQUEST) - - for student_id, semester_id, course_id, grade in zip(student_ids, semester_ids, course_ids, grades): - - try: - grade_of_student = Student_grades.objects.get( - course_id=course_id, roll_no=student_id, semester=semester_id) - grade_of_student.grade = grade - grade_of_student.verified = True - if allow_resubmission == 'YES': - grade_of_student.reSubmit = True - grade_of_student.save() - except Student_grades.DoesNotExist: - # If the grade doesn't exist, create a new one - hidden_grade = hidden_grades.objects.create( - course_id=course_id, student_id=student_id, semester_id=semester_id, grade=grade) - hidden_grade.save() - - response = HttpResponse(content_type='text/csv') - response['Content-Disposition'] = 'attachment; filename="grades.csv"' - - # Write data to CSV - writer = csv.writer(response) - writer.writerow(['Student ID', 'Semester ID', 'Course ID', 'Grade']) - for student_id, semester_id, course_id, grade in zip(student_ids, semester_ids, course_ids, grades): - writer.writerow([student_id, semester_id, course_id, grade]) - print("HELLO") - return response - return render(request, '../templates/examination/grades_updated.html', {}) - - -class submitGrades(APIView): - login_url = "/accounts/login" - - def get(self, request): - des = request.session.get("currentDesignationSelected") - if des == "acadadmin": - pass - else: - if request.is_ajax(): # For AJAX or JSON requests - return JsonResponse({"success": False, "error": "Access denied."}, status=403) - else: # For non-AJAX requests - return HttpResponseRedirect('/dashboard/') - academic_year = request.GET.get('academic_year') - - if academic_year: - if academic_year is None or not academic_year.isdigit(): - return JsonResponse({}) - # Filter course registration based on the academic year and get unique course IDs - unique_course_ids = course_registration.objects.filter( - working_year=academic_year - ).values("course_id").distinct() - - # Cast the course IDs to integers - unique_course_ids = unique_course_ids.annotate( - course_id_int=Cast("course_id", IntegerField()) - ) - - # Retrieve course information based on the unique course IDs - courses_info = Courses.objects.filter( - id__in=unique_course_ids.values_list("course_id_int", flat=True) - ).order_by('code') - - # Return the course information as JSON response - return JsonResponse({"courses": list(courses_info.values())}) - - # If no academic year is provided, return the working years for the dropdown - working_years = course_registration.objects.values("working_year").distinct() - - context = {"working_years": working_years} - - return render(request, "../templates/examination/gradeSubmission.html", context) - -@login_required(login_url="/accounts/login") -def submitEntergrades(request): - - course_id = request.GET.get("course") - year = request.GET.get("year") - if year is None or not year.isdigit(): - message = "YEAR SHOULD NOT BE NONE" - context = {"message": message} - - return render(request, "../templates/examination/message.html", context) - return HttpResponse("Invalid year parameter") - # Handle invalid year parameter - # You can return an error response or redirect the user to an error page - courses_info = Courses.objects.get(id=course_id) - - courses = Student_grades.objects.filter(course_id=courses_info.id, year=year) - - if courses: - message = "THIS Course was Already Submitted" - context = {"message": message} - - return render(request, "../templates/examination/message.html", context) - - students = course_registration.objects.filter( - course_id_id=course_id, working_year=year - ) - - # print(students) - - context = {"registrations": students, "curr_id": course_id, "year": year} - - return render(request, "../templates/examination/gradeSubmissionForm.html", context) - - -class submitEntergradesStoring(APIView): - permission_classes = [AllowAny] - - def post(self, request): - student_ids = request.POST.getlist("student_ids[]") - batch_ids = request.POST.getlist("batch_ids[]") - course_ids = request.POST.getlist("course_ids[]") - semester_ids = request.POST.getlist("semester_ids[]") - year_ids = request.POST.getlist("year_ids[]") - marks = request.POST.getlist("marks[]") - Student_grades = request.POST.getlist("Student_grades[]") - - if ( - len(student_ids) - != len(batch_ids) - != len(course_ids) - != len(semester_ids) - != len(year_ids) - != len(marks) - != len(Student_grades) - ): - return Response( - {"error": "Invalid Student_grades data provided"}, - status=status.HTTP_400_BAD_REQUEST, - ) - - for ( - student_id, - batch_id, - course_id, - semester_id, - year_id, - mark, - Student_grades, - ) in zip( - student_ids, - batch_ids, - course_ids, - semester_ids, - year_ids, - marks, - Student_grades, - ): - # Create an instance of hidden_grades model and save the data - - try: - grade_of_student = Student_grades.objects.get( - course_id=course_id, roll_no=student_id, semester=semester_id - ) - except Student_grades.DoesNotExist: - # If the Student_grades doesn't exist, create a new one - course_instance = Courses.objects.get(id=course_id) - student_grade = Student_grades.objects.create( - course_id=course_instance, - roll_no=student_id, - semester=semester_id, - Student_grades=Student_grades, - batch=batch_id, - year=year_id, - total_marks=mark, - ) - student_grade.save() - - response = HttpResponse(content_type="text/csv") - response["Content-Disposition"] = 'attachment; filename="Student_grades.csv"' - - # Write data to CSV - writer = csv.writer(response) - writer.writerow( - [ - "student_id", - "batch_ids", - "course_id", - "semester_id", - "year_ids", - "marks", - "Student_grades", - ] - ) - for ( - student_id, - batch_id, - course_id, - semester_id, - year_id, - mark, - Student_grades, - ) in zip( - student_ids, - batch_ids, - course_ids, - semester_ids, - year_ids, - marks, - Student_grades, - ): - writer.writerow( - [ - student_id, - batch_id, - course_id, - semester_id, - year_id, - mark, - Student_grades, - ] - ) - - return response - return render(request, "../templates/examination/grades_updated.html", {}) - -@login_required(login_url="/accounts/login") -def upload_grades(request): - if request.method == "POST" and request.FILES.get("csv_file"): - des = request.session.get("currentDesignationSelected") - if des == "acadadmin": - pass - else: - if request.is_ajax(): # For AJAX or JSON requests - return JsonResponse({"success": False, "error": "Access denied."}, status=403) - else: # For non-AJAX requests - return HttpResponseRedirect('/dashboard/') - csv_file = request.FILES["csv_file"] - - if not csv_file.name.endswith(".csv"): - return JsonResponse( - {"error": "Invalid file format. Please upload a CSV file."}, status=400 - ) - - course_id = request.POST.get("course_id") - academic_year = request.POST.get("academic_year") - # semester = request.POST.get('semester') - - if academic_year == "None" or not academic_year.isdigit(): - return JsonResponse( - {"error": "Academic year must be a valid number."}, status=400 - ) - - if not course_id or not academic_year: - return JsonResponse( - {"error": "Course ID and Academic Year are required."}, status=400 - ) - - courses_info = Courses.objects.get(id=course_id) - - courses = Student_grades.objects.filter( - course_id=courses_info.id, year=academic_year - ) - students = course_registration.objects.filter( - course_id_id=course_id, working_year=academic_year - ) - - if not students: - message = "NO STUDENTS REGISTERED IN THIS COURSE THIS SEMESTER" - redirect_url = reverse("examination:message") + f"?message={message}" - return JsonResponse( - {"error": message, "redirect_url": redirect_url}, status=400 - ) - - if courses and not courses.first().reSubmit: - - message = "THIS Course was Already Submitted" - redirect_url = reverse("examination:message") + f"?message={message}" - return JsonResponse( - {"error": message, "redirect_url": redirect_url}, status=400 - ) - - - - try: - # Parse the CSV file - decoded_file = csv_file.read().decode("utf-8").splitlines() - reader = csv.DictReader(decoded_file) - - required_columns = ["roll_no", "grade", "remarks"] - if not all(column in reader.fieldnames for column in required_columns): - return JsonResponse( - { - "error": "CSV file must contain the following columns: roll_no, grade, remarks." - }, - status=400, - ) - - for row in reader: - roll_no = row["roll_no"] - grade = row["grade"] - remarks = row["remarks"] - semester = row["semester"] if "semester" in row and row["semester"] else None - stud = Student.objects.get(id_id=roll_no) - semester = semester or stud.curr_semester_no - batch=stud.batch - reSubmit=False - - Student_grades.objects.update_or_create( - roll_no=roll_no, - course_id_id=course_id, - year=academic_year, - semester=semester, - batch=batch, - # Fields that will be updated if a match is found - defaults={ - 'grade': grade, - 'remarks': remarks, - 'reSubmit': reSubmit, - } - ) - des = request.session.get("currentDesignationSelected") - if ( - str(des) == "Associate Professor" - or str(des) == "Professor" - or str(des) == "Assistant Professor" - ): - return JsonResponse( - { - "message": "Grades uploaded successfully.", - "redirect_url": "/examination/submitGradesProf", - } - ) - return JsonResponse( - { - "message": "Grades uploaded successfully.", - "redirect_url": "/examination/submitGrades", - } - ) - - except Courses.DoesNotExist: - return JsonResponse({"error": "Invalid course ID."}, status=400) - - except Exception as e: - return JsonResponse({"error": f"An error occurred: {e}"}, status=500) - - return JsonResponse( - {"error": "Invalid request. Please upload a CSV file."}, status=400 - ) - -@login_required(login_url="/accounts/login") -def show_message(request): - des = request.session.get("currentDesignationSelected") - if des == "acadadmin" or str(des) == "Associate Professor" or str(des) == "Professor" or str(des) == "Assistant Professor" : - pass - else: - if request.is_ajax(): # For AJAX or JSON requests - return JsonResponse({"success": False, "error": "Access denied."}, status=403) - else: # For non-AJAX requests - return HttpResponseRedirect('/dashboard/') - message = request.GET.get("message", "Default message if none provided.") - des = request.session.get("currentDesignationSelected") - if ( - str(des) == "Associate Professor" - or str(des) == "Professor" - or str(des) == "Assistant Professor" - ): - return render(request, "examination/messageProf.html", {"message": message}) - return render(request, "examination/message.html", {"message": message}) - - -@login_required(login_url="/accounts/login") -def submitGradesProf(request): - des = request.session.get("currentDesignationSelected") - if str(des) == "Associate Professor" or str(des) == "Professor" or str(des) == "Assistant Professor" : - pass - else: - if request.is_ajax(): # For AJAX or JSON requests - return JsonResponse({"success": False, "error": "Access denied."}, status=403) - else: # For non-AJAX requests - return HttpResponseRedirect('/dashboard/') - # print(request.user,1) - unique_course_ids = ( - CourseInstructor.objects.filter(instructor_id_id=request.user.username) - .values("course_id_id") - .distinct() - ) - # unique_course_ids = course_registration.objects.values( - # 'course_id').distinct() - working_years = course_registration.objects.values("working_year").distinct() - course_ids_final=course_registration.objects.filter() - # Cast the course IDs to integers - unique_course_ids = unique_course_ids.annotate( - course_id_int=Cast("course_id", IntegerField()) - ) - - # Retrieve course names and course codes based on unique course IDs - - # print(unique_course_ids) - courses_info = Courses.objects.filter( - id__in=unique_course_ids.values_list("course_id_int", flat=True) - ) - - context = {"courses_info": courses_info, "working_years": working_years} - - print(working_years) - - return render(request, "../templates/examination/submitGradesProf.html", context) - -@login_required(login_url="/accounts/login") -def download_template(request): - des = request.session.get("currentDesignationSelected") - if des not in ["acadadmin", "Associate Professor", "Professor", "Assistant Professor", "Dean Academic"]: - if request.is_ajax(): - return JsonResponse({"success": False, "error": "Access denied."}, status=403) - else: - return HttpResponseRedirect('/dashboard/') - - course = request.GET.get('course') - year = request.GET.get('year') - - if not course or not year: - return JsonResponse({'error': 'Course and year are required'}, status=400) - - try: - # Fetching the custom user model - User = get_user_model() - - course_info = course_registration.objects.filter( - course_id_id=course, - working_year=year - ) - - if not course_info.exists(): - return JsonResponse({'error': 'No registration data found for the provided course and year'}, status=404) - - course_obj = course_info.first().course_id - response = HttpResponse(content_type="text/csv") - filename = f"{course_obj.code}_template_{year}.csv" - response['Content-Disposition'] = f'attachment; filename="{filename}"' - - # Create CSV writer - writer = csv.writer(response) - - # Write header - writer.writerow(["roll_no", "name", "grade", "remarks"]) - - # Write student roll numbers and names - for entry in course_info: - student_entry = entry.student_id - # Fetching the user instance dynamically - student_user = User.objects.get(username=student_entry.id_id) - writer.writerow([student_entry.id_id, student_user.first_name+" "+student_user.last_name, "", ""]) - - return response - - except Exception as e: - print(f"Error in download_template: {str(e)}") - return JsonResponse({'error': 'An unexpected error occurred'}, status=500) - - -@login_required(login_url="/accounts/login") -def verifyGradesDean(request): - des = request.session.get("currentDesignationSelected") - if des=="Dean Academic" : - pass - else: - if request.is_ajax(): # For AJAX or JSON requests - return JsonResponse({"success": False, "error": "Access denied."}, status=403) - else: # For non-AJAX requests - return HttpResponseRedirect('/dashboard/') - unique_course_ids = Student_grades.objects.filter(verified=True).values("course_id").distinct() - - # Cast the course IDs to integers - unique_course_ids = unique_course_ids.annotate( - course_id_int=Cast("course_id", IntegerField()) - ) - - # Retrieve course names and course codes based on unique course IDs - - # print(unique_course_ids) - courses_info = Courses.objects.filter( - id__in=unique_course_ids.values_list("course_id_int", flat=True) - ).order_by("code") - - unique_year_ids = Student_grades.objects.values("year").distinct() - - context = { - "courses_info": courses_info, - "unique_year_ids": unique_year_ids, - } - - return render(request, "../templates/examination/submitGradeDean.html", context) - -@login_required(login_url="/accounts/login") -def updateEntergradesDean(request): - des = request.session.get("currentDesignationSelected") - if des=="Dean Academic" : - pass - else: - if request.is_ajax(): # For AJAX or JSON requests - return JsonResponse({"success": False, "error": "Access denied."}, status=403) - else: # For non-AJAX requests - return HttpResponseRedirect('/dashboard/') - course_id = request.GET.get("course") - year = request.GET.get("year") - course_present = Student_grades.objects.filter( - course_id=course_id, year=year - ) - - if not course_present: - context = {"message": "THIS COURSE IS NOT SUBMITTED BY THE INSTRUCTOR"} - return render(request, "../templates/examination/messageDean.html", context) - - context = {"registrations": course_present} - - return render(request, "../templates/examination/updateEntergradesDean.html", context) - - -@login_required(login_url="/accounts/login") -def upload_grades_prof(request): - des = request.session.get("currentDesignationSelected") - if str(des) == "Associate Professor" or str(des) == "Professor" or str(des) == "Assistant Professor" : - pass - else: - if request.is_ajax(): # For AJAX or JSON requests - return JsonResponse({"success": False, "error": "Access denied."}, status=403) - else: # For non-AJAX requests - return HttpResponseRedirect('/dashboard/') - if request.method == "POST" and request.FILES.get("csv_file"): - csv_file = request.FILES["csv_file"] - - if not csv_file.name.endswith(".csv"): - return JsonResponse( - {"error": "Invalid file format. Please upload a CSV file."}, status=400 - ) - - course_id = request.POST.get("course_id") - academic_year = request.POST.get("academic_year") - # semester = request.POST.get('semester') - - if academic_year == "None" or not academic_year.isdigit(): - return JsonResponse( - {"error": "Academic year must be a valid number."}, status=400 - ) - - if not course_id or not academic_year: - return JsonResponse( - {"error": "Course ID and Academic Year are required."}, status=400 - ) - - courses_info = Courses.objects.get(id=course_id) - - courses = Student_grades.objects.filter( - course_id=courses_info.id, year=academic_year - ) - students = course_registration.objects.filter( - course_id_id=course_id, working_year=academic_year - ) - - if not students: - message = "NO STUDENTS REGISTERED IN THIS COURSE THIS SEMESTER" - redirect_url = reverse("examination:message") + f"?message={message}" - return JsonResponse( - {"error": message, "redirect_url": redirect_url}, status=400 - ) - - if courses and not courses.first().reSubmit: - - message = "THIS Course was Already Submitted" - redirect_url = reverse("examination:message") + f"?message={message}" - return JsonResponse( - {"error": message, "redirect_url": redirect_url}, status=400 - ) - - - - try: - # Parse the CSV file - decoded_file = csv_file.read().decode("utf-8").splitlines() - reader = csv.DictReader(decoded_file) - - required_columns = ["roll_no", "grade", "remarks"] - if not all(column in reader.fieldnames for column in required_columns): - return JsonResponse( - { - "error": "CSV file must contain the following columns: roll_no, grade, remarks." - }, - status=400, - ) - - for row in reader: - roll_no = row["roll_no"] - grade = row["grade"] - remarks = row["remarks"] - semester = row["semester"] if "semester" in row and row["semester"] else None - stud = Student.objects.get(id_id=roll_no) - semester = semester or stud.curr_semester_no - batch=stud.batch - reSubmit=False - - Student_grades.objects.update_or_create( - roll_no=roll_no, - course_id_id=course_id, - year=academic_year, - semester=semester, - batch=batch, - # Fields that will be updated if a match is found - defaults={ - 'grade': grade, - 'remarks': remarks, - 'reSubmit': reSubmit, - } - ) - des = request.session.get("currentDesignationSelected") - if ( - str(des) == "Associate Professor" - or str(des) == "Professor" - or str(des) == "Assistant Professor" - ): - return JsonResponse( - { - "message": "Grades uploaded successfully.", - "redirect_url": "/examination/submitGradesProf", - } - ) - return JsonResponse( - { - "message": "Grades uploaded successfully.", - "redirect_url": "/examination/submitGradesProf", - } - ) - - except Courses.DoesNotExist: - return JsonResponse({"error": "Invalid course ID."}, status=400) - - except Exception as e: - return JsonResponse({"error": f"An error occurred: {e}"}, status=500) - - return JsonResponse( - {"error": "Invalid request. Please upload a CSV file."}, status=400 - ) - - -@login_required(login_url="/accounts/login") -def validateDean(request): - des = request.session.get("currentDesignationSelected") - if des=="Dean Academic" : - pass - else: - if request.is_ajax(): # For AJAX or JSON requests - return JsonResponse({"success": False, "error": "Access denied."}, status=403) - else: # For non-AJAX requests - return HttpResponseRedirect('/dashboard/') - unique_course_ids = Student_grades.objects.filter(verified=True).values("course_id").distinct() - - # Cast the course IDs to integers - unique_course_ids = unique_course_ids.annotate( - course_id_int=Cast("course_id", IntegerField()) - ) - - # Retrieve course names and course codes based on unique course IDs - - # print(unique_course_ids) - courses_info = Courses.objects.filter( - id__in=unique_course_ids.values_list("course_id_int", flat=True) - ) - working_years = course_registration.objects.values("working_year").distinct() - - unique_batch_ids = Student_grades.objects.values("batch").distinct() - - context = {"courses_info": courses_info, "working_years": working_years} - - return render(request, "../templates/examination/validation.html", context) - -@login_required(login_url="/accounts/login") -def validateDeanSubmit(request): - des = request.session.get("currentDesignationSelected") - if des=="Dean Academic" : - pass - else: - if request.is_ajax(): # For AJAX or JSON requests - return JsonResponse({"success": False, "error": "Access denied."}, status=403) - else: # For non-AJAX requests - return HttpResponseRedirect('/dashboard/') - if request.method == "POST" and request.FILES.get("csv_file"): - csv_file = request.FILES["csv_file"] - - if not csv_file.name.endswith(".csv"): - message = "Please Submit a csv file " - context = { - "message":message, - } - return render(request, "../templates/examination/messageDean.html", context) - - course_id = request.POST.get("course") - academic_year = request.POST.get("year") - # semester = request.POST.get('semester') - print(academic_year) - if academic_year is None or not academic_year.isdigit(): - message = "Academic Year must be a number" - context = { - "message":message, - } - return render(request, "../templates/examination/messageDean.html", context) - - if not course_id or not academic_year: - message = "Course and Academic year are required" - context = { - "message":message, - } - return render(request, "../templates/examination/messageDean.html", context) - - # courses_info = Courses.objects.get(id=course_id) - - # courses = Student_grades.objects.filter( - # course_id=courses_info.id, year=academic_year - # ) - students = course_registration.objects.filter( - course_id_id=course_id, working_year=academic_year - ) - - - - try: - # Parse the CSV file - decoded_file = csv_file.read().decode("utf-8").splitlines() - reader = csv.DictReader(decoded_file) - - required_columns = ["roll_no", "grade", "remarks"] - if not all(column in reader.fieldnames for column in required_columns): - message = "CSV file must contain the following columns: roll_no, grade, remarks." - context = { - "message":message, - } - return render(request, "../templates/examination/messageDean.html", context) - - - mismatch=[] - for row in reader: - roll_no = row["roll_no"] - grade = row["grade"] - remarks = row["remarks"] - stud=Student.objects.get(id_id=roll_no) - semester=stud.curr_semester_no - batch=stud.batch - Student_grades.objects.filter( - roll_no=roll_no, - course_id_id=course_id, - year=academic_year, - batch=batch, - ) - student_grade = Student_grades.objects.get( - roll_no=roll_no, - course_id_id=course_id, - year=academic_year, - batch=batch - ) - if student_grade.grade != grade: - mismatch.append({ - "roll_no": roll_no, - "csv_grade": grade, - "db_grade": student_grade.grade, - "remarks": remarks, - "batch": batch, - "semester": semester, - "course_id": course_id, - }) - if not mismatch: - message = "There Are no Mismatches" - context = { - "message":message, - } - return render(request, "../templates/examination/messageDean.html", context) - context = { - "mismatch": mismatch, - } - return render(request, "../templates/examination/validationSubmit.html", context) - - except Exception as e: - - error_message = f"An error occurred while processing the file: {str(e)}" - context = { - "message": error_message, - } - return render(request, "../templates/examination/messageDean.html", context) - -@login_required(login_url="/accounts/login") -def downloadGrades(request): - des = request.session.get("currentDesignationSelected") - if str(des) == "Associate Professor" or str(des) == "Professor" or str(des) == "Assistant Professor" : - pass - else: - if request.is_ajax(): # For AJAX or JSON requests - return JsonResponse({"success": False, "error": "Access denied."}, status=403) - else: # For non-AJAX requests - return HttpResponseRedirect('/dashboard/') - academic_year = request.GET.get('academic_year') - - if academic_year: - if academic_year is None or not academic_year.isdigit(): - return JsonResponse({}) - # print(request.user,1) - unique_course_ids = ( - CourseInstructor.objects.filter(instructor_id_id=request.user.username) - .values("course_id_id") - .distinct() - ) - # unique_course_ids = course_registration.objects.values( - # 'course_id').distinct() - - # Cast the course IDs to integers - unique_course_ids = unique_course_ids.annotate( - course_id_int=Cast("course_id", IntegerField()) - ) - # Retrieve course names and course codes based on unique course IDs - - courses_info = Student_grades.objects.filter( - year=academic_year, - course_id_id__in=unique_course_ids.values_list("course_id_int", flat=True) - ) - courses_details=Courses.objects.filter( - id__in=courses_info.values_list("course_id_id", flat=True) - ) - # print(courses_info.values(),'abcd') - return JsonResponse({"courses": list(courses_details.values())}) - - - working_years = course_registration.objects.values("working_year").distinct() - - context = {"working_years": working_years} - - return render(request, "../templates/examination/download_resultProf.html", context) - - -@login_required(login_url="/accounts/login") -def generate_pdf(request): - des = request.session.get("currentDesignationSelected") - if str(des) == "Associate Professor" or str(des) == "Professor" or str(des) == "Assistant Professor" : - pass - else: - if request.is_ajax(): # For AJAX or JSON requests - return JsonResponse({"success": False, "error": "Access denied."}, status=403) - else: # For non-AJAX requests - return HttpResponseRedirect('/dashboard/') - course_id = request.POST.get('course_id') - academic_year = request.POST.get('academic_year') - course_info = get_object_or_404(Courses, id=course_id) - grades = Student_grades.objects.filter(course_id_id=course_id, year=academic_year).order_by("roll_no") - course=CourseInstructor.objects.filter(course_id_id=course_id,year=academic_year,instructor_id_id=request.user.username) - if not course: - return JsonResponse({"success": False, "error": "course not found."}, status=404) - semester=course.first().semester_no - - all_grades = ["O", "A+", "A", "B+", "B", "C+", "C", "D+", "D", "F", "I", "S", "X"] - grade_counts = {grade: grades.filter(grade=grade).count() for grade in all_grades} - - response = HttpResponse(content_type="application/pdf") - response["Content-Disposition"] = f'attachment; filename="{course_info.code}_grades.pdf"' - - doc = SimpleDocTemplate(response, pagesize=letter) - elements = [] - styles = getSampleStyleSheet() - - # Custom Header Style - header_style = ParagraphStyle( - "HeaderStyle", - parent=styles["Heading1"], - fontName="Helvetica-Bold", - fontSize=16, - textColor=HexColor("#333333"), - spaceAfter=20, - alignment=1, # Center alignment - ) - subheader_style = ParagraphStyle( - "SubheaderStyle", - parent=styles["Normal"], - fontSize=12, - textColor=HexColor("#666666"), - spaceAfter=10, - ) - instructor = request.user.first_name + " " + request.user.last_name - - # Add Header - elements.append(Paragraph(f"Grade Sheet", header_style)) - field_label_style = ParagraphStyle( - "FieldLabelStyle", - parent=styles["Normal"], - fontSize=12, - textColor=colors.black, # Black text color for labels - spaceAfter=5, -) - field_value_style = ParagraphStyle( - "FieldValueStyle", - parent=styles["Normal"], - fontSize=12, - textColor=HexColor("#666666"), # Gray text color for values - spaceAfter=10, -) - -# Add fields with labels in black and values in gray - elements.append(Paragraph(f"Session: {academic_year}", field_label_style)) - elements.append(Paragraph(f"Semester: {semester}", field_label_style)) - elements.append(Paragraph(f"Course Code: {course_info.code}", field_label_style)) - elements.append(Paragraph(f"Course Name: {course_info.name}", field_label_style)) - elements.append(Paragraph(f"Instructor: {instructor}", field_label_style)) - - # Table Data with Wider Column Widths - data = [["S.No.", "Roll Number", "Grade"]] - for i, grade in enumerate(grades, 1): - data.append([i, grade.roll_no, grade.grade]) - table = Table(data, colWidths=[80, 300, 100]) - - # Improved Table Style - table.setStyle( - TableStyle( - [ - ("BACKGROUND", (0, 0), (-1, 0), HexColor("#E0E0E0")), - ("TEXTCOLOR", (0, 0), (-1, 0), colors.black), - ("ALIGN", (0, 0), (-1, 0), "CENTER"), - ("FONTNAME", (0, 0), (-1, 0), "Helvetica-Bold"), - ("FONTSIZE", (0, 0), (-1, 0), 14), - ("BOTTOMPADDING", (0, 0), (-1, 0), 10), - ("BACKGROUND", (0, 1), (-1, -1), HexColor("#F9F9F9")), - ("ROWBACKGROUNDS", (0, 1), (-1, -1), [HexColor("#F9F9F9"), colors.white]), - ("TEXTCOLOR", (0, 1), (-1, -1), colors.black), - ("FONTNAME", (0, 1), (-1, -1), "Helvetica"), - ("FONTSIZE", (0, 1), (-1, -1), 12), - ("GRID", (0, 0), (-1, -1), 0.5, colors.grey), - ("ALIGN", (0, 0), (-1, -1), "CENTER"), - ("VALIGN", (0, 0), (-1, -1), "MIDDLE"), - ] - ) - ) - elements.append(table) - elements.append(Spacer(1, 20)) - - # Add Grade Distribution with Row Splitting - elements.append(Paragraph(f"Grade Distribution:", header_style)) - - # First Grade Table - grade_data1 = [["O", "A+", "A", "B+", "B", "C+", "C", "D+"]] - grade_data1.append([grade_counts[grade] for grade in grade_data1[0]]) - grade_table1 = Table(grade_data1, colWidths=[60] * 8) - grade_table1.setStyle( - TableStyle( - [ - ("BACKGROUND", (0, 0), (-1, 0), HexColor("#E0E0E0")), - ("TEXTCOLOR", (0, 0), (-1, 0), colors.black), - ("ALIGN", (0, 0), (-1, -1), "CENTER"), - ("FONTNAME", (0, 0), (-1, 0), "Helvetica-Bold"), - ("FONTSIZE", (0, 0), (-1, 0), 12), - ("BOTTOMPADDING", (0, 0), (-1, 0), 8), - ("GRID", (0, 0), (-1, -1), 0.5, colors.grey), - ("BACKGROUND", (0, 1), (-1, -1), colors.white), - ("TEXTCOLOR", (0, 1), (-1, -1), colors.black), - ] - ) - ) - elements.append(grade_table1) - elements.append(Spacer(1, 10)) - - # Second Grade Table - grade_data2 = [["D", "F", "I", "S", "X"]] - grade_data2.append([grade_counts[grade] for grade in grade_data2[0]]) - grade_table2 = Table(grade_data2, colWidths=[60] * 5) - grade_table2.setStyle( - TableStyle( - [ - ("BACKGROUND", (0, 0), (-1, 0), HexColor("#E0E0E0")), - ("TEXTCOLOR", (0, 0), (-1, 0), colors.black), - ("ALIGN", (0, 0), (-1, -1), "CENTER"), - ("FONTNAME", (0, 0), (-1, 0), "Helvetica-Bold"), - ("FONTSIZE", (0, 0), (-1, 0), 12), - ("BOTTOMPADDING", (0, 0), (-1, 0), 8), - ("GRID", (0, 0), (-1, -1), 0.5, colors.grey), - ("BACKGROUND", (0, 1), (-1, -1), colors.white), - ("TEXTCOLOR", (0, 1), (-1, -1), colors.black), - ] - ) - ) - elements.append(grade_table2) - elements.append(Spacer(1, 40)) - - verified_style = ParagraphStyle( - "VerifiedStyle", - parent=styles["Normal"], - fontSize=13, - textColor=HexColor("#333333"), - alignment=0, # Center alignment - spaceAfter=20, - ) - elements.append(Paragraph("I have carefully checked and verified the submitted grade. The grade distribution and submitted grades are correct. [Please mention any exception below.]", verified_style)) - - # Footer Signatures - def draw_signatures(canvas, doc): - canvas.saveState() - width, height = letter - canvas.drawString(inch, 0.75 * inch, "") - canvas.drawString(inch, 0.5 * inch, "Date") - canvas.drawString(width - 4 * inch, 0.75 * inch, "") - canvas.drawString(width - 4 * inch, 0.5 * inch, "Course Instructor's Signature") - canvas.restoreState() - - doc.build(elements, onLaterPages=draw_signatures, onFirstPage=draw_signatures) - return response - - -@login_required(login_url="/accounts/login") -def generate_result(request): - if request.method == 'POST': - des = request.session.get("currentDesignationSelected") - if des == "acadadmin": - pass - else: - if request.is_ajax(): # For AJAX or JSON requests - return JsonResponse({"success": False, "error": "Access denied."}, status=403) - else: # For non-AJAX requests - return HttpResponseRedirect('/dashboard/') - try: - data = json.loads(request.body) - semester = data.get('semester') - branch = data.get('specialization') - batch = data.get('batch') - - branch_info = Discipline.objects.filter(acronym=branch).first() - if not branch_info: - return JsonResponse({'error': 'Branch not found'}, status=404) - - curriculum_id = Batch.objects.filter( - year=batch, discipline_id=branch_info.id - ).values_list('curriculum_id', flat=True).first() - if not curriculum_id: - return JsonResponse({'error': 'Curriculum not found'}, status=404) - - semester_info = Semester.objects.filter( - curriculum_id=curriculum_id, semester_no=semester - ).first() - if not semester_info: - return JsonResponse({'error': 'Semester not found'}, status=404) - # print(batch, branch) - course_slots = CourseSlot.objects.filter(semester_id=semester_info) - course_ids_from_slots = course_slots.values_list('courses', flat=True) - course_ids_from_grades = Student_grades.objects.filter( - batch=batch, - semester=semester - ).values_list('course_id_id', flat=True) - course_ids = set(course_ids_from_slots).union(set(course_ids_from_grades)) - courses = Courses.objects.filter(id__in=course_ids) - courses_map={} - for course in courses: - courses_map[course.id]=(course.credit) - students = Student.objects.filter(batch=batch, specialization=branch).order_by('id') - # print(students.first().id_id,"studejt id") - - wb = Workbook() - ws = wb.active - ws.title = "Student Grades" - - - # ws.merge_cells(start_row=1, start_column=1, end_row=4, end_column=1) - # ws.merge_cells(start_row=1, start_column=2, end_row=4, end_column=2) - ws["A1"] = "S. No" - ws["B1"] = "Roll No" - for cell in ("A1", "B1"): - ws[cell].alignment = Alignment(horizontal="center", vertical="center") - ws[cell].font = Font(bold=True) - - - ws.column_dimensions[get_column_letter(1)].width = 12 - ws.column_dimensions[get_column_letter(2)].width = 18 - col_idx = 3 - for course in courses: - - ws.merge_cells(start_row=1, start_column=col_idx, end_row=1, end_column=col_idx + 1) - ws.merge_cells(start_row=2, start_column=col_idx, end_row=2, end_column=col_idx + 1) - ws.merge_cells(start_row=3, start_column=col_idx, end_row=3, end_column=col_idx + 1) - - ws.cell(row=1, column=col_idx).value = course.code - ws.cell(row=1, column=col_idx).alignment = Alignment(horizontal="center", vertical="center") - ws.cell(row=1, column=col_idx).font = Font(bold=True) - ws.cell(row=2, column=col_idx).value = course.name - ws.cell(row=2, column=col_idx).alignment = Alignment(horizontal="center", vertical="center") - ws.cell(row=2, column=col_idx).font = Font(bold=True) - - ws.cell(row=3, column=col_idx).value=course.credit - ws.cell(row=3, column=col_idx).alignment = Alignment(horizontal="center", vertical="center") - ws.cell(row=3, column=col_idx).font = Font(bold=True) - ws.cell(row=4, column=col_idx).value = "Grade" - ws.cell(row=4, column=col_idx + 1).value = "Remarks" - ws.cell(row=4, column=col_idx).alignment = Alignment(horizontal="center", vertical="center") - ws.cell(row=4, column=col_idx+1).alignment = Alignment(horizontal="center", vertical="center") - ws.column_dimensions[get_column_letter(col_idx)].width = 25 - ws.column_dimensions[get_column_letter(col_idx+1)].width = 25 - col_idx += 2 - - # ws.merge_cells(start_row=1, start_column=col_idx, end_row=4, end_column=col_idx) # SPI - ws.cell(row=1, column=col_idx).value = "SPI" - ws.cell(row=1, column=col_idx).alignment = Alignment(horizontal="center", vertical="center") - ws.cell(row=1, column=col_idx).font = Font(bold=True) - - # ws.merge_cells(start_row=1, start_column=col_idx + 1, end_row=4, end_column=col_idx + 1) # CPI - ws.cell(row=1, column=col_idx + 1).value = "CPI" - ws.cell(row=1, column=col_idx + 1).alignment = Alignment(horizontal="center", vertical="center") - ws.cell(row=1, column=col_idx + 1).font = Font(bold=True) - - - row_idx = 5 - for idx, student in enumerate(students, start=1): - ws.cell(row=row_idx, column=1).value = idx - ws.cell(row=row_idx, column=2).value = student.id_id - - ws.cell(row=row_idx, column=1).alignment = Alignment(horizontal="center", vertical="center") - ws.cell(row=row_idx, column=2).alignment = Alignment(horizontal="center", vertical="center") - student_grades = Student_grades.objects.filter( - roll_no=student.id_id, course_id_id__in=course_ids, semester=semester - ) - - grades_map = {} - for grade in student_grades: - grades_map[grade.course_id_id] = (grade.grade, grade.remarks,courses_map.get(grade.course_id_id) ) - - col_idx = 3 - gained_credit=0 - total_credit=0 - for course in courses: - grade, remark, credits = grades_map.get(course.id, ("N/A", "N/A",0)) - ws.cell(row=row_idx, column=col_idx).value = grade - ws.cell(row=row_idx, column=col_idx + 1).value = remark - ws.cell(row=row_idx, column=col_idx).alignment = Alignment(horizontal="center", vertical="center") - ws.cell(row=row_idx, column=col_idx+1).alignment = Alignment(horizontal="center", vertical="center") - if grade=="O" or grade=="A+": - gained_credit+=1*credits - total_credit+=credits - elif grade=="A": - gained_credit+=0.9*credits - total_credit+=credits - elif grade=="B+": - gained_credit+=0.8*credits - total_credit+=credits - elif grade=="B": - gained_credit+=0.7*credits - total_credit+=credits - elif grade=="C+": - gained_credit+=0.6*credits - total_credit+=credits - elif grade=="C": - gained_credit+=0.5*credits - total_credit+=credits - elif grade=="D+": - gained_credit+=0.4*credits - total_credit+=credits - elif grade=="D": - gained_credit+=0.3*credits - total_credit+=credits - elif grade=="F": - gained_credit+=0.2*credits - total_credit+=credits - - - col_idx += 2 - if total_credit==0 : - ws.cell(row=row_idx, column=col_idx).value =0 - else: - ws.cell(row=row_idx, column=col_idx).value = 10*(gained_credit/total_credit) - ws.cell(row=row_idx, column=col_idx + 1).value = 0 - ws.cell(row=row_idx, column=col_idx).alignment = Alignment(horizontal="center", vertical="center") - ws.cell(row=row_idx, column=col_idx+1).alignment = Alignment(horizontal="center", vertical="center") - - row_idx += 1 - - response = HttpResponse(content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') - response['Content-Disposition'] = 'attachment; filename="student_grades.xlsx"' - wb.save(response) - return response - - except json.JSONDecodeError: - return JsonResponse({'error': 'Invalid JSON data'}, status=400) - except Exception as e: - traceback.print_exc() - return JsonResponse({'error': str(e)}, status=500) - - - return JsonResponse({'error': 'Invalid request method'}, status=405) - -def checkresult(request): - des = request.session.get("currentDesignationSelected") - if des == "student": - pass - else: - if request.is_ajax(): # For AJAX or JSON requests - return JsonResponse({"success": False, "error": "Access denied."}, status=403) - else: # For non-AJAX requests - return HttpResponseRedirect('/dashboard/') - return render(request, "../templates/examination/check_result.html") - - -def grades_report(request): - if request.method == 'POST': - des = request.session.get("currentDesignationSelected") - if des == "student": - pass - else: - if request.is_ajax(): # For AJAX or JSON requests - return JsonResponse({"success": False, "error": "Access denied."}, status=403) - else: # For non-AJAX requests - return HttpResponseRedirect('/dashboard/') - roll_number=request.user.username - semester=request.POST.get('semester') - grades_info=Student_grades.objects.filter(roll_no=roll_number,semester=semester).select_related('course_id') - gained_credit=0 - total_credit=0 - all_credits=0 - for grades in grades_info: - credits=grades.course_id.credit - grade=grades.grade - if grade=="O" or grade=="A+": - gained_credit+=1*credits - total_credit+=credits - elif grade=="A": - gained_credit+=0.9*credits - total_credit+=credits - elif grade=="B+": - gained_credit+=0.8*credits - total_credit+=credits - elif grade=="B": - gained_credit+=0.7*credits - total_credit+=credits - elif grade=="C+": - gained_credit+=0.6*credits - total_credit+=credits - elif grade=="C": - gained_credit+=0.5*credits - total_credit+=credits - elif grade=="D+": - gained_credit+=0.4*credits - total_credit+=credits - elif grade=="D": - gained_credit+=0.3*credits - total_credit+=credits - elif grade=="F": - gained_credit+=0.2*credits - total_credit+=credits - all_credits+=credits - spi = 10*(gained_credit/total_credit) if total_credit > 0 else 0 - all_grades = Student_grades.objects.filter(roll_no=roll_number) - total_units = sum(grade.course_id.credit for grade in all_grades) - student_user = request.user.first_name+' '+request.user.last_name - student = Student.objects.filter(id=roll_number).first() - # count=Student.objects.filter(id=roll.id).count() - # student=students.count() - - context = { - 'student': student, - 'student_user': student_user, - 'grades': grades_info, - 'spi': round(spi, 2), - 'semester_units': all_credits, - 'total_units': total_units, - } - return render(request, '../templates/examination/grades_report.html', context) - - diff --git a/FusionIIIT/applications/feeds/migrations/0001_initial.py b/FusionIIIT/applications/feeds/migrations/0001_initial.py index 7b794eac6..30ae94c92 100644 --- a/FusionIIIT/applications/feeds/migrations/0001_initial.py +++ b/FusionIIIT/applications/feeds/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 3.1.5 on 2024-07-16 15:44 +# Generated by Django 3.1.5 on 2023-03-15 18:53 from django.conf import settings from django.db import migrations, models diff --git a/FusionIIIT/applications/feeds/migrations/0002_data_integrity_fixes.py b/FusionIIIT/applications/feeds/migrations/0002_data_integrity_fixes.py new file mode 100644 index 000000000..8dad87de7 --- /dev/null +++ b/FusionIIIT/applications/feeds/migrations/0002_data_integrity_fixes.py @@ -0,0 +1,192 @@ +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +def dedupe_reports(apps, schema_editor): + Report = apps.get_model('feeds', 'report') + seen = set() + duplicate_ids = [] + + for report in Report.objects.order_by('id').iterator(): + key = (report.user_id, report.question_id) + if key in seen: + duplicate_ids.append(report.id) + else: + seen.add(key) + + if duplicate_ids: + Report.objects.filter(id__in=duplicate_ids).delete() + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('feeds', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='answeraquestion', + name='answers', + field=models.ManyToManyField(blank=True, related_name='answers', to=settings.AUTH_USER_MODEL), + ), + migrations.AlterField( + model_name='answeraquestion', + name='dislikes', + field=models.ManyToManyField(blank=True, related_name='answer_dislikes', to=settings.AUTH_USER_MODEL), + ), + migrations.AlterField( + model_name='answeraquestion', + name='likes', + field=models.ManyToManyField(blank=True, related_name='answer_likes', to=settings.AUTH_USER_MODEL), + ), + migrations.AlterField( + model_name='answeraquestion', + name='question', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='feeds.askaquestion'), + ), + migrations.AlterField( + model_name='answeraquestion', + name='user', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + ), + migrations.AlterField( + model_name='askaquestion', + name='dislikes', + field=models.ManyToManyField(blank=True, related_name='dislikes', to=settings.AUTH_USER_MODEL), + ), + migrations.AlterField( + model_name='askaquestion', + name='likes', + field=models.ManyToManyField(blank=True, related_name='likes', to=settings.AUTH_USER_MODEL), + ), + migrations.AlterField( + model_name='askaquestion', + name='requests', + field=models.ManyToManyField(blank=True, related_name='requests', to=settings.AUTH_USER_MODEL), + ), + migrations.AlterField( + model_name='askaquestion', + name='user', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + ), + migrations.AlterField( + model_name='comments', + name='likes_comment', + field=models.ManyToManyField(blank=True, related_name='likes_comment', to=settings.AUTH_USER_MODEL), + ), + migrations.AlterField( + model_name='comments', + name='question', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='feeds.askaquestion'), + ), + migrations.AlterField( + model_name='comments', + name='user', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + ), + migrations.AlterField( + model_name='hidden', + name='question', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='feeds.askaquestion'), + ), + migrations.AlterField( + model_name='hidden', + name='user', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + ), + migrations.AlterField( + model_name='profile', + name='user', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + ), + migrations.AlterField( + model_name='questionaccesscontrol', + name='posted_by', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='feeds.roles'), + ), + migrations.AlterField( + model_name='questionaccesscontrol', + name='question', + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name='question_list', + to='feeds.askaquestion', + ), + ), + migrations.AlterField( + model_name='reply', + name='comment', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='feeds.comments'), + ), + migrations.AlterField( + model_name='reply', + name='replies', + field=models.ManyToManyField(blank=True, related_name='replies', to=settings.AUTH_USER_MODEL), + ), + migrations.AlterField( + model_name='reply', + name='user', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + ), + migrations.AlterField( + model_name='roles', + name='user', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + ), + migrations.AlterField( + model_name='report', + name='question', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='feeds.askaquestion'), + ), + migrations.AlterField( + model_name='report', + name='user', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + ), + migrations.AlterField( + model_name='tags', + name='my_subtag', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='feeds.alltags'), + ), + migrations.AlterField( + model_name='tags', + name='my_tag', + field=models.CharField( + choices=[ + ('CSE', 'CSE'), + ('ECE', 'ECE'), + ('Mechanical', 'Mechanical'), + ('Technical-Clubs', 'Technical Clubs'), + ('Cultural-Clubs', 'Cultural Clubs'), + ('Sports-Clubs', 'Sports Clubs'), + ('Business-and-Career', 'Business and Career'), + ('Entertainment', 'Entertainment'), + ('IIITDMJ-Campus', 'IIITDMJ Campus'), + ('Jabalpur-city', 'Jabalpur city'), + ('IIITDMJ-Rules-and-Regulations', 'IIITDMJ rules and regulations'), + ('Academics', 'Academics'), + ('IIITDMJ', 'IIITDMJ'), + ('Life-Relationship-and-Self', 'Life Relationship and Self'), + ('Technology-and-Education', 'Technology and Education'), + ('Programmes', 'Programmes'), + ('Others', 'Others'), + ('Design', 'Design'), + ], + default='CSE', + max_length=100, + ), + ), + migrations.AlterField( + model_name='tags', + name='user', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + ), + migrations.RunPython(dedupe_reports, migrations.RunPython.noop), + migrations.AlterUniqueTogether( + name='report', + unique_together={('user', 'question')}, + ), + ] diff --git a/FusionIIIT/applications/feeds/models.py b/FusionIIIT/applications/feeds/models.py index 818c989f5..3948c043e 100644 --- a/FusionIIIT/applications/feeds/models.py +++ b/FusionIIIT/applications/feeds/models.py @@ -1,4 +1,6 @@ from django.contrib.auth.models import Permission, User +from django.db.models.signals import m2m_changed +from django.dispatch import receiver from django.db import models from django.utils import timezone from django.utils.safestring import mark_safe @@ -174,15 +176,15 @@ class AskaQuestion(models.Model): """ can_delete=models.BooleanField(default=False) can_update=models.BooleanField(default=False) - user = models.ForeignKey(User, default=1,on_delete=models.CASCADE) + user = models.ForeignKey(User, on_delete=models.CASCADE) subject = models.CharField(max_length=100, blank=False) description = models.CharField(max_length=500, null=True, blank=True, default="") select_tag = models.ManyToManyField(AllTags) file = models.FileField(null=True, blank=True,upload_to='feeds/files') uploaded_at = models.DateTimeField(auto_now_add=False, auto_now=False, default=timezone.now) - likes = models.ManyToManyField(User, default=1,related_name='likes', blank=True) - dislikes = models.ManyToManyField(User, default=1,related_name='dislikes', blank=True) - requests = models.ManyToManyField(User, default=1,related_name='requests', blank=True) + likes = models.ManyToManyField(User, related_name='likes', blank=True) + dislikes = models.ManyToManyField(User, related_name='dislikes', blank=True) + requests = models.ManyToManyField(User, related_name='requests', blank=True) is_liked=models.BooleanField(default=False) is_requested=models.BooleanField(default=False) request = models.IntegerField(default=0) @@ -215,14 +217,14 @@ class AnsweraQuestion(models.Model): """ Records answers on a question """ - user = models.ForeignKey(User, default=1, on_delete=models.CASCADE) + user = models.ForeignKey(User, on_delete=models.CASCADE) content = models.TextField(max_length=1000, blank=False) - question = models.ForeignKey(AskaQuestion, default=1, on_delete=models.CASCADE) + question = models.ForeignKey(AskaQuestion, on_delete=models.CASCADE) uploaded_at = models.DateTimeField(auto_now=False, auto_now_add=False, default=timezone.now) - answers = models.ManyToManyField(User, default=1, related_name='answers', blank=True) + answers = models.ManyToManyField(User, related_name='answers', blank=True) total_answers = models.IntegerField(default=0) - likes = models.ManyToManyField(User, default=1,related_name='answer_likes', blank=True) - dislikes = models.ManyToManyField(User, default=1,related_name='answer_dislikes', blank=True) + likes = models.ManyToManyField(User, related_name='answer_likes', blank=True) + dislikes = models.ManyToManyField(User, related_name='answer_dislikes', blank=True) is_liked=models.BooleanField(default=False) def total_likes(self): @@ -253,10 +255,10 @@ class Comments(models.Model): """ Records comments on posts """ - user = models.ForeignKey(User, default=1, on_delete=models.CASCADE) - question = models.ForeignKey(AskaQuestion, default=1 , on_delete = models.CASCADE) + user = models.ForeignKey(User, on_delete=models.CASCADE) + question = models.ForeignKey(AskaQuestion, on_delete = models.CASCADE) comment_text = models.CharField(max_length=5000) - likes_comment = models.ManyToManyField(User, default=1,related_name='likes_comment', blank=True) + likes_comment = models.ManyToManyField(User, related_name='likes_comment', blank=True) total_likes_comment = models.IntegerField(default=0) commented_at = models.DateTimeField(auto_now_add=False, auto_now=False, default=timezone.now) is_liked = models.BooleanField(default=False) @@ -271,11 +273,11 @@ class Reply(models.Model): """ Records replies on a post """ - user = models.ForeignKey(User, default=1, on_delete=models.CASCADE) - comment = models.ForeignKey(Comments,default=1, on_delete=models.CASCADE) + user = models.ForeignKey(User, on_delete=models.CASCADE) + comment = models.ForeignKey(Comments, on_delete=models.CASCADE) msg = models.CharField(max_length=1000) content = models.CharField(max_length=5000, default="") - replies = models.ManyToManyField(User, default=1,related_name='replies', blank=True) + replies = models.ManyToManyField(User, related_name='replies', blank=True) total_replies = models.IntegerField(default=0) replied_at = models.DateTimeField(auto_now_add=False, auto_now=False, default=timezone.now) @@ -289,19 +291,19 @@ class report(models.Model): """ records report on a quesiton """ - user = models.ForeignKey(User, default=1, on_delete=models.CASCADE) - question = models.ForeignKey(AskaQuestion, default=1, on_delete = models.CASCADE) + user = models.ForeignKey(User, on_delete=models.CASCADE) + question = models.ForeignKey(AskaQuestion, on_delete = models.CASCADE) report_msg = models.CharField(max_length=1000,default="") - # class Meta: - # unique_together = ('user', 'question') + class Meta: + unique_together = ('user', 'question') class hidden(models.Model): """ records a hidden question """ - user = models.ForeignKey(User, default=1, on_delete=models.CASCADE) - question = models.ForeignKey(AskaQuestion, default=1, on_delete=models.CASCADE) + user = models.ForeignKey(User, on_delete=models.CASCADE) + question = models.ForeignKey(AskaQuestion, on_delete=models.CASCADE) # hide = models.BooleanField(default=False) class Meta: @@ -315,9 +317,9 @@ class tags(models.Model): records tags for a user """ - user = models.ForeignKey(User, default=1, on_delete=models.CASCADE) - my_tag = models.CharField(max_length=100, default=1, choices=Constants.TAG_LIST) - my_subtag = models.ForeignKey(AllTags, default=1, on_delete = models.CASCADE) + user = models.ForeignKey(User, on_delete=models.CASCADE) + my_tag = models.CharField(max_length=100, default='CSE', choices=Constants.TAG_LIST) + my_subtag = models.ForeignKey(AllTags, on_delete = models.CASCADE) class Meta: unique_together = ('user', 'my_subtag') @@ -329,7 +331,7 @@ class Profile(models.Model): """ records profile details of a user """ - user = models.ForeignKey(User, default=1, on_delete=models.CASCADE) + user = models.ForeignKey(User, on_delete=models.CASCADE) bio = models.CharField(max_length=250,blank=True) profile_picture = models.ImageField(null=True, blank=True, upload_to='feeds/profile_pictures') profile_view = models.IntegerField(default=0) @@ -340,7 +342,7 @@ class Roles(models.Model): """ Records role a for a user """ - user = models.ForeignKey(User, default=1, on_delete=models.CASCADE) + user = models.ForeignKey(User, on_delete=models.CASCADE) role = models.CharField(max_length=100, blank=False) active = models.BooleanField(default=True) def __str__(self): @@ -350,11 +352,46 @@ class QuestionAccessControl(models.Model): """ records the different access permissions like the user can comment on a question or not, can vot or not for a user """ - question = models.ForeignKey(AskaQuestion, related_name='question_list', default=1, on_delete=models.CASCADE) + question = models.ForeignKey(AskaQuestion, related_name='question_list', on_delete=models.CASCADE) canVote = models.BooleanField() canAnswer = models.BooleanField() canComment = models.BooleanField() - posted_by = models.ForeignKey(Roles, default=1, on_delete=models.CASCADE) + posted_by = models.ForeignKey(Roles, on_delete=models.CASCADE) created_at = models.DateTimeField(auto_now_add=False, auto_now=False, default=timezone.now) def __str__(self): return "question number " + str(self.question.id) + + +def _sync_count(instance, field_name, count_field): + setattr(instance, count_field, getattr(instance, field_name).count()) + instance.save(update_fields=[count_field]) + + +@receiver(m2m_changed, sender=AskaQuestion.likes.through) +def sync_question_likes(sender, instance, action, **kwargs): + if action in ('post_add', 'post_remove', 'post_clear'): + _sync_count(instance, 'likes', 'total_likes') + + +@receiver(m2m_changed, sender=AskaQuestion.dislikes.through) +def sync_question_dislikes(sender, instance, action, **kwargs): + if action in ('post_add', 'post_remove', 'post_clear'): + _sync_count(instance, 'dislikes', 'total_dislikes') + + +@receiver(m2m_changed, sender=AnsweraQuestion.answers.through) +def sync_answer_count(sender, instance, action, **kwargs): + if action in ('post_add', 'post_remove', 'post_clear'): + _sync_count(instance, 'answers', 'total_answers') + + +@receiver(m2m_changed, sender=Comments.likes_comment.through) +def sync_comment_likes(sender, instance, action, **kwargs): + if action in ('post_add', 'post_remove', 'post_clear'): + _sync_count(instance, 'likes_comment', 'total_likes_comment') + + +@receiver(m2m_changed, sender=Reply.replies.through) +def sync_reply_count(sender, instance, action, **kwargs): + if action in ('post_add', 'post_remove', 'post_clear'): + _sync_count(instance, 'replies', 'total_replies') diff --git a/FusionIIIT/applications/feeds/views.py b/FusionIIIT/applications/feeds/views.py index fea8f5f42..4ef9c62c9 100644 --- a/FusionIIIT/applications/feeds/views.py +++ b/FusionIIIT/applications/feeds/views.py @@ -689,7 +689,6 @@ def profile(request, string): string- user's username @variables """ - print("user is this", request) if request.method == "POST": profile = Profile.objects.select_related().all().filter(user=request.user) Pr = None diff --git a/FusionIIIT/applications/filetracking/admin.py b/FusionIIIT/applications/filetracking/admin.py index af7ed0d4b..82b78df95 100644 --- a/FusionIIIT/applications/filetracking/admin.py +++ b/FusionIIIT/applications/filetracking/admin.py @@ -1,23 +1,7 @@ from django.contrib import admin -from .models import File, Tracking +# Register your models here. +from applications.filetracking.models import File, Tracking -class FileAdmin(admin.ModelAdmin): - list_display = ('id', 'uploader', 'designation', - 'subject', 'upload_date', 'is_read') - search_fields = ('uploader__user__username', 'subject', 'description') - list_filter = ('is_read',) - - -admin.site.register(File, FileAdmin) - - -class TrackingAdmin(admin.ModelAdmin): - list_display = ('file_id', 'current_id', 'receiver_id', - 'receive_date', 'forward_date', 'is_read') - search_fields = ('file_id__subject', - 'current_id__user__username', 'receiver_id__username') - list_filter = ('is_read',) - - -admin.site.register(Tracking, TrackingAdmin) +admin.site.register(File) +admin.site.register(Tracking) diff --git a/FusionIIIT/applications/filetracking/api/serializers.py b/FusionIIIT/applications/filetracking/api/serializers.py deleted file mode 100644 index bdae2024e..000000000 --- a/FusionIIIT/applications/filetracking/api/serializers.py +++ /dev/null @@ -1,24 +0,0 @@ -from applications.filetracking.models import File, Tracking -from django.core.files import File as DjangoFile -from rest_framework import serializers - - -class FileSerializer(serializers.ModelSerializer): - class Meta: - model = File - fields = '__all__' - - -class TrackingSerializer(serializers.ModelSerializer): - class Meta: - model = Tracking - fields = '__all__' - - -class FileHeaderSerializer(serializers.ModelSerializer): - ''' - This serializes everything except the attachments of a file and whether it is read or not - ''' - class Meta: - model = File - exclude = ['upload_file', 'is_read'] diff --git a/FusionIIIT/applications/filetracking/api/urls.py b/FusionIIIT/applications/filetracking/api/urls.py deleted file mode 100644 index 0d8ecad38..000000000 --- a/FusionIIIT/applications/filetracking/api/urls.py +++ /dev/null @@ -1,28 +0,0 @@ -from django.conf.urls import url -from .views import ( - CreateFileView, - ViewFileView, - ViewInboxView, - ViewOutboxView, - ViewHistoryView, - ForwardFileView, - DraftFileView, - CreateDraftFile, - GetDesignationsView, - CreateArchiveFile, - ArchiveFileView -) - -urlpatterns = [ - url(r'^file/$', CreateFileView.as_view(), name='create_file'), - url(r'^file/(?P\d+)/$', ViewFileView.as_view(), name='view_file'), - url(r'^inbox/$', ViewInboxView.as_view(), name='view_inbox'), - url(r'^outbox/$', ViewOutboxView.as_view(), name='view_outbox'), - url(r'^history/(?P\d+)/$', ViewHistoryView.as_view(), name='view_history'), - url(r'^forwardfile/(?P\d+)/$', ForwardFileView.as_view(), name='forward_file'), - url(r'^draft/$', DraftFileView.as_view(), name='view_drafts'), - url(r'^createdraft/$', CreateDraftFile.as_view(), name='create_draft'), - url(r'^createarchive/$', CreateArchiveFile.as_view(), name='archive_file'), - url(r'^archive/$', ArchiveFileView.as_view(), name='view_archived'), - url(r'^designations/(?P\w+)/$', GetDesignationsView.as_view(), name='get_designations'), -] diff --git a/FusionIIIT/applications/filetracking/api/views.py b/FusionIIIT/applications/filetracking/api/views.py deleted file mode 100644 index 53daaf425..000000000 --- a/FusionIIIT/applications/filetracking/api/views.py +++ /dev/null @@ -1,292 +0,0 @@ -import logging -from venv import logger -from django.forms import ValidationError -from django.contrib.auth.models import User -from rest_framework.views import APIView -from rest_framework.response import Response -from rest_framework import status, permissions -from rest_framework.authentication import TokenAuthentication -from ..models import File, Tracking -from applications.globals.models import Designation -from ..sdk.methods import create_draft, create_file, view_drafts, view_file, delete_file, view_inbox, view_outbox, view_history, forward_file, get_designations, archive_file, view_archived - -class CreateFileView(APIView): - authentication_classes = [TokenAuthentication] - permission_classes = [permissions.IsAuthenticated] - - def post(self, request): - try: - current_user = request.user.username - current_designation = request.data.get('designation') - receiver_username = request.data.get('receiver_username') - receiver_designation = request.data.get('receiver_designation') - subject = request.data.get('subject') - description = request.data.get('description') - - uploaded_file = request.FILES.get('file') # Get the file if provided - - if None in [current_designation, receiver_username, receiver_designation, subject, description]: - return Response({'error': 'One or more required fields are missing.'}, status=status.HTTP_400_BAD_REQUEST) - - file_id = create_file(uploader=current_user, uploader_designation=current_designation, - receiver=receiver_username, receiver_designation=receiver_designation, subject=subject, description=description, attached_file=uploaded_file) - - return Response({'file_id': file_id}, status=status.HTTP_201_CREATED) - except Exception as e: - return Response({'error': str(e)}, status=status.HTTP_400_BAD_REQUEST) - - -class ViewFileView(APIView): - authentication_classes = [TokenAuthentication] - permission_classes = [permissions.IsAuthenticated] - - def get(self, request, file_id): - try: - file_details = view_file(int(file_id)) - # print(file_details) - return Response(file_details, status=status.HTTP_200_OK) - except ValueError: - return Response({'error': 'Invalid file ID format.'}, status=status.HTTP_400_BAD_REQUEST) - except File.DoesNotExist: - return Response({'error': 'File not found.'}, status=status.HTTP_404_NOT_FOUND) - except Exception as e: - return Response({'error': str(e)}, status=status.HTTP_400_BAD_REQUEST) - - def delete(self, request, file_id): - try: - # file_details = view_file(int(file_id)) - # print(file_details) - success = delete_file(int(file_id)) - if success: - return Response({'message': 'File deleted successfully'}, - status=status.HTTP_204_NO_CONTENT) - else : - return - - except ValueError: - return Response({'error': 'Invalid file ID format'}, - status=status.HTTP_400_BAD_REQUEST) - except File.DoesNotExist: - return Response({'error': 'File not found'}, - status=status.HTTP_404_NOT_FOUND) - except ValidationError as e: - return Response(e.detail, status=status.HTTP_400_BAD_REQUEST) # Handle ValidationError specifically - except Exception as e: # Catch unexpected errors - logger.error(f"Unexpected error in DeleteFileView: {e}") - return Response({'error': 'An internal server error occurred'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) - - -class ViewInboxView(APIView): - authentication_classes = [TokenAuthentication] - permission_classes = [permissions.IsAuthenticated] - - def get(self, request): - """ - API endpoint to view inbox files. - - Expects query parameters: - - username (required): User requesting the inbox. - - designation (optional): Designation to filter files by. - - src_module (required): Source module to filter files by. - - Returns: - JSON response containing a list of serialized file data, including sender information. - """ - - username = request.query_params.get('username') - designation = request.query_params.get('designation') - src_module = request.query_params.get('src_module') - - - # if not username or not src_module: - # return Response({'error': 'Missing required query parameters: username and src_module.'}, status=400) - - inbox_files = view_inbox(username, designation, src_module) - return Response(inbox_files) - -class ViewOutboxView(APIView): - authentication_classes = [TokenAuthentication] - permission_classes = [permissions.IsAuthenticated] - - - def get(self, request): - """ - API endpoint to view outbox files. - - Expects query parameters: - - username (required): User requesting the outbox. - - designation (optional): Designation to filter files by. - - src_module (required): Source module to filter files by. - - Returns: - JSON response containing a paginated list of serialized file data. - """ - - username = request.query_params.get('username') - designation = request.query_params.get('designation') - src_module = request.query_params.get('src_module') - - - if not username or not src_module: - return Response({'error': 'Missing required query parameters: username and src_module.'}, status=400) - - outbox_files = view_outbox(username, designation, src_module) - return Response(outbox_files) - - -class ViewHistoryView(APIView): - authentication_classes = [TokenAuthentication] - permission_classes = [permissions.IsAuthenticated] - - def get(self, request, file_id): - """ - View history of a particular file with the given file_id. - - Args: - request (rest_framework.request.Request): The incoming request object. - file_id (int): Primary key of the file to retrieve history for. - - Returns: - rest_framework.response.Response: JSON response containing serialized tracking history. - """ - try: - tracking_array = [] - histories = view_history(file_id) - for history in histories: - temp_obj_action = history; - temp_obj_action['receiver_id'] = User.objects.get(id=history['receiver_id']).username - temp_obj_action['receive_design'] = Designation.objects.get(id=history['receive_design']).name - tracking_array.append(temp_obj_action) - - return Response(tracking_array) - except Tracking.DoesNotExist: - return Response({'error': f'File with ID {file_id} not found.'}, status=404) - except Exception as e: - logger.error(f"An unexpected error occurred: {e}") - return Response({'error': 'Internal server error.'}, status=500) - -class ForwardFileView(APIView): -# # # Authentication and permission classes (adjust based on your needs) - authentication_classes = [TokenAuthentication] - permission_classes = [permissions.IsAuthenticated] - - def post(self, request, file_id): -# # Extract data from request.data - receiver = request.data.get('receiver') - receiver_designation = request.data.get('receiver_designation') - file_extra_JSON = request.data.get('file_extra_JSON', {}) - remarks = request.data.get('remarks', "") - - # Validate data - if not receiver or not receiver_designation: - raise ValidationError("Missing required fields: receiver and receiver_designation") - - # # Extract and validate file attachment (if present) - file_attachment = request.FILES.get('file_attachment') - if file_attachment: - if file_attachment.size > 10 * 1024 * 1024: # Adjust size limit as needed - raise ValidationError("File size exceeds limit (10 MB)") - - # Call forward_file function - try: - new_tracking_id = forward_file( - int(file_id), - receiver, - receiver_designation, - file_extra_JSON, - remarks, - file_attachment - ) - logging.info(f"Successfully forwarded file {file_id} with tracking ID: {new_tracking_id}") - except Exception as e: - logging.error(f"Error forwarding file {file_id}: {str(e)}") - raise ValidationError(str(e)) # Re-raise exception with a user-friendly message - - # Return response - return Response({'tracking_id': new_tracking_id}, status=status.HTTP_201_CREATED) - -class CreateDraftFile(APIView): - authentication_classes = [TokenAuthentication] - permission_classes = [permissions.IsAuthenticated] - - def post(self, request): - uploader = request.data.get('uploader') - uploader_designation = request.data.get('uploader_designation') - src_module = request.data.get('src_module', 'filetracking') - src_object_id = request.data.get('src_object_id', '') - file_extra_JSON = request.data.get('file_extra_JSON', {}) - attached_file = request.FILES.get('attached_file', None) - - try: - file_id = create_draft( - uploader=uploader, - uploader_designation=uploader_designation, - src_module=src_module, - src_object_id=src_object_id, - file_extra_JSON=file_extra_JSON, - attached_file=attached_file - ) - return Response({'file_id': file_id}, status=status.HTTP_201_CREATED) - except Exception as e: - return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) - -class DraftFileView(APIView): - authentication_classes = [TokenAuthentication] - permission_classes = [permissions.IsAuthenticated] - - def get(self, request): - username = request.query_params.get('username') - designation = request.query_params.get('designation') - src_module = request.query_params.get('src_module') - - try: - draft_files = view_drafts(username, designation, src_module) - print(draft_files) - return Response(draft_files, status=status.HTTP_200_OK) - except Exception as e: - return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) - -class ArchiveFileView(APIView): - authentication_classes = [TokenAuthentication] - permission_classes = [permissions.IsAuthenticated] - - def get(self, request): - username = request.query_params.get('username') - designation = request.query_params.get('designation', '') - src_module = request.query_params.get('src_module') - try: - archived_files = view_archived(username, designation, src_module) - return Response(archived_files, status=status.HTTP_200_OK) - except Exception as e: - return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) - - -class CreateArchiveFile(APIView): - authentication_classes = [TokenAuthentication] - permission_classes = [permissions.IsAuthenticated] - - def post(self, request): - file_id = request.data.get('file_id', None) - - if file_id is None: - return Response({'error': 'Missing file_id'}, status=status.HTTP_400_BAD_REQUEST) - - try: - success = archive_file(file_id) - if success: - return Response({'success': True}) - else: - return Response({'error': 'File does not exist'}, status=status.HTTP_404_NOT_FOUND) - - except Exception as e: - return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) - - - -class GetDesignationsView(APIView): - #authentication_classes = [TokenAuthentication] - #permission_classes = [permissions.IsAuthenticated] - - def get(self, request, username, *args, **kwargs): - user_designations = get_designations(username) - return Response({'designations': user_designations}) diff --git a/FusionIIIT/applications/filetracking/decorators.py b/FusionIIIT/applications/filetracking/decorators.py deleted file mode 100644 index dd900730e..000000000 --- a/FusionIIIT/applications/filetracking/decorators.py +++ /dev/null @@ -1,51 +0,0 @@ -from django.shortcuts import render, get_object_or_404 -from django.contrib.auth.models import User -from applications.globals.models import ExtraInfo, HoldsDesignation -from .sdk.methods import get_HoldsDesignation_obj - - -def user_check(request): - """ - This function is used to check if the user is a student or not. - Its return type is bool. - @param: - request - contains metadata about the requested page - - @Variables: - current_user - get user from request - user_details - extract details of the user from the database - desig_id - check for designation - student - designation for a student - final_user - final designation of the request(our user) - """ - try: - current_user = get_object_or_404(User, username=request.user.username) - user_details = ExtraInfo.objects.select_related('user','department').get(user=request.user) - des = HoldsDesignation.objects.all().select_related().filter(user=request.user).first() - if str(des.designation) == "student": - return True - else: - return False - except Exception as e: - return False - -def user_is_student(view_func): - def _wrapped_view(request, *args, **kwargs): - if user_check(request): - return render(request, 'filetracking/fileTrackingNotAllowed.html') - else: - return view_func(request, *args, **kwargs) - return _wrapped_view - -def dropdown_designation_valid(view_func): - def _wrapped_view(request, *args, **kwargs): - designation_name = request.session.get('currentDesignationSelected', 'default_value') #from dropdown - username = request.user - try: - designation_id = get_HoldsDesignation_obj( - username, designation_name).id - except: - return render(request, 'filetracking/invalid_designation.html', {'curr_des' : designation_name}) - else: - return view_func(request, *args, **kwargs) - return _wrapped_view diff --git a/FusionIIIT/applications/filetracking/migrations/0001_initial.py b/FusionIIIT/applications/filetracking/migrations/0001_initial.py index 56a210012..6924ae1ff 100644 --- a/FusionIIIT/applications/filetracking/migrations/0001_initial.py +++ b/FusionIIIT/applications/filetracking/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 3.1.5 on 2024-07-16 15:44 +# Generated by Django 3.1.5 on 2023-03-15 18:53 from django.conf import settings from django.db import migrations, models @@ -10,8 +10,8 @@ class Migration(migrations.Migration): initial = True dependencies = [ - ('globals', '0001_initial'), migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('globals', '0001_initial'), ] operations = [ @@ -24,9 +24,6 @@ class Migration(migrations.Migration): ('upload_date', models.DateTimeField(auto_now_add=True)), ('upload_file', models.FileField(blank=True, upload_to='')), ('is_read', models.BooleanField(default=False)), - ('src_module', models.CharField(default='filetracking', max_length=100)), - ('src_object_id', models.CharField(max_length=100, null=True)), - ('file_extra_JSON', models.JSONField(null=True)), ('designation', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='upload_designation', to='globals.designation')), ('uploader', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='uploaded_files', to='globals.extrainfo')), ], @@ -43,7 +40,6 @@ class Migration(migrations.Migration): ('remarks', models.CharField(blank=True, max_length=250, null=True)), ('upload_file', models.FileField(blank=True, upload_to='')), ('is_read', models.BooleanField(default=False)), - ('tracking_extra_JSON', models.JSONField(null=True)), ('current_design', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='globals.holdsdesignation')), ('current_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='globals.extrainfo')), ('file_id', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='filetracking.file')), diff --git a/FusionIIIT/applications/filetracking/models.py b/FusionIIIT/applications/filetracking/models.py index 87f91ced8..5f9581a08 100644 --- a/FusionIIIT/applications/filetracking/models.py +++ b/FusionIIIT/applications/filetracking/models.py @@ -16,25 +16,22 @@ class File(models.Model): is_read = models.BooleanField(default = False) - # additions for API - src_module = models.CharField(max_length=100, default='filetracking') - src_object_id = models.CharField(max_length=100,null=True) - file_extra_JSON = models.JSONField(null=True) - class Meta: db_table = 'File' - # def __str__(self): - # return str(self.ref_id) + #def __str__(self): + #return str(self.ref_id) class Tracking(models.Model): """ - This is File Tracing Table which contains the status of each individual file created by the user + This is File Tracing Table which contains the status of each indivisual file created by the user """ file_id = models.ForeignKey(File, on_delete=models.CASCADE, null=True) current_id = models.ForeignKey(ExtraInfo, on_delete=models.CASCADE) current_design = models.ForeignKey(HoldsDesignation, null=True, on_delete=models.CASCADE) + # receiver_id = models.ForeignKey(ExtraInfo, on_delete=models.CASCADE, related_name='receiver_id') + # receive_design = models.ForeignKey(HoldsDesignation, null=True, on_delete=models.CASCADE, related_name='rec_design') receiver_id = models.ForeignKey(User,null = True, on_delete=models.CASCADE, related_name='receiver_id') receive_design = models.ForeignKey(Designation, null=True, on_delete=models.CASCADE, related_name='rec_design') @@ -44,8 +41,5 @@ class Tracking(models.Model): upload_file = models.FileField(blank=True) is_read = models.BooleanField(default = False) - # additions for API - tracking_extra_JSON = models.JSONField(null=True) - class Meta: - db_table = 'Tracking' \ No newline at end of file + db_table = 'Tracking' diff --git a/FusionIIIT/applications/filetracking/sdk/methods.py b/FusionIIIT/applications/filetracking/sdk/methods.py deleted file mode 100644 index 7bb56763e..000000000 --- a/FusionIIIT/applications/filetracking/sdk/methods.py +++ /dev/null @@ -1,428 +0,0 @@ -from amqp import NotFound -from django.contrib.auth.models import User -from applications.filetracking.models import Tracking, File -from applications.globals.models import Designation, HoldsDesignation, ExtraInfo -from applications.filetracking.api.serializers import FileSerializer, FileHeaderSerializer, TrackingSerializer -from django.core.exceptions import ValidationError -from typing import Any - - -def create_file( - uploader: str, - uploader_designation: str, - receiver: str, - receiver_designation: str, - subject: str = "", - description: str = "", - src_module: str = "filetracking", - src_object_id: str = "", - file_extra_JSON: dict = {}, - attached_file: Any = None) -> int: - ''' - This function is used to create a file object corresponding to any object of a module that needs to be tracked - ''' - - ''' - Functioning: - create base file with params - create tracking with params - if both complete then return id of file - else raise error - - also, delete file object if tracking isnt created - ''' - uploader_user_obj = get_user_object_from_username(uploader) - uploader_extrainfo_obj = get_ExtraInfo_object_from_username(uploader) - uploader_designation_obj = Designation.objects.get( - name=uploader_designation) - receiver_obj = get_user_object_from_username(receiver) - receiver_designation_obj = Designation.objects.get( - name=receiver_designation) - - new_file = File.objects.create( - uploader=uploader_extrainfo_obj, - subject=subject, - description=description, - designation=uploader_designation_obj, - src_module=src_module, - src_object_id=src_object_id, - file_extra_JSON=file_extra_JSON, - ) - - - if attached_file is not None: - new_file.upload_file.save(attached_file.name, attached_file, save=True) - - uploader_holdsdesignation_obj = HoldsDesignation.objects.get( - user=uploader_user_obj, designation=uploader_designation_obj) - - new_tracking = Tracking.objects.create( - file_id=new_file, - current_id=uploader_extrainfo_obj, - current_design=uploader_holdsdesignation_obj, - receiver_id=receiver_obj, - receive_design=receiver_designation_obj, - tracking_extra_JSON=file_extra_JSON, - remarks=f"File with id:{str(new_file.id)} created by {uploader} and sent to {receiver}" - # upload_file = None, dont add file for first tracking - ) - if new_tracking is None: - new_file.delete() - raise ValidationError('Tracking model data is incorrect') - else: - return new_file.id - - -def view_file(file_id: int) -> dict: - ''' - This function returns all the details of a given file - ''' - try: - requested_file = File.objects.get(id=file_id) - serializer = FileSerializer(requested_file) - file_details = serializer.data - return file_details - except File.DoesNotExist: - raise NotFound("File Not Found with provided ID") - - -def delete_file(file_id: int) -> bool: - ''' - This function is used to delete a file from being tracked, all the tracking history is deleted as well and returns true if the deletion was successful - ''' - try: - File.objects.filter(id=file_id).delete() - return True - except File.DoesNotExist: - return False - -# inbox and outbox could be sorted based on most recent linked tracking entry - -def view_inbox(username: str, designation: str, src_module: str) -> list: - ''' - This function is used to get all the files in the inbox of a particular user and designation - ''' - user_designation = Designation.objects.get(name=designation) - recipient_object = get_user_object_from_username(username) - received_files_tracking = Tracking.objects.select_related('file_id').filter( - receiver_id=recipient_object, - receive_design=user_designation, - file_id__src_module=src_module, - file_id__is_read=False).order_by('-receive_date'); - received_files = [tracking.file_id for tracking in received_files_tracking] - - # remove duplicate file ids (from sending back and forth) - received_files_unique = uniqueList(received_files) - - received_files_serialized = list(FileHeaderSerializer( - received_files_unique, many=True).data) - - for file in received_files_serialized: - file['sent_by_user'] = get_last_file_sender(file['id']).username - file['sent_by_designation'] = get_last_file_sender_designation(file['id']).name - return received_files_serialized - - -def view_outbox(username: str, designation: str, src_module: str) -> list: - ''' - This function is used to get all the files in the outbox of a particular user and designation - ''' - user_designation = get_designation_obj_from_name(designation=designation) - user_object = get_user_object_from_username(username) - user_HoldsDesignation_object = HoldsDesignation.objects.get( - user=user_object, designation=user_designation) - sender_ExtraInfo_object = get_ExtraInfo_object_from_username(username) - sent_files_tracking = Tracking.objects.select_related('file_id').filter( - current_id=sender_ExtraInfo_object, - current_design=user_HoldsDesignation_object, - file_id__src_module=src_module, - file_id__is_read=False).order_by('-receive_date') - sent_files = [tracking.file_id for tracking in sent_files_tracking] - - # remove duplicate file ids (from sending back and forth) - sent_files_unique = uniqueList(sent_files) - - sent_files_serialized = FileHeaderSerializer(sent_files_unique, many=True) - return sent_files_serialized.data - - - -def view_archived(username: str, designation: str, src_module: str) -> dict: - ''' - This function is used to get all the files in the archive of a particular user and designation - Archived file mean those which the user has ever interacted with, and are now finished or archived - ''' - user_designation = Designation.objects.get(name=designation) - user_object = get_user_object_from_username(username) - received_archived_tracking = Tracking.objects.select_related('file_id').filter( - receiver_id=user_object, - receive_design=user_designation, - file_id__src_module=src_module, - file_id__is_read=True) - - user_HoldsDesignation_object = HoldsDesignation.objects.get( - user=user_object, designation=user_designation) - sender_ExtraInfo_object = get_ExtraInfo_object_from_username(username) - sent_archived_tracking = Tracking.objects.select_related('file_id').filter( - current_id=sender_ExtraInfo_object, - current_design=user_HoldsDesignation_object, - file_id__src_module=src_module, - file_id__is_read=True).order_by('-receive_date') - - archived_tracking = received_archived_tracking | sent_archived_tracking - archived_files = [tracking.file_id for tracking in archived_tracking] - - # remove duplicate file ids (from sending back and forth) - archived_files_unique = uniqueList(archived_files) - - archived_files_serialized = FileHeaderSerializer(archived_files_unique, many=True) - return archived_files_serialized.data - - - -def archive_file(file_id: int) -> bool: - ''' - This function is used to archive a file and returns true if the archiving was successful - ''' - try: - File.objects.filter(id=file_id).update(is_read=True) - return True - except File.DoesNotExist: - return False - -def unarchive_file(file_id: int) -> bool: - ''' - This functions is used to unarchive a file and returns true if the unarchiving was successful - ''' - try: - File.objects.filter(id=file_id).update(is_read=False) - return True - except File.DoesNotExist: - return False - - - -def create_draft( - uploader: str, - uploader_designation: str, - src_module: str = "filetracking", - src_object_id: str = "", - file_extra_JSON: dict = {}, - attached_file: Any = None) -> int: - ''' - This function is used to create a draft file object corresponding to any object of a module that needs to be tracked - It is similar to create_file but is not sent to anyone - Later this file can be sent to someone by forward_file by using draft file_id - ''' - uploader_extrainfo_obj = get_ExtraInfo_object_from_username(uploader) - uploader_designation_obj = Designation.objects.get( - name=uploader_designation) - - new_file = File.objects.create( - uploader=uploader_extrainfo_obj, - designation=uploader_designation_obj, - src_module=src_module, - src_object_id=src_object_id, - file_extra_JSON=file_extra_JSON, - upload_file=attached_file - ) - return new_file.id - - -def view_drafts(username: str, designation: str, src_module: str) -> dict: - ''' - This function is used to get all the files in the drafts (has not been sent) of a particular user and designation - ''' - user_designation = Designation.objects.get(name=designation) - user_ExtraInfo_object = get_ExtraInfo_object_from_username(username) - draft_files = File.objects.filter( - tracking__isnull=True, uploader=user_ExtraInfo_object, designation=user_designation, src_module=src_module).order_by('-upload_date') - draft_files_serialized = FileHeaderSerializer(draft_files, many=True) - return draft_files_serialized.data - - - -def forward_file( - file_id: int, - receiver: str, - receiver_designation: str, - file_extra_JSON: dict, - remarks: str = "", - file_attachment: Any = None) -> int: - ''' - This function forwards the file and inserts a new tracking history into the file tracking table - Note that only the current owner(with appropriate designation) of the file has the ability to forward files - ''' - # HoldsDesignation and ExtraInfo object are used instead - # of Designation and User object because of the legacy code being that way - - current_owner = get_current_file_owner(file_id) - current_owner_designation = get_current_file_owner_designation(file_id) - current_owner_extra_info = ExtraInfo.objects.get(user=current_owner) - current_owner_holds_designation = HoldsDesignation.objects.get( - user=current_owner, designation=current_owner_designation) - receiver_obj = User.objects.get(username=receiver) - receiver_designation_obj = Designation.objects.get( - name=receiver_designation) - tracking_data = { - 'file_id': file_id, - 'current_id': current_owner_extra_info.id, - 'current_design': current_owner_holds_designation.id, - 'receiver_id': receiver_obj.id, - 'receive_design': receiver_designation_obj.id, - 'tracking_extra_JSON': file_extra_JSON, - 'remarks': remarks, - } - if file_attachment is not None: - tracking_data['upload_file'] = file_attachment - - tracking_entry = TrackingSerializer(data=tracking_data) - if tracking_entry.is_valid(): - tracking_entry.save() - return tracking_entry.instance.id - else: - raise ValidationError('forward data is incomplete') - - -def view_history(file_id: int) -> dict: - ''' - This function is used to get the history of a particular file with the given file_id - ''' - Tracking_history = Tracking.objects.filter( - file_id=file_id).order_by('-receive_date') - Tracking_history_serialized = TrackingSerializer( - Tracking_history, many=True) - return Tracking_history_serialized.data - - -# HELPER FUNCTIONS - -def get_current_file_owner(file_id: int) -> User: - ''' - This functions returns the current owner of the file. - The current owner is the latest recipient of the file - ''' - latest_tracking = Tracking.objects.filter( - file_id=file_id).order_by('-receive_date').first() - latest_recipient = latest_tracking.receiver_id - return latest_recipient - - -def get_current_file_owner_designation(file_id: int) -> Designation: - ''' - This function returns the designation of the current owner of the file. - The current owner is the latest recipient of the file - ''' - latest_tracking = Tracking.objects.filter( - file_id=file_id).order_by('-receive_date').first() - latest_recipient_designation = latest_tracking.receive_design - return latest_recipient_designation - -def get_last_file_sender(file_id: int) -> User: - ''' - This Function returns the last file sender, - one who has last forwarded/sent the file - ''' - latest_tracking = Tracking.objects.filter( - file_id=file_id).order_by('-receive_date').first() - latest_sender_extra_info = latest_tracking.current_id - return latest_sender_extra_info.user - -def get_last_file_sender_designation(file_id: int) -> Designation: - ''' - This Function returns the last file sender's Designation, - one who has last forwarded/sent the file - ''' - latest_tracking = Tracking.objects.filter( - file_id=file_id).order_by('receive_date').first() - latest_sender_holds_designation = latest_tracking.current_design - return latest_sender_holds_designation.designation - -def get_designations(username: str) -> list: - ''' - This function is used to return a list of all the designation names of a particular user - ''' - user = User.objects.get(username=username) - designations_held = HoldsDesignation.objects.filter(user=user) - designation_name = [hold_designation.designation.name for hold_designation in designations_held] - return designation_name - - -def get_user_object_from_username(username: str) -> User: - user = User.objects.get(username=username) - return user - -def get_ExtraInfo_object_from_username(username: str) -> ExtraInfo: - user = User.objects.get(username=username) - extrainfo = ExtraInfo.objects.get(user=user) - return extrainfo - -def uniqueList(l: list) -> list: - ''' - This function is used to return a list with unique elements - O(n) time and space - ''' - seen = set() - unique_list = [] - for item in l: - if item not in seen: - unique_list.append(item) - seen.add(item) - return unique_list - -def add_uploader_department_to_files_list(files: list) -> list: - ''' - This function is used to add the department of the uploader to the file - ''' - for file in files: - uploader_Extrainfo = file['uploader'] - if uploader_Extrainfo.department is None: - # for files created by staff or users that dont have department - file['uploader_department'] = 'FTS' - else: - file['uploader_department'] = ( - str(uploader_Extrainfo.department)).split(': ')[1] - - return files - -def get_designation_obj_from_name(designation: str) -> Designation: - des = Designation.objects.get(name = designation) - return des - -def get_HoldsDesignation_obj(username: str, designation:str) -> HoldsDesignation: - user_object = get_user_object_from_username(username=username) - user_designation = get_designation_obj_from_name(designation=designation) - obj = HoldsDesignation.objects.get( - user=user_object, designation=user_designation) - return obj - -def get_last_recv_tracking_for_user(file_id: int, username: str, designation: str)-> Tracking: - ''' - This returns the last tracking where username+designation recieved file_id - ''' - - recv_user_obj = get_user_object_from_username(username) - recv_design_obj = get_designation_obj_from_name(designation) - - last_tracking = Tracking.objects.filter(file_id=file_id, - receiver_id=recv_user_obj, - receive_design=recv_design_obj).order_by('-receive_date')[0] - return last_tracking - -def get_last_forw_tracking_for_user(file_id: int, username: str, designation: str) -> Tracking: - ''' - Returns the last tracking where the specified user forwarded the file. - ''' - - # Get user and designation objects - sender_user_obj = get_ExtraInfo_object_from_username(username) - sender_designation_obj = get_HoldsDesignation_obj(username=username, designation=designation) - - # Filter Tracking objects by file_id, sender_id, and sender_designation - last_tracking = Tracking.objects.filter(file_id=file_id, - current_id=sender_user_obj, - current_design=sender_designation_obj).order_by('-forward_date').first() - return last_tracking - -def get_extra_info_object_from_id(id: int): - return ExtraInfo.objects.get(id=id) diff --git a/FusionIIIT/applications/filetracking/urls.py b/FusionIIIT/applications/filetracking/urls.py index 7b56041a6..1007cdbf6 100644 --- a/FusionIIIT/applications/filetracking/urls.py +++ b/FusionIIIT/applications/filetracking/urls.py @@ -1,46 +1,31 @@ -from django.conf.urls import url, include +from django.conf.urls import url from . import views -from .api import urls app_name = 'filetracking' urlpatterns = [ url(r'^$', views.filetracking, name='filetracking'), - url(r'^draftdesign/$', views.draft_design, name='draft_design'), - url(r'^drafts/(?P\d+)$', views.drafts_view, name='drafts_view'), - url(r'^outbox/(?P\d+)$', views.outbox_view, name='outbox_view'), - url(r'^inbox/$', views.inbox_view, name='inbox_view'), - url(r'^outward/$', views.outbox_view, name='outward'), - url(r'^inward/$', views.inbox_view, name='inward'), - url(r'^confirmdelete/(?P\d+)$', - views.confirmdelete, name='confirm_delete'), - url(r'^archive/(?P\d+)/$', views.archive_view, name='archive_view'), - url(r'^finish/(?P\d+)/$', views.archive_file, name='finish_file'), - url(r'^viewfile/(?P\d+)/$', views.view_file, name='view_file_view'), + url(r'^drafts/$', views.drafts, name='drafts'), + url(r'^fileview/(?P\d+)$', views.fileview, name='fileview'), + url(r'^fileview1/(?P\d+)$', views.fileview1, name='fileview1'), + url(r'^fileview2/(?P\d+)$', views.fileview2, name='fileview2'), + url(r'^outward/$', views.outward, name='outward'), + url(r'^inward/$', views.inward, name='inward'), + url(r'^confirmdelete/(?P\d+)$', views.confirmdelete, name='confirm_delete'), + url(r'^archive/(?P\d+)/$', views.archive, name='archive'), + url(r'^finish/(?P\d+)/$', views.finish, name='finish'), url(r'^forward/(?P\d+)/$', views.forward, name='forward'), url(r'^ajax/$', views.AjaxDropdown1, name='ajax_dropdown1'), url(r'^ajax_dropdown/$', views.AjaxDropdown, name='ajax_dropdown'), - url(r'^delete/(?P\d+)$', views.delete, name='delete'), - url(r'^forward_inward/(?P\d+)/$', - views.forward_inward, name='forward_inward'), + url(r'^test/$',views.test, name='test'), + url(r'^delete/(?P\d+)$',views.delete, name='delete'), + url(r'^forward_inward/(?P\d+)/$', views.forward_inward, name='forward_inward'), - # correction team 24 + ## correction team 24 url(r'^finish_design/$', views.finish_design, name='finish_design'), - url(r'^finish_fileview/(?P\d+)$', - views.finish_fileview, - name='finish_fileview'), + url(r'^finish_fileview/(?P\d+)$', views.finish_fileview, name='finish_fileview'), url(r'^archive_design/$', views.archive_design, name='archive_design'), - url(r'^archive_finish/(?P\d+)/$', - views.archive_finish, name='archive_finish'), - url(r'unarchive/(?P\d+)/$', - views.unarchive_file, name='unarchive'), - url(r'^getdesignations/(?P\w+)/$', views.get_designations_view, name="get_user_designations"), - url(r'^editdraft/(?P\w+)/$', views.edit_draft_view, name="edit_draft"), - url(r'^download_file/(?P\w+)/$', views.download_file, name="download_file"), - - # REST api urls - url(r'^api/', include(urls)) - + url(r'^archive_finish/(?P\d+)/$', views.archive_finish, name='archive_finish'), ] diff --git a/FusionIIIT/applications/filetracking/views.py b/FusionIIIT/applications/filetracking/views.py index 0bba0d9a1..ef5d8f347 100644 --- a/FusionIIIT/applications/filetracking/views.py +++ b/FusionIIIT/applications/filetracking/views.py @@ -1,38 +1,19 @@ -from sqlite3 import IntegrityError -from django.http import HttpResponse, HttpResponseRedirect, JsonResponse from django.contrib import messages -from django.shortcuts import render, get_object_or_404, redirect, reverse +from django.shortcuts import render, get_object_or_404, redirect +from .models import File, Tracking +from applications.globals.models import ExtraInfo, HoldsDesignation, Designation +from django.template.defaulttags import csrf_token +from django.http import HttpResponse, HttpResponseRedirect, JsonResponse from django.contrib.auth.decorators import login_required +from django.db import IntegrityError from django.core import serializers from django.contrib.auth.models import User -from django.views.decorators.http import require_POST -from django.utils import timezone -from django.utils.dateparse import parse_datetime -from django.core.paginator import Paginator - -from .models import File, Tracking -from applications.globals.models import ExtraInfo, HoldsDesignation, Designation -from .utils import * -from .sdk.methods import * -from .decorators import * -from datetime import datetime; - from timeit import default_timer as time -from notification.views import office_module_notif, file_tracking_notif - -import io -from reportlab.lib.pagesizes import letter -from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Image -from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle - -import json -import zipfile -import os +from notification.views import office_module_notif,file_tracking_notif +from .utils import * -@login_required(login_url="/accounts/login/") -@user_is_student -@dropdown_designation_valid +@login_required(login_url = "/accounts/login/") def filetracking(request): """ The function is used to create files by current user(employee). @@ -54,50 +35,39 @@ def filetracking(request): holdsdesignations - The HoldsDesignation object. context - Holds data needed to make necessary changes in the template. """ - - if request.method == "POST": + if request.method =="POST": try: if 'save' in request.POST: uploader = request.user.extrainfo subject = request.POST.get('title') description = request.POST.get('desc') design = request.POST.get('design') - designation = Designation.objects.get(id=HoldsDesignation.objects.select_related( - 'user', 'working', 'designation').get(id=design).designation_id) + designation = Designation.objects.get(id = HoldsDesignation.objects.select_related('user','working','designation').get(id = design).designation_id) upload_file = request.FILES.get('myfile') - if upload_file and upload_file.size / 1000 > 10240: - messages.error( - request, "File should not be greater than 10MB") - return redirect("/filetracking") - - form_remarks = request.POST.get('remarks') - extraJSON = { - 'remarks': form_remarks if form_remarks is not None else '', - } + if(upload_file.size / 1000 > 10240): + messages.error(request,"File should not be greater than 10MB") + return redirect("/filetracking") File.objects.create( uploader=uploader, description=description, subject=subject, designation=designation, - upload_file=upload_file, - file_extra_JSON=extraJSON + upload_file=upload_file ) - messages.success(request, 'File Draft Saved Successfully') + messages.success(request,'File Draft Saved Successfully') if 'send' in request.POST: uploader = request.user.extrainfo subject = request.POST.get('title') description = request.POST.get('desc') design = request.POST.get('design') - designation = Designation.objects.get(id=HoldsDesignation.objects.select_related( - 'user', 'working', 'designation').get(id=design).designation_id) + designation = Designation.objects.get(id = HoldsDesignation.objects.select_related('user','working','designation').get(id = design).designation_id) upload_file = request.FILES.get('myfile') - if upload_file and upload_file.size / 1000 > 10240: - messages.error( - request, "File should not be greater than 10MB") + if(upload_file.size / 1000 > 10240): + messages.error(request,"File should not be greater than 10MB") return redirect("/filetracking") file = File.objects.create( @@ -108,12 +78,12 @@ def filetracking(request): upload_file=upload_file ) + current_id = request.user.extrainfo remarks = request.POST.get('remarks') sender = request.POST.get('design') - current_design = HoldsDesignation.objects.select_related( - 'user', 'working', 'designation').get(id=sender) + current_design = HoldsDesignation.objects.select_related('user','working','designation').get(id=sender) receiver = request.POST.get('receiver') try: @@ -121,7 +91,7 @@ def filetracking(request): except Exception as e: messages.error(request, 'Enter a valid Username') return redirect('/filetracking/') - receive = request.POST.get('receive') + receive = request.POST.get('recieve') try: receive_design = Designation.objects.get(name=receive) except Exception as e: @@ -139,65 +109,55 @@ def filetracking(request): remarks=remarks, upload_file=upload_file, ) - file_tracking_notif(request.user, receiver_id, subject) - messages.success(request, 'File sent successfully') + #office_module_notif(request.user, receiver_id) + file_tracking_notif(request.user,receiver_id,subject) + messages.success(request,'File sent successfully') except IntegrityError: message = "FileID Already Taken.!!" return HttpResponse(message) - file = File.objects.select_related( - 'uploader__user', 'uploader__department', 'designation').all() - extrainfo = ExtraInfo.objects.select_related('user', 'department').all() - holdsdesignations = HoldsDesignation.objects.select_related( - 'user', 'working', 'designation').all() - - designation_name = request.session.get('currentDesignationSelected', 'default_value') - all_available_designations = request.session.get( - 'allDesignations', 'default_value2') - username = request.user - designation_id = get_HoldsDesignation_obj( - username, designation_name).id + file = File.objects.select_related('uploader__user','uploader__department','designation').all() + extrainfo = ExtraInfo.objects.select_related('user','department').all() + holdsdesignations = HoldsDesignation.objects.select_related('user','working','designation').all() + designations = get_designation(request.user) context = { 'file': file, 'extrainfo': extrainfo, 'holdsdesignations': holdsdesignations, - 'designation_name': designation_name, - 'designation_id': designation_id, - 'notifications': request.user.notifications.all(), - 'path_parent': 'compose' + 'designations': designations, } return render(request, 'filetracking/composefile.html', context) -@login_required(login_url="/accounts/login") -@user_is_student -@dropdown_designation_valid -def draft_design(request): - """ - This function redirects the user to the drafts page of designation selected in dropdown - @param: - request - trivial. +@login_required(login_url = "/accounts/login") +def drafts(request): """ + The function is used to get the designation of the user and renders it on draft template. - dropdown_design = request.session.get( - 'currentDesignationSelected', 'default_value') - username = request.user - dropdown_HoldsDesignation_obj = get_HoldsDesignation_obj( - username, dropdown_design) + @param: + request - trivial. + + @variables: + + + context - Holds data needed to make necessary changes in the template. + """ + designation = get_designation(request.user) + context = { + 'designation': designation, + } + return render(request, 'filetracking/drafts.html', context) - return redirect('/filetracking/drafts/' + str(dropdown_HoldsDesignation_obj.id)) +@login_required(login_url = "/accounts/login") +def fileview(request,id): -@login_required(login_url="/accounts/login") -@user_is_student -@dropdown_designation_valid -def drafts_view(request, id): """ - This function is used to view all the drafts created by the user ordered by upload date.it collects all the created files from File object. + This function is used to veiw all all created files by the user ordered by upload date.it collects all the created files from File object. @param: request - trivial @@ -206,216 +166,147 @@ def drafts_view(request, id): @parameters draft - file obeject containing all the files created by user context - holds data needed to render the template + + """ - user_HoldsDesignation_obj = HoldsDesignation.objects.select_related( - 'user', 'working', 'designation').get(pk=id) - s = str(user_HoldsDesignation_obj).split(" - ") - designation = s[1] - draft_files = view_drafts( - username=user_HoldsDesignation_obj.user, - designation=user_HoldsDesignation_obj.designation, - src_module='filetracking' - ) - - # Correct upload_date type - for f in draft_files: - f['upload_date'] = parse_datetime(f['upload_date']) - f['uploader'] = get_extra_info_object_from_id(f['uploader']) - - draft_files = add_uploader_department_to_files_list(draft_files) + # draft = File.objects.select_related('uploader__user','uploader__department','designation').filter(uploader=request.user.extrainfo).order_by('-upload_date') + # extrainfo = ExtraInfo.objects.select_related('user','department').all() + + extrainfo = ExtraInfo.objects.select_related('user','department').all() + + ids = File.objects.filter(uploader=request.user.extrainfo).order_by('-upload_date').values_list('id', flat=True) + draft_files_pk=[] + + for i in ids: + file_tracking_ids = Tracking.objects.filter(file_id=i).values_list('id', flat=True) + if(len(file_tracking_ids)==0): + draft_files_pk.append(i) + + draft_file_list=[] + for i in draft_files_pk: + draft_file_list.append(File.objects.get(pk=i)) + + + user_designation = HoldsDesignation.objects.select_related('user','working','designation').get(pk=id) + s = str(user_designation).split(" - ") + designations = s[1] context = { - 'draft_files': draft_files, - 'designations': designation, - 'notifications': request.user.notifications.all(), - 'path_parent': 'draft' + + 'draft': draft_file_list, + 'extrainfo': extrainfo, + 'designations': designations, } - return render(request, 'filetracking/drafts.html', context) + return render(request, 'filetracking/fileview.html', context) -@login_required(login_url="/accounts/login") -@user_is_student -@dropdown_designation_valid -def outbox_view(request): + +@login_required(login_url = "/accounts/login") +def fileview1(request,id): + """ - The function is used to get all the files sent by user(employee) to other employees - which are filtered from Tracking(table) objects by current user i.e. current_id. - It displays files sent by user to other employees of a Tracking(table) of filetracking(model) - in the 'Outbox' tab of template. + The function is used to get all the files sent by user(employee) to other employees + which are filtered from Tracking(table) objects by current user i.e. current_id. + It displays files sent by user to other employees of a Tracking(table) of filetracking(model) + in the 'Outbox' tab of template. - @param: - request - trivial. - id - user id + @param: + request - trivial. + id - user id - @variables: - outward_files - File objects filtered by current_id i.e, present working user. - context - Holds data needed to make necessary changes in the template. + @variables: + out - The Tracking object filtered by current_id i.e, present working user. + context - Holds data needed to make necessary changes in the template. + """ - dropdown_design = request.session.get('currentDesignationSelected', 'default_value') - username = request.user - user_HoldsDesignation_obj = get_HoldsDesignation_obj(username, dropdown_design) - s = str(user_HoldsDesignation_obj).split(" - ") - designation = s[1] - - outward_files = view_outbox(username=user_HoldsDesignation_obj.user, - designation=user_HoldsDesignation_obj.designation, - src_module='filetracking') - - for f in outward_files: - last_forw_tracking = get_last_forw_tracking_for_user(file_id=f['id'], - username=user_HoldsDesignation_obj.user, - designation=user_HoldsDesignation_obj.designation) - f['sent_to_user'] = last_forw_tracking.receiver_id - f['sent_to_design'] = last_forw_tracking.receive_design - f['last_sent_date'] = last_forw_tracking.forward_date - f['upload_date'] = parse_datetime(f['upload_date']) - f['uploader'] = get_extra_info_object_from_id(f['uploader']) - - subject_query = request.GET.get('subject', '') - sent_to_query = request.GET.get('sent_to', '') - date_query = request.GET.get('date', '') - - if subject_query: - outward_files = [f for f in outward_files if subject_query.lower() in f['subject'].lower()] - if sent_to_query: - outward_files = [f for f in outward_files if sent_to_query.lower() in f['sent_to_user'].username.lower()] - if date_query: - try: - search_date = datetime.strptime(date_query, '%Y-%m-%d') - outward_files = [f for f in outward_files if f['last_sent_date'].date() == search_date.date()] - except ValueError: - outward_files = [] # Invalid date format - paginator = Paginator(outward_files, 10) - page_number = request.GET.get('page') - page_obj = paginator.get_page(page_number) + outward_files = Tracking.objects.select_related('file_id__uploader__user','file_id__uploader__department','file_id__designation','current_id__user','current_id__department', + 'current_design__user','current_design__working','current_design__designation','receiver_id','receive_design').filter(current_id=request.user.extrainfo).order_by('-forward_date') + + user_designation = HoldsDesignation.objects.select_related('user','working','designation').get(pk=id) context = { - 'page_obj': page_obj, - 'viewer_designation': designation, - 'notifications': request.user.notifications.all(), - 'path_parent': 'outbox', + + 'out': outward_files, + 'abcd': user_designation, } - return render(request, 'filetracking/outbox.html', context) + return render(request, 'filetracking/fileview1.html', context) -@login_required(login_url="/accounts/login") -@user_is_student -@dropdown_designation_valid -def inbox_view(request): +@login_required(login_url = "/accounts/login") +def fileview2(request,id): + """ The function is used to fetch the files received by the user form other employees. These files are filtered by receiver id and ordered by receive date. @param: request - trivial. - id - HoldsDesignation object id + id - user id @variables: - inward_files - File object with additional sent by information + inward_file - The Tracking object filtered by receiver_id i.e, present working user. context - Holds data needed to make necessary changes in the template. """ - dropdown_design = request.session.get( - 'currentDesignationSelected', 'default_value') - username = request.user - user_HoldsDesignation_obj = get_HoldsDesignation_obj( - username, dropdown_design) - s = str(user_HoldsDesignation_obj).split(" - ") - designation = s[1] - inward_files = view_inbox( - username=user_HoldsDesignation_obj.user, - designation=user_HoldsDesignation_obj.designation, - src_module='filetracking' - ) - - # correct upload_date type and add recieve_date - for f in inward_files: - f['upload_date'] = parse_datetime(f['upload_date']) - - last_recv_tracking = get_last_recv_tracking_for_user(file_id=f['id'], - username=user_HoldsDesignation_obj.user, - designation=user_HoldsDesignation_obj.designation) - f['receive_date'] = last_recv_tracking.receive_date - f['uploader'] = get_extra_info_object_from_id(f['uploader']) - f['is_forwarded'] = (False if (str(get_current_file_owner(f['id']).username) == str(user_HoldsDesignation_obj.user)) else True) - - - inward_files = add_uploader_department_to_files_list(inward_files) + inward_file = Tracking.objects.select_related('file_id__uploader__user','file_id__uploader__department','file_id__designation','current_id__user','current_id__department', + 'current_design__user','current_design__working','current_design__designation','receiver_id','receive_design').filter(receiver_id=request.user).order_by('-receive_date') - subject_query = request.GET.get('subject', '') - sent_to_query = request.GET.get('sent_to', '') - date_query = request.GET.get('date', '') - - if subject_query: - inward_files = [f for f in inward_files if subject_query.lower() in f['subject'].lower()] - if sent_to_query: - inward_files = [f for f in inward_files if sent_to_query.lower() in f['sent_to_user'].username.lower()] - if date_query: - try: - search_date = datetime.strptime(date_query, '%Y-%m-%d') - inward_files = [f for f in inward_files if f['last_sent_date'].date() == search_date.date()] - except ValueError: - inward_files = [] # Invalid date format - - paginator = Paginator(inward_files, 10) - page_number = request.GET.get('page') - page_obj = paginator.get_page(page_number) + user_designation = HoldsDesignation.objects.select_related('user','working','designation').get(pk=id) + s = str(user_designation).split(" - ") + designations = s[1] context = { - 'page_obj': page_obj, - 'designations': designation, - 'notifications': request.user.notifications.all(), - 'path_parent': 'inbox' + + 'in_file': inward_file, + 'designations': designations, } - return render(request, 'filetracking/inbox.html', context) + return render(request, 'filetracking/fileview2.html', context) -@login_required(login_url="/accounts/login") -@user_is_student -@dropdown_designation_valid +@login_required(login_url = "/accounts/login") def outward(request): """ - This function redirects the user to the outbox page of designation selected in dropdown + This function fetches the different designations of the user and renders it on outward template @param: request - trivial. - """ - - dropdown_design = request.session.get( - 'currentDesignationSelected', 'default_value') - username = request.user - dropdown_HoldsDesignation_obj = get_HoldsDesignation_obj( - username, dropdown_design) - - return redirect('/filetracking/outbox/' + str(dropdown_HoldsDesignation_obj.id)) + @variables: + context - Holds the different designation data of the user + + """ + designation = get_designation(request.user) + context = { + 'designation': designation, + } + return render( request, 'filetracking/outward.html', context) -@login_required(login_url="/accounts/login") -@user_is_student -@dropdown_designation_valid +@login_required(login_url = "/accounts/login") def inward(request): - """ - This function redirects the user to the inbox page of designation selected in dropdown - @param: - request - trivial. """ + This function fetches the different designations of the user and renders it on inward template - dropdown_design = request.session.get( - 'currentDesignationSelected', 'default_value') - username = request.user - dropdown_HoldsDesignation_obj = get_HoldsDesignation_obj( - username, dropdown_design) + + @param: + request - trivial. - return redirect('/filetracking/inbox/' + str(dropdown_HoldsDesignation_obj.id)) + @variables: + context - Holds the different designation data of the user + """ + designation = get_designation(request.user) + context = { + + 'designation': designation, + } + return render(request, 'filetracking/inward.html', context) @login_required(login_url = "/accounts/login") -@user_is_student -@dropdown_designation_valid def confirmdelete(request,id): + """ The function is used to confirm the deletion of a file. @param: @@ -425,96 +316,16 @@ def confirmdelete(request,id): @variables: context - Holds data needed to make necessary changes in the template. """ - file = File.objects.select_related( - 'uploader__user', 'uploader__department', 'designation').get(pk=id) + file = File.objects.select_related('uploader__user','uploader__department','designation').get(pk = id) context = { 'j': file, } - return render(request, 'filetracking/confirmdelete.html', context) - -@login_required(login_url="/accounts/login") -@user_is_student -@dropdown_designation_valid -def view_file(request, id): - ''' - This function is used to view a particular file received by an employee from another. - This function also conditionally renders two forms 'forward_file' and 'archive_file' - based on if the user has necessary permissions or not. - The business permissions are as follows: - 1. User can forward file only if they are the last recipient of the file - 2. User can archive a file only if they have received it last and they are also the original owner of the file - - To forward the file and to archive the file separate views with POST request are called - - It displays the details file of a File and remarks as well as the attachments of all the users - who have been involved till that point of the workflow. - - @param: - request - Trivial. - id - ID of the file object which the user intends to forward to another employee. - - @variables: - file - The File object. - track - The Tracking object. - designation - the designations of the user - ''' + return render(request, 'filetracking/confirmdelete.html',context) - file = get_object_or_404(File, id=id) - track = Tracking.objects.select_related('file_id__uploader__user', 'file_id__uploader__department', 'file_id__designation', 'current_id__user', 'current_id__department', - 'current_design__user', 'current_design__working', 'current_design__designation', 'receiver_id', 'receive_design').filter(file_id=file).order_by('receive_date') - designations = get_designation(request.user) - forward_enable = False - archive_enable = False - - current_owner = get_current_file_owner(file.id) - file_uploader = get_user_object_from_username(file.uploader.user.username) - - last_receiver_designation = get_current_file_owner_designation(file.id).name - - if current_owner == request.user and file.is_read is False: - forward_enable = True - if current_owner == request.user and last_receiver_designation == file.designation.name and file_uploader == request.user and file.is_read is False: - archive_enable = True - - parent_of_prev_path = request.META.get('HTTP_REFERER').strip("/").split('/')[-2] - context = { - 'designations': designations, - 'file': file, - 'track': track, - 'forward_enable': forward_enable, - 'archive_enable': archive_enable, - 'notifications': request.user.notifications.all(), - 'path_parent': parent_of_prev_path - } - return render(request, 'filetracking/viewfile.html', context) - -@login_required(login_url="/accounts/login") -@user_is_student -@dropdown_designation_valid -def archive_file(request, id): - '''This function is used to archive a file. - It returns unauthorized access if the user is not file uploader - and the current owner of the file - ''' - if request.method == "POST": - file = get_object_or_404(File, id=id) - current_owner = get_current_file_owner(file.id) - file_uploader = get_user_object_from_username(file.uploader.user.username) - if current_owner == request.user and file_uploader == request.user: - file.is_read = True - file.save() - messages.success(request, 'File Archived') - else: - messages.error(request, 'Unauthorized access') - - return render(request, 'filetracking/composefile.html') - -@login_required(login_url="/accounts/login") -@user_is_student -@dropdown_designation_valid +@login_required(login_url = "/accounts/login") def forward(request, id): """ The function is used to forward files received by user(employee) from other @@ -540,241 +351,198 @@ def forward(request, id): holdsdesignations = HoldsDesignation objects. context - Holds data needed to make necessary changes in the template. """ - + file = get_object_or_404(File, id=id) - track = Tracking.objects.select_related('file_id__uploader__user', 'file_id__uploader__department', 'file_id__designation', 'current_id__user', 'current_id__department', - 'current_design__user', 'current_design__working', 'current_design__designation', 'receiver_id', 'receive_design').filter(file_id=file).order_by('receive_date') + track = Tracking.objects.select_related('file_id__uploader__user','file_id__uploader__department','file_id__designation','current_id__user','current_id__department', + 'current_design__user','current_design__working','current_design__designation','receiver_id','receive_design').filter(file_id=file) + + if request.method == "POST": + if 'finish' in request.POST: + file.complete_flag = True + file.save() - designations = get_designation(request.user) + if 'send' in request.POST: + current_id = request.user.extrainfo + remarks = request.POST.get('remarks') + track.update(is_read=True) - designation_name = request.session.get('currentDesignationSelected', 'default_value') - all_available_designations = request.session.get( - 'allDesignations', 'default_value2') + sender = request.POST.get('sender') + current_design = HoldsDesignation.objects.select_related('user','working','designation').get(id=sender) + + receiver = request.POST.get('receiver') + try: + receiver_id = User.objects.get(username=receiver) + except Exception as e: + messages.error(request, 'Enter a valid destination') + designations = HoldsDesignation.objects.select_related('user','working','designation').filter(user=request.user) + + context = { + + 'designations': designations, + 'file': file, + 'track': track, + } + return render(request, 'filetracking/forward.html', context) + receive = request.POST.get('recieve') + try: + receive_design = Designation.objects.get(name=receive) + except Exception as e: + messages.error(request, 'Enter a valid Designation') + designations = get_designation(request.user) - username = request.user - designation_id = get_HoldsDesignation_obj( - username, designation_name).id + context = { + + 'designations': designations, + 'file': file, + 'track': track, + } + return render(request, 'filetracking/forward.html', context) - if request.method == "POST": - if 'finish' in request.POST: - file.is_read = True - file.save() - if 'send' in request.POST: - current_id = request.user.extrainfo - remarks = request.POST.get('remarks') - track.update(is_read=True) - - sender = request.POST.get('sender') - current_design = HoldsDesignation.objects.select_related( - 'user', 'working', 'designation').get(id=sender) - - receiver = request.POST.get('receiver') - try: - receiver_id = User.objects.get(username=receiver) - except Exception as e: - messages.error(request, 'Enter a valid destination') - designations = HoldsDesignation.objects.select_related( - 'user', 'working', 'designation').filter(user=request.user) - - context = { - 'designations': designations, - 'file': file, - 'track': track, - 'designation_name': designation_name, - 'designation_id': designation_id, - 'notifications': request.user.notifications.all(), - 'path_parent': 'inbox' - } - - return render(request, 'filetracking/forward.html', context) - receive = request.POST.get('receive') - try: - receive_design = Designation.objects.get(name=receive) - except Exception as e: - messages.error(request, 'Enter a valid Designation') - designations = get_designation(request.user) - - context = { - 'designations': designations, - 'file': file, - 'track': track, - 'designation_name': designation_name, - 'designation_id': designation_id, - 'notifications': request.user.notifications.all(), - 'path_parent': 'inbox' - } - - return render(request, 'filetracking/forward.html', context) - - upload_file = request.FILES.get('myfile') - - Tracking.objects.create( - file_id=file, - current_id=current_id, - current_design=current_design, - receive_design=receive_design, - receiver_id=receiver_id, - remarks=remarks, - upload_file=upload_file, - ) - file_tracking_notif(request.user, receiver_id, file.subject) - messages.success(request, 'File sent successfully') - return redirect(reverse('filetracking:filetracking')) + + upload_file = request.FILES.get('myfile') + + Tracking.objects.create( + file_id=file, + current_id=current_id, + current_design=current_design, + receive_design=receive_design, + receiver_id=receiver_id, + remarks=remarks, + upload_file=upload_file, + ) + messages.success(request, 'File sent successfully') + + + designations = get_designation(request.user) context = { - 'designations': designations, + + 'designations':designations, 'file': file, 'track': track, - 'designation_name': designation_name, - 'designation_id': designation_id, - 'notifications': request.user.notifications.all(), - 'path_parent': 'inbox' } return render(request, 'filetracking/forward.html', context) -@login_required(login_url="/accounts/login") -@user_is_student -@dropdown_designation_valid +@login_required(login_url = "/accounts/login") def archive_design(request): - """ - This function redirects the user to the archive page of designation selected in dropdown - @param: - request - trivial. - """ - - dropdown_design = request.session.get( - 'currentDesignationSelected', 'default_value') - username = request.user - dropdown_HoldsDesignation_obj = get_HoldsDesignation_obj( - username, dropdown_design) - return redirect('/filetracking/archive/' + str(dropdown_HoldsDesignation_obj.id)) + designation = HoldsDesignation.objects.select_related('user','working','designation').filter(user=request.user) + context = { + 'designation': designation, + } + return render( request, 'filetracking/archive_design.html', context) + + -@login_required(login_url="/accounts/login") -@user_is_student -@dropdown_designation_valid -def archive_view(request, id): - """ - The function is used to fetch the files in the user's archive - (those which have passed by user and been archived/finished) - - @param: - request - trivial. - id - HoldsDesignation object id - @variables: - archive_files - File object with additional information - context - Holds data needed to make necessary changes in the template. - """ - user_HoldsDesignation_obj = HoldsDesignation.objects.select_related( - 'user', 'working', 'designation').get(pk=id) - s = str(user_HoldsDesignation_obj).split(" - ") - designation = s[1] +@login_required(login_url = "/accounts/login") +def archive(request , id): - archive_files = view_archived( - username=user_HoldsDesignation_obj.user, - designation=user_HoldsDesignation_obj.designation, - src_module='filetracking' - ) + draft = File.objects.select_related('uploader__user','uploader__department','designation').filter(is_read=True).order_by('-upload_date') - # correct upload_date type and add receive_date - for f in archive_files: - f['upload_date'] = parse_datetime(f['upload_date']) - f['designation'] = Designation.objects.get(id=f['designation']) - f['uploader'] = get_extra_info_object_from_id(f['uploader']) - archive_files = add_uploader_department_to_files_list(archive_files) + extrainfo = ExtraInfo.objects.select_related('user','department').all() + # designations = Designation.objects.filter(upload_designation=extrainfo.id) + abcd = HoldsDesignation.objects.select_related('user','working','designation').get(pk=id) + s = str(abcd).split(" - ") + designations = s[1] + #designations = HoldsDesignation.objects.filter(user=request.user) + # for x in designations: + # if abcd==x: + # designations=abcd context = { - 'archive_files': archive_files, - 'designations': designation, - 'notifications': request.user.notifications.all(), - 'path_parent': 'archive' + + 'draft': draft, + 'extrainfo': extrainfo, + 'designations': designations, } - return render(request, 'filetracking/archive.html', context) -@login_required(login_url="/accounts/login") -@user_is_student -@dropdown_designation_valid + + return render(request, 'filetracking/archive.html' , context) + + +@login_required(login_url = "/accounts/login") def archive_finish(request, id): - file1 = get_object_or_404(File, id=id) + file1 = get_object_or_404(File, id=id) ##file = get_object_or_404(File, ref_id=id) track = Tracking.objects.filter(file_id=file1) + + return render(request, 'filetracking/archive_finish.html', {'file': file1, 'track': track}) -@login_required(login_url="/accounts/login") -@user_is_student -@dropdown_designation_valid + + +@login_required(login_url = "/accounts/login") def finish_design(request): - designation = HoldsDesignation.objects.select_related( - 'user', 'working', 'designation').filter(user=request.user) + designation = HoldsDesignation.objects.select_related('user','working','designation').filter(user=request.user) context = { 'designation': designation, - 'notifications': request.user.notifications.all() } - return render(request, 'filetracking/finish_design.html', context) + return render( request, 'filetracking/finish_design.html', context) -@login_required(login_url="/accounts/login") -@user_is_student -@dropdown_designation_valid +@login_required(login_url = "/accounts/login") def finish_fileview(request, id): - out = Tracking.objects.select_related('file_id__uploader__user', 'file_id__uploader__department', 'file_id__designation', 'current_id__user', 'current_id__department', - 'current_design__user', 'current_design__working', 'current_design__designation', 'receiver_id', 'receive_design').filter(file_id__uploader=request.user.extrainfo, is_read=False).order_by('-forward_date') + out = Tracking.objects.select_related('file_id__uploader__user','file_id__uploader__department','file_id__designation','current_id__user','current_id__department', + 'current_design__user','current_design__working','current_design__designation','receiver_id','receive_design').filter(file_id__uploader=request.user.extrainfo, is_read=False).order_by('-forward_date') + + + + + abcd = HoldsDesignation.objects.select_related('user','working','designation').get(pk=id) - abcd = HoldsDesignation.objects.select_related( - 'user', 'working', 'designation').get(pk=id) context = { 'out': out, 'abcd': abcd, - 'notifications': request.user.notifications.all() } return render(request, 'filetracking/finish_fileview.html', context) -@login_required(login_url="/accounts/login") -@user_is_student -@dropdown_designation_valid + + +@login_required(login_url = "/accounts/login") def finish(request, id): - # file = get_object_or_404(File, ref_id=id) - file1 = get_object_or_404(File, id=id) + file1 = get_object_or_404(File, id=id) ##file = get_object_or_404(File, ref_id=id) track = Tracking.objects.filter(file_id=file1) + if request.method == "POST": - if 'Finished' in request.POST: - File.objects.filter(pk=id).update(is_read=True) - track.update(is_read=True) - messages.success(request, 'File Archived') + if 'Finished' in request.POST: + File.objects.filter(pk=id).update(is_read=True) + track.update(is_read=True) + messages.success(request,'File Archived') - context = { - 'file': file1, - 'track': track, - 'fileid': id, - 'notifications': request.user.notifications.all() - } + + - return render(request, 'filetracking/finish.html', ) -def AjaxDropdown1(request): + + + return render(request, 'filetracking/finish.html', {'file': file1, 'track': track,'fileid':id}) + + + +def AjaxDropdown1(request): """ - This function returns the designation of receiver on the forward or compose file template. + This function returns the designation of receiver on the forward or compose file template. @param: request - trivial. - + @variables: context - return the httpresponce containing the matched designation of the user @@ -785,19 +553,20 @@ def AjaxDropdown1(request): hold = Designation.objects.filter(name__startswith=value) holds = serializers.serialize('json', list(hold)) context = { - 'holds': holds + 'holds' : holds } return HttpResponse(JsonResponse(context), content_type='application/json') def AjaxDropdown(request): + """ - This function returns the usernames of receiver on the forward or compose file template. + This function returns the usernames of receiver on the forward or compose file template. @param: request - trivial. - + @variables: context - return the httpresponce containing the matched username @@ -813,27 +582,31 @@ def AjaxDropdown(request): return HttpResponse(JsonResponse(context), content_type='application/json') +def test(request): + return HttpResponse('success') + + @login_required(login_url = "/accounts/login") -@user_is_student -@dropdown_designation_valid def delete(request,id): + """ The function is used the delete of a file and it returns to the drafts page. @param: request - trivial. id - id of the file that is going to be deleted - + """ - file = File.objects.get(pk=id) + file = File.objects.get(pk = id) file.delete() - return redirect('/filetracking/draftdesign/') + return redirect('/filetracking/drafts/') + + -@user_is_student -@dropdown_designation_valid def forward_inward(request,id): + """ This function is used forward the files which are available in the inbox of the user . @param: @@ -844,230 +617,21 @@ def forward_inward(request,id): file - file object track - tracking object of the file context - necessary data to render - + """ file = get_object_or_404(File, id=id) file.is_read = True - track = Tracking.objects.select_related('file_id__uploader__user', 'file_id__uploader__department', 'file_id__designation', 'current_id__user', 'current_id__department', - 'current_design__user', 'current_design__working', 'current_design__designation', 'receiver_id', 'receive_design').filter(file_id=file) + track = Tracking.objects.select_related('file_id__uploader__user','file_id__uploader__department','file_id__designation','current_id__user','current_id__department', + 'current_design__user','current_design__working','current_design__designation','receiver_id','receive_design').filter(file_id=file) designations = get_designation(request.user) context = { - - 'designations': designations, + + 'designations':designations, 'file': file, 'track': track, - 'notifications': request.user.notifications.all() } return render(request, 'filetracking/forward.html', context) -def get_designations_view(request, username): - designations = get_designations(username) - return JsonResponse(designations, safe=False) - -def unarchive_file(request, id): - try: - file = get_object_or_404(File, id=id) - file.is_read = False - file.save() - messages.success(request, 'File unarchived') - except File.DoesNotExist: - messages.error(request, 'File does not exist') - except Exception as e: - messages.error(request, 'Unable to unarchive: {}'.format(str(e))) - - return render(request, 'filetracking/archive.html') - -@login_required(login_url="/accounts/login") -@user_is_student -@dropdown_designation_valid -def edit_draft_view(request, id, *args, **kwargs): - """ - The function is used to edit and send drafted files, and also alter their title and subject - along with their remarks and attachments - - @param: - request - trivial. - id - id of the file object which the user intends to forward to other employee. - - @variables: - file - The File object. - track - The Tracking object. - remarks = Remarks posted by user. - receiver = Receiver to be selected by user for forwarding file. - receiver_id = Receiver_id who has been selected for forwarding file. - upload_file = File attached by user. - extrainfo = ExtraInfo object. - holdsdesignations = HoldsDesignation objects. - context - Holds data needed to make necessary changes in the template. - """ - - file = get_object_or_404(File, id=id) - track = Tracking.objects.select_related('file_id__uploader__user', 'file_id__uploader__department', 'file_id__designation', 'current_id__user', 'current_id__department', - 'current_design__user', 'current_design__working', 'current_design__designation', 'receiver_id', 'receive_design').filter(file_id=file).order_by('receive_date') - - if request.method == "POST": - if 'send' in request.POST: - current_id = request.user.extrainfo - remarks = request.POST.get('remarks') - subject = request.POST.get('subject') - description = request.POST.get('description') - - file.subject = subject - file.description = description - file.save() - track.update(is_read=True) - - sender = request.POST.get('sender') - current_design = HoldsDesignation.objects.select_related( - 'user', 'working', 'designation').get(id=sender) - - receiver = request.POST.get('receiver') - try: - receiver_id = User.objects.get(username=receiver) - except Exception as e: - messages.error(request, 'Enter a valid destination') - designations = HoldsDesignation.objects.select_related( - 'user', 'working', 'designation').filter(user=request.user) - - context = { - - 'designations': designations, - 'file': file, - 'track': track, - 'notifications': request.user.notifications.all() - } - return redirect(reverse('filetracking:filetracking')) - - receive = request.POST.get('receive') - try: - receive_design = Designation.objects.get(name=receive) - except Exception as e: - messages.error(request, 'Enter a valid Designation') - designations = get_designation(request.user) - - context = { - - 'designations': designations, - 'file': file, - 'notifications': request.user.notifications.all() - } - return redirect(reverse('filetracking:filetracking')) - - upload_file = request.FILES.get('myfile') - - if upload_file is None and file.upload_file is not None: - upload_file = file.upload_file - - # since frontend isnt reflecting uploaded file in edit draft, but upload_file may exist in File - # (this feature isnt working atm, duplicate is still stored) - #if upload_file == file.upload_file: - # upload_file = None - - Tracking.objects.create( - file_id=file, - current_id=current_id, - current_design=current_design, - receive_design=receive_design, - receiver_id=receiver_id, - remarks=remarks, - upload_file=upload_file, - ) - - file_tracking_notif(request.user, receiver_id, subject) - messages.success(request, 'File sent successfully') - return render(request, 'filetracking/composefile.html') - - designations = get_designation(request.user) - - designation_name = request.session.get('currentDesignationSelected', 'default_value') - all_available_designations = request.session.get( - 'allDesignations', 'default_value2') - - - username = request.user - designation_id = get_HoldsDesignation_obj( - username, designation_name).id - - remarks = None - receive = None - receiver = None - - if file.file_extra_JSON and file.file_extra_JSON['remarks']: - remarks = file.file_extra_JSON['remarks'] - - context = { - 'designations': designations, - 'file': file, - 'track': track, - 'designation_name': designation_name, - 'designation_id': designation_id, - 'remarks' : remarks, - 'notifications': request.user.notifications.all() - } - - return render(request, 'filetracking/editdraft.html', context) - - -@login_required(login_url="/accounts/login/") -@user_is_student -@dropdown_designation_valid -@require_POST -def download_file(request, id): - file = get_object_or_404(File, id=id) - track = Tracking.objects.select_related('file_id__uploader__user', 'file_id__uploader__department', 'file_id__designation', 'current_id__user', 'current_id__department', - 'current_design__user', 'current_design__working', 'current_design__designation', 'receiver_id', 'receive_design').filter(file_id=id).order_by('receive_date') - - buffer = io.BytesIO() - doc = SimpleDocTemplate(buffer, pagesize=letter) - elements = [] - styles = getSampleStyleSheet() - style_heading = styles['Heading1'] - style_paragraph = styles['BodyText'] - - elements.append( - Paragraph(f"
Subject - {file.subject}
", style_heading)) - elements.append(Spacer(1, 12)) - elements.append( - Paragraph(f"Description: {file.description}", style_paragraph)) - elements.append(Spacer(1, 12)) - - for t in track: - sent_by = f"Sent by: {t.current_design} - {t.forward_date.strftime('%B %d, %Y %I:%M %p')}" - received_by = f"Received by: {t.receiver_id} - {t.receive_design}" - combined_info = f"{sent_by}       {received_by}" - elements.append(Paragraph(combined_info, style_paragraph)) - elements.append(Spacer(1, 12)) - remarks = f"Remarks: {t.remarks}" if t.remarks else "Remarks: No Remarks" - elements.append(Paragraph(remarks, style_paragraph)) - elements.append(Spacer(1, 12)) - attachment = f"Attachment: {os.path.basename(t.upload_file.name)}" if t.upload_file else "Attachment: No attachments" - elements.append(Paragraph(attachment, style_paragraph)) - elements.append(Paragraph('
', style_paragraph)) - elements.append(Spacer(2, 12)) - - doc.build(elements) - pdf_data = buffer.getvalue() - buffer.close() - - formal_filename = f'{file.uploader.department.name}-{file.upload_date.year}-{file.upload_date.month}-#{file.id}' - output_filename = f'iiitdmj-fts-{formal_filename}' - - zip_buffer = io.BytesIO() - with zipfile.ZipFile(zip_buffer, 'w') as zip_file: - pdf_filename = f'{file.uploader.department.name}-{file.upload_date.year}-{file.upload_date.month}-#{file.id}-notesheet.pdf' - zip_file.writestr(output_filename+'.pdf', pdf_data) - for t in track: - if t.upload_file: - zip_file.write(t.upload_file.path, - os.path.basename(t.upload_file.name)) - - zip_data = zip_buffer.getvalue() - zip_buffer.close() - - response = HttpResponse(zip_data, content_type='application/zip') - response['Content-Disposition'] = f'attachment; filename="{output_filename}.zip"' - - return response diff --git a/FusionIIIT/applications/finance_accounts/migrations/0001_initial.py b/FusionIIIT/applications/finance_accounts/migrations/0001_initial.py index 7bcb45aba..4dfa8fed7 100644 --- a/FusionIIIT/applications/finance_accounts/migrations/0001_initial.py +++ b/FusionIIIT/applications/finance_accounts/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 3.1.5 on 2024-07-16 15:44 +# Generated by Django 3.1.5 on 2023-03-15 18:53 from django.db import migrations, models diff --git a/FusionIIIT/applications/globals/__init__.py b/FusionIIIT/applications/globals/__init__.py new file mode 100644 index 000000000..61af46687 --- /dev/null +++ b/FusionIIIT/applications/globals/__init__.py @@ -0,0 +1 @@ +default_app_config = "applications.globals.apps.GlobalsConfig" diff --git a/FusionIIIT/applications/globals/admin.py b/FusionIIIT/applications/globals/admin.py index ba595e235..23ae9f27d 100644 --- a/FusionIIIT/applications/globals/admin.py +++ b/FusionIIIT/applications/globals/admin.py @@ -1,7 +1,7 @@ from django.contrib import admin from .models import (DepartmentInfo, Designation, ExtraInfo, Faculty, Feedback, - HoldsDesignation, Issue, IssueImage, Staff,ModuleAccess) + HoldsDesignation, Issue, IssueImage, ModuleAccess, Staff) # Register your models here. diff --git a/FusionIIIT/applications/globals/api/serializers.py b/FusionIIIT/applications/globals/api/serializers.py index 97028f1ad..b1fcae44b 100644 --- a/FusionIIIT/applications/globals/api/serializers.py +++ b/FusionIIIT/applications/globals/api/serializers.py @@ -15,7 +15,7 @@ User = get_user_model() class UserLoginSerializer(serializers.Serializer): - username = serializers.CharField(max_length=30, required=True) + username = serializers.CharField(required=True) password = serializers.CharField(required=True, write_only=True) @@ -44,6 +44,16 @@ class Meta: class ExtraInfoSerializer(serializers.ModelSerializer): department = DepartmentInfoSerializer() + + def validate_phone_no(self, value): + if value in [None, ""]: + return value + + phone_no = str(value).strip() + if not phone_no.isdigit() or len(phone_no) != 10: + raise serializers.ValidationError("Phone number must contain exactly 10 digits.") + return int(phone_no) + class Meta: model = ExtraInfo fields = ('department','id','title','sex','date_of_birth', diff --git a/FusionIIIT/applications/globals/api/urls.py b/FusionIIIT/applications/globals/api/urls.py index f78aeca97..8a6402364 100644 --- a/FusionIIIT/applications/globals/api/urls.py +++ b/FusionIIIT/applications/globals/api/urls.py @@ -3,23 +3,21 @@ from . import views urlpatterns = [ + url(r'^auth/login/', views.login, name='login-api'), url(r'^auth/logout/', views.logout, name='logout-api'), - url(r'^auth/me', views.auth_view, name='auth-api'), - url(r'^update-role/', views.update_last_selected_role, name='update_last_selected_role'), - - # Profile endpoints + # generic profile endpoint url(r'^profile/(?P.+)/', views.profile, name='profile-api'), + # current user profile url(r'^profile/', views.profile, name='profile-api'), url(r'^profile_update/', views.profile_update, name='update-profile-api'), url(r'^profile_delete/(?P[0-9]+)/', views.profile_delete, name='delete-profile-api'), - # Notification endpoints - url(r'^notification/',views.notification,name='notification'), - url(r'^notificationread',views.NotificationRead,name='notifications-read'), - url(r'^notificationdelete',views.delete_notification,name='notifications-delete'), - url(r'^notificationunread',views.NotificationUnread,name='notifications-unread'), - - # Course management proxy - url(r'^admin_delete_course/(?P\d+)/', views.admin_delete_course_proxy, name='admin_delete_course_proxy') -] \ No newline at end of file + url(r'^dashboard/',views.dashboard,name='dashboard-api'), + url(r'^update-role/', views.update_role, name='update-role-api'), + url(r'^notification/$', views.NotificationList, name='notifications-list'), + url(r'^notificationread$', views.NotificationRead, name='notifications-read'), + url(r'^notificationunread$', views.NotificationUnread, name='notifications-unread'), + url(r'^notificationdelete$', views.NotificationDelete, name='notifications-delete'), + +] diff --git a/FusionIIIT/applications/globals/api/utils.py b/FusionIIIT/applications/globals/api/utils.py index 4b77023cc..5f297d7b1 100644 --- a/FusionIIIT/applications/globals/api/utils.py +++ b/FusionIIIT/applications/globals/api/utils.py @@ -1,9 +1,56 @@ -from django.contrib.auth import authenticate +from django.contrib.auth.backends import ModelBackend +from django.contrib.auth import get_user_model from rest_framework import serializers def get_and_authenticate_user(username, password): - user = authenticate(username=username, password=password) - if user is None: - raise serializers.ValidationError("Invalid credentials.") - return user + username = (username or '').strip() + User = get_user_model() + backend = ModelBackend() + + matched_user = User.objects.filter(username__iexact=username).first() + if matched_user is not None and not matched_user.is_active: + raise serializers.ValidationError("This account is inactive. Please contact the administrator.") + + exact_username_user = User.objects.filter(username=username).first() + if exact_username_user is not None: + user = backend.authenticate( + request=None, + username=exact_username_user.username, + password=password, + ) + if user is not None: + return user + else: + username_matches = list(User.objects.filter(username__iexact=username)[:2]) + if len(username_matches) == 1: + user = backend.authenticate( + request=None, + username=username_matches[0].username, + password=password, + ) + if user is not None: + return user + elif len(username_matches) > 1: + raise serializers.ValidationError( + "Multiple accounts match this username. Please enter the username with the correct letter case." + ) + + email_matches = list(User.objects.filter(email__iexact=username)[:2]) + if len(email_matches) == 1: + matched_user = email_matches[0] + if not matched_user.is_active: + raise serializers.ValidationError("This account is inactive. Please contact the administrator.") + user = backend.authenticate( + request=None, + username=matched_user.username, + password=password, + ) + if user is not None: + return user + elif len(email_matches) > 1: + raise serializers.ValidationError( + "Multiple accounts use this email address. Please sign in with your username instead." + ) + + raise serializers.ValidationError("Invalid credentials.") diff --git a/FusionIIIT/applications/globals/api/views.py b/FusionIIIT/applications/globals/api/views.py index 50b969321..cb4378dd3 100644 --- a/FusionIIIT/applications/globals/api/views.py +++ b/FusionIIIT/applications/globals/api/views.py @@ -1,11 +1,13 @@ from django.contrib.auth import get_user_model +from django.core.cache import cache from applications.academic_information.models import Student from applications.eis.api.views import profile as eis_profile from applications.globals.models import (HoldsDesignation,Designation) from applications.gymkhana.api.views import coordinator_club from applications.placement_cell.models import (Achievement, Course, Education, Experience, Has, Patent, - Project, Publication, Skill) + Project, Publication, Skill, + PlacementProfileAuditLog) from django.shortcuts import get_object_or_404, redirect from rest_framework.permissions import IsAuthenticated @@ -14,14 +16,13 @@ from rest_framework.decorators import api_view, permission_classes,authentication_classes from rest_framework.permissions import AllowAny from rest_framework.response import Response -from django.http import JsonResponse from . import serializers -from applications.globals.models import (ExtraInfo, Feedback, HoldsDesignation, - Issue, IssueImage, DepartmentInfo, ModuleAccess) + from .utils import get_and_authenticate_user from notifications.models import Notification +from notifications.signals import notify User = get_user_model() @@ -32,25 +33,10 @@ def login(request): serializer.is_valid(raise_exception=True) user = get_and_authenticate_user(**serializer.validated_data) data = serializers.AuthUserSerializer(user).data - - desig = list(HoldsDesignation.objects.select_related('user','working','designation').all().filter(working = user).values_list('designation')) - b = [i for sub in desig for i in sub] - design = HoldsDesignation.objects.select_related('user','designation').filter(working=user) - - designation=[] - - if str(user.extrainfo.user_type) == "student": - designation.append(str(user.extrainfo.user_type)) - - for i in design: - if str(i.designation) != str(user.extrainfo.user_type): - designation.append(str(i.designation)) - resp = { 'success' : 'True', 'message' : 'User logged in successfully', - 'token' : data['auth_token'], - 'designations':designation + 'token' : data['auth_token'] } return Response(data=resp, status=status.HTTP_200_OK) @@ -63,15 +49,13 @@ def logout(request): return Response(data=resp, status=status.HTTP_200_OK) @api_view(['GET']) -@permission_classes([AllowAny]) -def auth_view(request): +@permission_classes([IsAuthenticated]) +@authentication_classes([TokenAuthentication]) +def dashboard(request): user=request.user + name = request.user.first_name +"_"+ request.user.last_name - roll_no = request.user.username - extra_info = get_object_or_404(ExtraInfo, user=user) - last_selected_role = extra_info.last_selected_role - designation_list = list(HoldsDesignation.objects.all().filter(working = request.user).values_list('designation')) designation_id = [designation for designations in designation_list for designation in designations] designation_info = [] @@ -79,74 +63,73 @@ def auth_view(request): name_ = get_object_or_404(Designation, id = id) designation_info.append(str(name_.name)) - accessible_modules = {} - - for designation in designation_info: - module_access = ModuleAccess.objects.filter(designation__iexact=designation).first() - if module_access: - filtered_modules = {} - - field_names = [field.name for field in ModuleAccess._meta.get_fields() if field.name not in ['id', 'designation']] - - for field_name in field_names: - filtered_modules[field_name] = getattr(module_access, field_name) - - accessible_modules[designation] = filtered_modules - - resp={ - 'designation_info' : designation_info, - 'name': name, - 'roll_no': roll_no, - 'accessible_modules': accessible_modules, - 'last_selected_role': last_selected_role - } - - return Response(data=resp,status=status.HTTP_200_OK) - -@api_view(['GET']) -@permission_classes([IsAuthenticated]) -@authentication_classes([TokenAuthentication]) -def notification(request): notifications=serializers.NotificationSerializer(request.user.notifications.all(),many=True).data + club_details= coordinator_club(request) resp={ - 'notifications':notifications, + 'notifications':notifications, + 'desgination_info' : designation_info, + 'designation_info' : designation_info, + 'last_selected_role' : cache.get('last_selected_role_{}'.format(user.id)), + 'club_details' : club_details } return Response(data=resp,status=status.HTTP_200_OK) + @api_view(['PATCH']) @permission_classes([IsAuthenticated]) -def update_last_selected_role(request): - new_role = request.data.get('last_selected_role') - - if new_role is None: - return Response({'error': 'last_selected_role is required'}, status=status.HTTP_400_BAD_REQUEST) - - extra_info = get_object_or_404(ExtraInfo, user=request.user) - - extra_info.last_selected_role = new_role - extra_info.save() +@authentication_classes([TokenAuthentication]) +def update_role(request): + selected_role = (request.data.get('last_selected_role') or '').strip() + designation_ids = HoldsDesignation.objects.filter( + working=request.user, + ).values_list('designation_id', flat=True) + allowed_roles = set( + Designation.objects.filter(id__in=designation_ids).values_list('name', flat=True), + ) + + if selected_role not in allowed_roles: + return Response( + {'last_selected_role': ['Selected role is not assigned to this user.']}, + status=status.HTTP_400_BAD_REQUEST, + ) - return Response({'message': 'last_selected_role updated successfully'}, status=status.HTTP_200_OK) + cache.set('last_selected_role_{}'.format(request.user.id), selected_role, None) + return Response( + {'last_selected_role': selected_role}, + status=status.HTTP_200_OK, + ) @api_view(['GET']) +@permission_classes([IsAuthenticated]) +@authentication_classes([TokenAuthentication]) def profile(request, username=None): user = get_object_or_404(User, username=username) if username else request.user + if not hasattr(user, 'extrainfo'): + return Response( + { + 'user': serializers.UserSerializer(user).data, + 'profile': None, + 'current': serializers.HoldsDesignationSerializer( + user.current_designation.all(), + many=True, + ).data, + }, + status=status.HTTP_200_OK, + ) + + user_detail = serializers.UserSerializer(user).data profile = serializers.ExtraInfoSerializer(user.extrainfo).data - + base_response = { + 'user': user_detail, + 'profile': profile, + 'current': serializers.HoldsDesignationSerializer(user.current_designation.all(), many=True).data, + } + if profile['user_type'] == 'student': student = user.extrainfo.student - std_sem = Student.objects.get(id=student.id).curr_semester_no - skills = list( - Has.objects.filter(unique_id_id=student) - .select_related("skill_id") - .values("skill_id__skill", "skill_rating") - ) - formatted_skills = [ - {"skill_name": skill["skill_id__skill"], "skill_rating": skill["skill_rating"]} - for skill in skills - ] + skills = serializers.HasSerializer(student.has_set.all(),many=True).data education = serializers.EducationSerializer(student.education_set.all(), many=True).data course = serializers.CourseSerializer(student.course_set.all(), many=True).data experience = serializers.ExperienceSerializer(student.experience_set.all(), many=True).data @@ -156,9 +139,8 @@ def profile(request, username=None): patent = serializers.PatentSerializer(student.patent_set.all(), many=True).data current = serializers.HoldsDesignationSerializer(user.current_designation.all(), many=True).data resp = { - 'profile' : profile, - 'semester_no' : std_sem, - 'skills' : formatted_skills, + **base_response, + 'skills' : skills, 'education' : education, 'course' : course, 'experience' : experience, @@ -169,93 +151,154 @@ def profile(request, username=None): 'current' : current } return Response(data=resp, status=status.HTTP_200_OK) - else: - return Response(data={'error': 'User is not a student'}, status=status.HTTP_400_BAD_REQUEST) + elif profile['user_type'] == 'faculty': + return Response(data=base_response, status=status.HTTP_200_OK) + + return Response(data=base_response, status=status.HTTP_200_OK) @api_view(['PUT']) +@permission_classes([IsAuthenticated]) +@authentication_classes([TokenAuthentication]) def profile_update(request): user = request.user profile = user.extrainfo current = user.current_designation.filter(designation__name="student") if current: student = profile.student - if 'education' in request.data: - data = request.data - data['education']['unique_id'] = profile - serializer = serializers.EducationSerializer(data=data['education']) + def _update_or_create(serializer_class, payload, *, instance_model=None, profile_field="unique_id"): + payload = dict(payload) + owner = student if profile_field == "unique_id" else profile + payload[profile_field] = owner + instance = None + instance_id = payload.pop("id", None) + if instance_model is not None and instance_id not in [None, ""]: + filter_kwargs = {"id": instance_id} + filter_kwargs[profile_field] = owner + instance = instance_model.objects.filter(**filter_kwargs).first() + if instance is None: + return None, Response({"error": "Record does not exist"}, status=status.HTTP_400_BAD_REQUEST) + serializer = ( + serializer_class(instance, data=payload, partial=True) + if instance + else serializer_class(data=payload) + ) if serializer.is_valid(): serializer.save() - return Response(serializer.data, status=status.HTTP_200_OK) - return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + return serializer, Response(serializer.data, status=status.HTTP_200_OK) + return serializer, Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + if 'education' in request.data: + serializer, response = _update_or_create( + serializers.EducationSerializer, + request.data['education'], + instance_model=Education, + ) + return response elif 'profilesubmit' in request.data: + payload = request.data['profilesubmit'] + required_errors = {} + for field in ['about_me', 'date_of_birth', 'address', 'phone_no']: + if field in payload and payload.get(field) in [None, '', []]: + required_errors[field] = ['This field is required.'] + if required_errors: + return Response(required_errors, status=status.HTTP_400_BAD_REQUEST) serializer = serializers.ExtraInfoSerializer(profile, data=request.data['profilesubmit'],partial=True) if serializer.is_valid(): serializer.save() + PlacementProfileAuditLog.objects.create( + student=student, + actor=request.user, + action='profile_updated', + details={key: value for key, value in payload.items() if key in ['about_me', 'date_of_birth', 'address', 'phone_no']}, + ) + notify.send( + sender=request.user, + recipient=request.user, + verb='Your profile has been updated.', + url='/profile', + module='Placement Cell', + ) return Response(serializer.data, status=status.HTTP_200_OK) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) elif 'skillsubmit' in request.data: - try: - skill_data = request.data['skillsubmit'] - skill_id = skill_data['skill_id'] - skill_name = skill_id['skill_name'] - skill_rating = skill_data['skill_rating'] - - if not skill_name or skill_rating is None: - return Response({"error": "Missing skill_name or skill_rating"}, status=status.HTTP_400_BAD_REQUEST) - - skill, created = Skill.objects.get_or_create(skill=skill_name) - has_obj, created = Has.objects.get_or_create(skill_id=skill, unique_id=student, defaults={"skill_rating": skill_rating}) - if not created: - has_obj.skill_rating = skill_rating - has_obj.save() - - return Response({"message": "Skill added successfully"}, status=status.HTTP_200_OK) - - except Exception as e: - return Response({"error": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) - - elif 'achievementsubmit' in request.data: - request.data['achievementsubmit']['unique_id'] = profile - serializer = serializers.AchievementSerializer(data=request.data['achievementsubmit']) + payload = dict(request.data['skillsubmit']) + skill_instance = None + if payload.get('id') not in [None, '']: + skill_instance = Has.objects.filter(id=payload.get('id'), unique_id=student).first() + if skill_instance is None: + return Response({'error': 'Skill does not exist'}, status=status.HTTP_400_BAD_REQUEST) + payload.pop('id', None) + serializer = ( + serializers.HasSerializer(skill_instance, data=payload, partial=True) + if skill_instance + else serializers.HasSerializer(data=payload) + ) if serializer.is_valid(): - serializer.save() + serializer.save(unique_id=student) + PlacementProfileAuditLog.objects.create( + student=student, + actor=request.user, + action='skill_updated' if skill_instance else 'skill_added', + details={'skill': request.data['skillsubmit'].get('skill_id', {})}, + ) return Response(serializer.data, status=status.HTTP_200_OK) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + elif 'achievementsubmit' in request.data: + serializer, response = _update_or_create( + serializers.AchievementSerializer, + request.data['achievementsubmit'], + instance_model=Achievement, + ) + return response elif 'publicationsubmit' in request.data: - request.data['publicationsubmit']['unique_id'] = profile - serializer = serializers.PublicationSerializer(data=request.data['publicationsubmit']) - if serializer.is_valid(): - serializer.save() - return Response(serializer.data, status=status.HTTP_200_OK) - return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + serializer, response = _update_or_create( + serializers.PublicationSerializer, + request.data['publicationsubmit'], + instance_model=Publication, + ) + return response elif 'patentsubmit' in request.data: - request.data['patentsubmit']['unique_id'] = profile - serializer = serializers.PatentSerializer(data=request.data['patentsubmit']) - if serializer.is_valid(): - serializer.save() - return Response(serializer.data, status=status.HTTP_200_OK) - return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + serializer, response = _update_or_create( + serializers.PatentSerializer, + request.data['patentsubmit'], + instance_model=Patent, + ) + return response elif 'coursesubmit' in request.data: - request.data['coursesubmit']['unique_id'] = profile - serializer = serializers.CourseSerializer(data=request.data['coursesubmit']) - if serializer.is_valid(): - serializer.save() - return Response(serializer.data, status=status.HTTP_200_OK) - return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + serializer, response = _update_or_create( + serializers.CourseSerializer, + request.data['coursesubmit'], + instance_model=Course, + ) + return response elif 'projectsubmit' in request.data: - request.data['projectsubmit']['unique_id'] = profile - serializer = serializers.ProjectSerializer(data=request.data['projectsubmit']) - if serializer.is_valid(): - serializer.save() - return Response(serializer.data, status=status.HTTP_200_OK) - return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + serializer, response = _update_or_create( + serializers.ProjectSerializer, + request.data['projectsubmit'], + instance_model=Project, + ) + if response.status_code == status.HTTP_200_OK: + PlacementProfileAuditLog.objects.create( + student=student, + actor=request.user, + action='project_updated' if request.data['projectsubmit'].get('id') else 'project_added', + details={'project_name': request.data['projectsubmit'].get('project_name')}, + ) + return response elif 'experiencesubmit' in request.data: - request.data['experiencesubmit']['unique_id'] = profile - serializer = serializers.ExperienceSerializer(data=request.data['experiencesubmit']) - if serializer.is_valid(): - serializer.save() - return Response(serializer.data, status=status.HTTP_200_OK) - return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + serializer, response = _update_or_create( + serializers.ExperienceSerializer, + request.data['experiencesubmit'], + instance_model=Experience, + ) + if response.status_code == status.HTTP_200_OK: + PlacementProfileAuditLog.objects.create( + student=student, + actor=request.user, + action='experience_updated' if request.data['experiencesubmit'].get('id') else 'experience_added', + details={'title': request.data['experiencesubmit'].get('title')}, + ) + return response return Response({'error': 'Cannot update'}, status=status.HTTP_400_BAD_REQUEST) @api_view(['DELETE']) @@ -340,111 +383,51 @@ def NotificationRead(request): } return Response(response,status=status.HTTP_404_NOT_FOUND) + +@api_view(['GET']) +@permission_classes([IsAuthenticated]) +@authentication_classes([TokenAuthentication]) +def NotificationList(request): + notifications = request.user.notifications.all().order_by('-timestamp') + serializer = serializers.NotificationSerializer(notifications, many=True) + return Response({'notifications': serializer.data}, status=status.HTTP_200_OK) + + @api_view(['POST']) @permission_classes([IsAuthenticated]) @authentication_classes([TokenAuthentication]) def NotificationUnread(request): try: notifId = int(request.data['id']) - user = request.user - notification = get_object_or_404(Notification, recipient=user, id=notifId) - if not notification.unread: - notification.unread = True - notification.save() + notification = get_object_or_404(Notification, recipient=request.user, id=notifId) + notification.mark_as_unread() response = { - 'message': 'Notification successfully marked as unread.' + 'message': 'notification successfully marked as unseen.' } return Response(response, status=status.HTTP_200_OK) except: response = { - 'error': 'Failed to mark the notification as unread.' + 'error': 'Failed, notification is not marked as unseen.' } return Response(response, status=status.HTTP_404_NOT_FOUND) - -@api_view(['POST']) + + +@api_view(['POST']) @permission_classes([IsAuthenticated]) @authentication_classes([TokenAuthentication]) -def delete_notification(request): +def NotificationDelete(request): try: - notifId = int(request.data['id']) + notifId = int(request.data['id']) notification = get_object_or_404(Notification, recipient=request.user, id=notifId) - notification.deleted = True - notification.save() - + notification.unread = False + notification.save(update_fields=['deleted', 'unread']) response = { - 'message': 'Notification marked as deleted.' + 'message': 'notification deleted successfully.' } return Response(response, status=status.HTTP_200_OK) - except Exception as e: + except: response = { - 'error': 'Failed to mark the notification as deleted.', - 'details': str(e) + 'error': 'Failed to delete notification.' } - return Response(response, status=status.HTTP_400_BAD_REQUEST) - -from django.db import transaction - -@api_view(['DELETE']) -@permission_classes([IsAuthenticated]) -@authentication_classes([TokenAuthentication]) -def admin_delete_course_proxy(request, course_id): - """ - Proxy function to call the actual course delete function from programme_curriculum API - """ - try: - from applications.programme_curriculum.models import Course, CourseSlot, CourseInstructor - - try: - course = Course.objects.get(id=course_id) - except Course.DoesNotExist: - return JsonResponse({ - 'success': False, - 'message': 'Course not found.' - }, status=404) - - course_name = course.name - course_code = course.code - - try: - instructor_count = CourseInstructor.objects.filter(course_id=course).count() - if instructor_count > 0: - return JsonResponse({ - 'success': False, - 'message': f'Cannot delete course. It has {instructor_count} active instructor assignment(s). Please remove instructor assignments first.' - }, status=400) - - slot_count = CourseSlot.objects.filter(courses=course).count() - if slot_count > 0: - return JsonResponse({ - 'success': False, - 'message': f'Cannot delete course. It is assigned to {slot_count} course slot(s) in curriculum(s). Please remove from course slots first.' - }, status=400) - - except Exception as dependency_error: - return JsonResponse({ - 'success': False, - 'message': f'Error checking course dependencies: {str(dependency_error)}' - }, status=500) - - try: - with transaction.atomic(): - course.delete() - - return JsonResponse({ - 'success': True, - 'message': f'Course "{course_name}" has been successfully deleted.' - }, status=200) - - except Exception as delete_error: - return JsonResponse({ - 'success': False, - 'message': f'Error deleting course: {str(delete_error)}' - }, status=500) - - except Exception as e: - return JsonResponse({ - 'success': False, - 'message': 'An unexpected error occurred while deleting the course.', - 'error': str(e) - }, status=500) \ No newline at end of file + return Response(response, status=status.HTTP_404_NOT_FOUND) diff --git a/FusionIIIT/applications/globals/apps.py b/FusionIIIT/applications/globals/apps.py index 074b1bcec..3c0ee442c 100644 --- a/FusionIIIT/applications/globals/apps.py +++ b/FusionIIIT/applications/globals/apps.py @@ -1,5 +1,8 @@ from django.apps import AppConfig -class GlobalsConfig(AppConfig): - name = 'applications.globals' +class GlobalsConfig(AppConfig): + name = 'applications.globals' + + def ready(self): + import applications.globals.signals # noqa: F401 diff --git a/FusionIIIT/applications/globals/migrations/0001_initial.py b/FusionIIIT/applications/globals/migrations/0001_initial.py index f67ff0834..1fc3a8925 100644 --- a/FusionIIIT/applications/globals/migrations/0001_initial.py +++ b/FusionIIIT/applications/globals/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 3.1.5 on 2024-07-16 15:44 +# Generated by Django 3.1.5 on 2023-03-15 18:53 import applications.globals.models import datetime @@ -39,7 +39,7 @@ class Migration(migrations.Migration): ('title', models.CharField(choices=[('Mr.', 'Mr.'), ('Mrs.', 'Mrs.'), ('Ms.', 'Ms.'), ('Dr.', 'Dr.'), ('Professor', 'Prof.'), ('Shreemati', 'Shreemati'), ('Shree', 'Shree')], default='Dr.', max_length=20)), ('sex', models.CharField(choices=[('M', 'Male'), ('F', 'Female'), ('O', 'Other')], default='M', max_length=2)), ('date_of_birth', models.DateField(default=datetime.date(1970, 1, 1))), - ('user_status', models.CharField(choices=[('NEW', 'NEW'), ('PRESENT', 'PRESENT')], default='PRESENT', max_length=50)), + ('user_status', models.CharField(choices=[('PRESENT', 'PRESENT'), ('NEW', 'NEW')], default='PRESENT', max_length=50)), ('address', models.TextField(default='', max_length=1000)), ('phone_no', models.BigIntegerField(default=9999999999, null=True)), ('user_type', models.CharField(choices=[('student', 'student'), ('staff', 'staff'), ('compounder', 'compounder'), ('faculty', 'faculty')], max_length=20)), @@ -109,4 +109,4 @@ class Migration(migrations.Migration): 'unique_together': {('user', 'designation'), ('working', 'designation')}, }, ), - ] \ No newline at end of file + ] diff --git a/FusionIIIT/applications/globals/migrations/0002_moduleaccess.py b/FusionIIIT/applications/globals/migrations/0002_moduleaccess.py index 75fd77a4f..fb6c90e80 100644 --- a/FusionIIIT/applications/globals/migrations/0002_moduleaccess.py +++ b/FusionIIIT/applications/globals/migrations/0002_moduleaccess.py @@ -1,39 +1,38 @@ -# Generated by Django 3.1.5 on 2024-10-20 11:26 - from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('globals', '0001_initial'), + ("globals", "0001_initial"), ] operations = [ migrations.CreateModel( - name='ModuleAccess', + name="ModuleAccess", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('designation', models.CharField(max_length=155)), - ('program_and_curriculum', models.BooleanField(default=False)), - ('course_registration', models.BooleanField(default=False)), - ('course_management', models.BooleanField(default=False)), - ('other_academics', models.BooleanField(default=False)), - ('spacs', models.BooleanField(default=False)), - ('department', models.BooleanField(default=False)), - ('examinations', models.BooleanField(default=False)), - ('hr', models.BooleanField(default=False)), - ('iwd', models.BooleanField(default=False)), - ('complaint_management', models.BooleanField(default=False)), - ('fts', models.BooleanField(default=False)), - ('purchase_and_store', models.BooleanField(default=False)), - ('rspc', models.BooleanField(default=False)), - ('hostel_management', models.BooleanField(default=False)), - ('mess_management', models.BooleanField(default=False)), - ('gymkhana', models.BooleanField(default=False)), - ('placement_cell', models.BooleanField(default=False)), - ('visitor_hostel', models.BooleanField(default=False)), - ('phc', models.BooleanField(default=False)), + ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ("designation", models.CharField(max_length=155)), + ("program_and_curriculum", models.BooleanField(default=False)), + ("course_registration", models.BooleanField(default=False)), + ("course_management", models.BooleanField(default=False)), + ("other_academics", models.BooleanField(default=False)), + ("spacs", models.BooleanField(default=False)), + ("department", models.BooleanField(default=False)), + ("database", models.BooleanField(default=False)), + ("examinations", models.BooleanField(default=False)), + ("hr", models.BooleanField(default=False)), + ("iwd", models.BooleanField(default=False)), + ("complaint_management", models.BooleanField(default=False)), + ("fts", models.BooleanField(default=False)), + ("purchase_and_store", models.BooleanField(default=False)), + ("rspc", models.BooleanField(default=False)), + ("hostel_management", models.BooleanField(default=False)), + ("mess_management", models.BooleanField(default=False)), + ("gymkhana", models.BooleanField(default=False)), + ("placement_cell", models.BooleanField(default=False)), + ("visitor_hostel", models.BooleanField(default=False)), + ("phc", models.BooleanField(default=False)), ], ), ] diff --git a/FusionIIIT/applications/globals/migrations/0003_passwordresettracker.py b/FusionIIIT/applications/globals/migrations/0003_passwordresettracker.py deleted file mode 100644 index 3deabb5ed..000000000 --- a/FusionIIIT/applications/globals/migrations/0003_passwordresettracker.py +++ /dev/null @@ -1,21 +0,0 @@ -# Generated by Django 3.1.5 on 2025-01-01 15:10 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('globals', '0002_moduleaccess'), - ] - - operations = [ - migrations.CreateModel( - name='PasswordResetTracker', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('email', models.EmailField(max_length=254, unique=True)), - ('last_reset', models.DateTimeField(blank=True, null=True)), - ], - ), - ] diff --git a/FusionIIIT/applications/globals/migrations/0004_extrainfo_last_selected_role.py b/FusionIIIT/applications/globals/migrations/0004_extrainfo_last_selected_role.py deleted file mode 100644 index 5450ee4f7..000000000 --- a/FusionIIIT/applications/globals/migrations/0004_extrainfo_last_selected_role.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.1.5 on 2025-02-01 22:39 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('globals', '0003_passwordresettracker'), - ] - - operations = [ - migrations.AddField( - model_name='extrainfo', - name='last_selected_role', - field=models.CharField(blank=True, max_length=20, null=True), - ), - ] diff --git a/FusionIIIT/applications/globals/migrations/0005_moduleaccess_database.py b/FusionIIIT/applications/globals/migrations/0005_moduleaccess_database.py deleted file mode 100644 index de02426c4..000000000 --- a/FusionIIIT/applications/globals/migrations/0005_moduleaccess_database.py +++ /dev/null @@ -1,16 +0,0 @@ -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('globals', '0004_extrainfo_last_selected_role'), - ] - - operations = [ - migrations.AddField( - model_name='moduleaccess', - name='database', - field=models.BooleanField(default=False), - ), - ] diff --git a/FusionIIIT/applications/globals/models.py b/FusionIIIT/applications/globals/models.py index 3e200d39a..e0b71813e 100644 --- a/FusionIIIT/applications/globals/models.py +++ b/FusionIIIT/applications/globals/models.py @@ -64,11 +64,13 @@ class Constants: DESIGNATIONS = ( ('academic', 'Academic Designation'), ('administrative', 'Administrative Designation'), + + ) - USER_STATUS = ( + USER_STATUS = { ("NEW", "NEW"), ("PRESENT", "PRESENT"), - ) + } class Designation(models.Model): @@ -152,7 +154,6 @@ class ExtraInfo(models.Model): null=True, blank=True, upload_to='globals/profile_pictures') about_me = models.TextField(default='NA', max_length=1000, blank=True) date_modified = models.DateTimeField('date_updated', blank=True, null=True) - last_selected_role = models.CharField(max_length=20, null=True, blank=True) @property def age(self): @@ -225,8 +226,7 @@ class Faculty(models.Model): def __str__(self): - # return str(self.id) - return f"{self.id} - {self.id.user.first_name} {self.id.user.last_name}" + return str(self.id) """ Feedback and bug report models start""" @@ -314,7 +314,6 @@ class Issue(models.Model): """ End of feedback and bug report models""" - class ModuleAccess(models.Model): designation = models.CharField(max_length=155) program_and_curriculum = models.BooleanField(default=False) @@ -340,11 +339,3 @@ class ModuleAccess(models.Model): def __str__(self): return self.designation - - -class PasswordResetTracker(models.Model): - email = models.EmailField(unique=True) - last_reset = models.DateTimeField(null=True, blank=True) - - def __str__(self): - return self.email diff --git a/FusionIIIT/applications/globals/signals.py b/FusionIIIT/applications/globals/signals.py new file mode 100644 index 000000000..a1ec6c3a8 --- /dev/null +++ b/FusionIIIT/applications/globals/signals.py @@ -0,0 +1,14 @@ +from django.contrib.auth import get_user_model +from django.db.models.signals import post_save +from django.dispatch import receiver + +from applications.globals.utils import ensure_extrainfo + + +User = get_user_model() + + +@receiver(post_save, sender=User) +def create_extrainfo_for_new_user(sender, instance, created, **kwargs): + if created: + ensure_extrainfo(instance) diff --git a/FusionIIIT/applications/globals/static/globals/img/Designer.jpg b/FusionIIIT/applications/globals/static/globals/img/Designer.jpg deleted file mode 100644 index ee8305b77..000000000 Binary files a/FusionIIIT/applications/globals/static/globals/img/Designer.jpg and /dev/null differ diff --git a/FusionIIIT/applications/globals/static/globals/img/Designer.png b/FusionIIIT/applications/globals/static/globals/img/Designer.png deleted file mode 100644 index c8c2bdf00..000000000 Binary files a/FusionIIIT/applications/globals/static/globals/img/Designer.png and /dev/null differ diff --git a/FusionIIIT/applications/globals/static/globals/img/LHTC.jpg b/FusionIIIT/applications/globals/static/globals/img/LHTC.jpg deleted file mode 100644 index 7cc2a7731..000000000 Binary files a/FusionIIIT/applications/globals/static/globals/img/LHTC.jpg and /dev/null differ diff --git a/FusionIIIT/applications/globals/static/globals/img/fusion.png b/FusionIIIT/applications/globals/static/globals/img/fusion.png deleted file mode 100644 index 3de95db63..000000000 Binary files a/FusionIIIT/applications/globals/static/globals/img/fusion.png and /dev/null differ diff --git a/FusionIIIT/applications/globals/static/globals/img/fusion_icon.png b/FusionIIIT/applications/globals/static/globals/img/fusion_icon.png deleted file mode 100644 index 195e9ded8..000000000 Binary files a/FusionIIIT/applications/globals/static/globals/img/fusion_icon.png and /dev/null differ diff --git a/FusionIIIT/applications/globals/static/globals/js/jquery-3.0.0.min.js b/FusionIIIT/applications/globals/static/globals/js/jquery-3.0.0.min.js deleted file mode 100644 index 62d410d95..000000000 --- a/FusionIIIT/applications/globals/static/globals/js/jquery-3.0.0.min.js +++ /dev/null @@ -1,4 +0,0 @@ -/*! jQuery v3.0.0 | (c) jQuery Foundation | jquery.org/license */ -!function(a,b){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){"use strict";var c=[],d=a.document,e=Object.getPrototypeOf,f=c.slice,g=c.concat,h=c.push,i=c.indexOf,j={},k=j.toString,l=j.hasOwnProperty,m=l.toString,n=m.call(Object),o={};function p(a,b){b=b||d;var c=b.createElement("script");c.text=a,b.head.appendChild(c).parentNode.removeChild(c)}var q="3.0.0",r=function(a,b){return new r.fn.init(a,b)},s=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,t=/^-ms-/,u=/-([a-z])/g,v=function(a,b){return b.toUpperCase()};r.fn=r.prototype={jquery:q,constructor:r,length:0,toArray:function(){return f.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:f.call(this)},pushStack:function(a){var b=r.merge(this.constructor(),a);return b.prevObject=this,b},each:function(a){return r.each(this,a)},map:function(a){return this.pushStack(r.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(f.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor()},push:h,sort:c.sort,splice:c.splice},r.extend=r.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||r.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(a=arguments[h]))for(b in a)c=g[b],d=a[b],g!==d&&(j&&d&&(r.isPlainObject(d)||(e=r.isArray(d)))?(e?(e=!1,f=c&&r.isArray(c)?c:[]):f=c&&r.isPlainObject(c)?c:{},g[b]=r.extend(j,f,d)):void 0!==d&&(g[b]=d));return g},r.extend({expando:"jQuery"+(q+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===r.type(a)},isArray:Array.isArray,isWindow:function(a){return null!=a&&a===a.window},isNumeric:function(a){var b=r.type(a);return("number"===b||"string"===b)&&!isNaN(a-parseFloat(a))},isPlainObject:function(a){var b,c;return a&&"[object Object]"===k.call(a)?(b=e(a))?(c=l.call(b,"constructor")&&b.constructor,"function"==typeof c&&m.call(c)===n):!0:!1},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?j[k.call(a)]||"object":typeof a},globalEval:function(a){p(a)},camelCase:function(a){return a.replace(t,"ms-").replace(u,v)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b){var c,d=0;if(w(a)){for(c=a.length;c>d;d++)if(b.call(a[d],d,a[d])===!1)break}else for(d in a)if(b.call(a[d],d,a[d])===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(s,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(w(Object(a))?r.merge(c,"string"==typeof a?[a]:a):h.call(c,a)),c},inArray:function(a,b,c){return null==b?-1:i.call(b,a,c)},merge:function(a,b){for(var c=+b.length,d=0,e=a.length;c>d;d++)a[e++]=b[d];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,e,f=0,h=[];if(w(a))for(d=a.length;d>f;f++)e=b(a[f],f,c),null!=e&&h.push(e);else for(f in a)e=b(a[f],f,c),null!=e&&h.push(e);return g.apply([],h)},guid:1,proxy:function(a,b){var c,d,e;return"string"==typeof b&&(c=a[b],b=a,a=c),r.isFunction(a)?(d=f.call(arguments,2),e=function(){return a.apply(b||this,d.concat(f.call(arguments)))},e.guid=a.guid=a.guid||r.guid++,e):void 0},now:Date.now,support:o}),"function"==typeof Symbol&&(r.fn[Symbol.iterator]=c[Symbol.iterator]),r.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(a,b){j["[object "+b+"]"]=b.toLowerCase()});function w(a){var b=!!a&&"length"in a&&a.length,c=r.type(a);return"function"===c||r.isWindow(a)?!1:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var x=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C={}.hasOwnProperty,D=[],E=D.pop,F=D.push,G=D.push,H=D.slice,I=function(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return c;return-1},J="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",K="[\\x20\\t\\r\\n\\f]",L="(?:\\\\.|[\\w-]|[^\x00-\\xa0])+",M="\\["+K+"*("+L+")(?:"+K+"*([*^$|!~]?=)"+K+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+L+"))|)"+K+"*\\]",N=":("+L+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+M+")*)|.*)\\)|)",O=new RegExp(K+"+","g"),P=new RegExp("^"+K+"+|((?:^|[^\\\\])(?:\\\\.)*)"+K+"+$","g"),Q=new RegExp("^"+K+"*,"+K+"*"),R=new RegExp("^"+K+"*([>+~]|"+K+")"+K+"*"),S=new RegExp("="+K+"*([^\\]'\"]*?)"+K+"*\\]","g"),T=new RegExp(N),U=new RegExp("^"+L+"$"),V={ID:new RegExp("^#("+L+")"),CLASS:new RegExp("^\\.("+L+")"),TAG:new RegExp("^("+L+"|[*])"),ATTR:new RegExp("^"+M),PSEUDO:new RegExp("^"+N),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+K+"*(even|odd|(([+-]|)(\\d*)n|)"+K+"*(?:([+-]|)"+K+"*(\\d+)|))"+K+"*\\)|)","i"),bool:new RegExp("^(?:"+J+")$","i"),needsContext:new RegExp("^"+K+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+K+"*((?:-\\d)?\\d*)"+K+"*\\)|)(?=[^-]|$)","i")},W=/^(?:input|select|textarea|button)$/i,X=/^h\d$/i,Y=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,$=/[+~]/,_=new RegExp("\\\\([\\da-f]{1,6}"+K+"?|("+K+")|.)","ig"),aa=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},ba=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\x80-\uFFFF\w-]/g,ca=function(a,b){return b?"\x00"===a?"\ufffd":a.slice(0,-1)+"\\"+a.charCodeAt(a.length-1).toString(16)+" ":"\\"+a},da=function(){m()},ea=ta(function(a){return a.disabled===!0},{dir:"parentNode",next:"legend"});try{G.apply(D=H.call(v.childNodes),v.childNodes),D[v.childNodes.length].nodeType}catch(fa){G={apply:D.length?function(a,b){F.apply(a,H.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function ga(a,b,d,e){var f,h,j,k,l,o,r,s=b&&b.ownerDocument,w=b?b.nodeType:9;if(d=d||[],"string"!=typeof a||!a||1!==w&&9!==w&&11!==w)return d;if(!e&&((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,p)){if(11!==w&&(l=Z.exec(a)))if(f=l[1]){if(9===w){if(!(j=b.getElementById(f)))return d;if(j.id===f)return d.push(j),d}else if(s&&(j=s.getElementById(f))&&t(b,j)&&j.id===f)return d.push(j),d}else{if(l[2])return G.apply(d,b.getElementsByTagName(a)),d;if((f=l[3])&&c.getElementsByClassName&&b.getElementsByClassName)return G.apply(d,b.getElementsByClassName(f)),d}if(c.qsa&&!A[a+" "]&&(!q||!q.test(a))){if(1!==w)s=b,r=a;else if("object"!==b.nodeName.toLowerCase()){(k=b.getAttribute("id"))?k=k.replace(ba,ca):b.setAttribute("id",k=u),o=g(a),h=o.length;while(h--)o[h]="#"+k+" "+sa(o[h]);r=o.join(","),s=$.test(a)&&qa(b.parentNode)||b}if(r)try{return G.apply(d,s.querySelectorAll(r)),d}catch(x){}finally{k===u&&b.removeAttribute("id")}}}return i(a.replace(P,"$1"),b,d,e)}function ha(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ia(a){return a[u]=!0,a}function ja(a){var b=n.createElement("fieldset");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ka(a,b){var c=a.split("|"),e=c.length;while(e--)d.attrHandle[c[e]]=b}function la(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&a.sourceIndex-b.sourceIndex;if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function na(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function oa(a){return function(b){return"label"in b&&b.disabled===a||"form"in b&&b.disabled===a||"form"in b&&b.disabled===!1&&(b.isDisabled===a||b.isDisabled!==!a&&("label"in b||!ea(b))!==a)}}function pa(a){return ia(function(b){return b=+b,ia(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function qa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=ga.support={},f=ga.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=ga.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=n.documentElement,p=!f(n),v!==n&&(e=n.defaultView)&&e.top!==e&&(e.addEventListener?e.addEventListener("unload",da,!1):e.attachEvent&&e.attachEvent("onunload",da)),c.attributes=ja(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ja(function(a){return a.appendChild(n.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=Y.test(n.getElementsByClassName),c.getById=ja(function(a){return o.appendChild(a).id=u,!n.getElementsByName||!n.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c?[c]:[]}},d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return"undefined"!=typeof b.getElementsByClassName&&p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=Y.test(n.querySelectorAll))&&(ja(function(a){o.appendChild(a).innerHTML="",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+K+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+K+"*(?:value|"+J+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ja(function(a){a.innerHTML="";var b=n.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+K+"*[*^$|!~]?="),2!==a.querySelectorAll(":enabled").length&&q.push(":enabled",":disabled"),o.appendChild(a).disabled=!0,2!==a.querySelectorAll(":disabled").length&&q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=Y.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ja(function(a){c.disconnectedMatch=s.call(a,"*"),s.call(a,"[s!='']:x"),r.push("!=",N)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=Y.test(o.compareDocumentPosition),t=b||Y.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===n||a.ownerDocument===v&&t(v,a)?-1:b===n||b.ownerDocument===v&&t(v,b)?1:k?I(k,a)-I(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,g=[a],h=[b];if(!e||!f)return a===n?-1:b===n?1:e?-1:f?1:k?I(k,a)-I(k,b):0;if(e===f)return la(a,b);c=a;while(c=c.parentNode)g.unshift(c);c=b;while(c=c.parentNode)h.unshift(c);while(g[d]===h[d])d++;return d?la(g[d],h[d]):g[d]===v?-1:h[d]===v?1:0},n):n},ga.matches=function(a,b){return ga(a,null,null,b)},ga.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(S,"='$1']"),c.matchesSelector&&p&&!A[b+" "]&&(!r||!r.test(b))&&(!q||!q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return ga(b,n,null,[a]).length>0},ga.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},ga.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&C.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},ga.escape=function(a){return(a+"").replace(ba,ca)},ga.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},ga.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=ga.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=ga.selectors={cacheLength:50,createPseudo:ia,match:V,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(_,aa),a[3]=(a[3]||a[4]||a[5]||"").replace(_,aa),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||ga.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&ga.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return V.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&T.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(_,aa).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+K+")"+a+"("+K+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=ga.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(O," ")+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h,t=!1;if(q){if(f){while(p){m=b;while(m=m[p])if(h?m.nodeName.toLowerCase()===r:1===m.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){m=q,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n&&j[2],m=n&&q.childNodes[n];while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if(1===m.nodeType&&++t&&m===b){k[a]=[w,n,t];break}}else if(s&&(m=b,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n),t===!1)while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if((h?m.nodeName.toLowerCase()===r:1===m.nodeType)&&++t&&(s&&(l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),k[a]=[w,t]),m===b))break;return t-=e,t===d||t%d===0&&t/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||ga.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ia(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=I(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ia(function(a){var b=[],c=[],d=h(a.replace(P,"$1"));return d[u]?ia(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ia(function(a){return function(b){return ga(a,b).length>0}}),contains:ia(function(a){return a=a.replace(_,aa),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ia(function(a){return U.test(a||"")||ga.error("unsupported lang: "+a),a=a.replace(_,aa).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:oa(!1),disabled:oa(!0),checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return X.test(a.nodeName)},input:function(a){return W.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:pa(function(){return[0]}),last:pa(function(a,b){return[b-1]}),eq:pa(function(a,b,c){return[0>c?c+b:c]}),even:pa(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:pa(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:pa(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:pa(function(a,b,c){for(var d=0>c?c+b:c;++db;b++)d+=a[b].value;return d}function ta(a,b,c){var d=b.dir,e=b.next,f=e||d,g=c&&"parentNode"===f,h=x++;return b.first?function(b,c,e){while(b=b[d])if(1===b.nodeType||g)return a(b,c,e)}:function(b,c,i){var j,k,l,m=[w,h];if(i){while(b=b[d])if((1===b.nodeType||g)&&a(b,c,i))return!0}else while(b=b[d])if(1===b.nodeType||g)if(l=b[u]||(b[u]={}),k=l[b.uniqueID]||(l[b.uniqueID]={}),e&&e===b.nodeName.toLowerCase())b=b[d]||b;else{if((j=k[f])&&j[0]===w&&j[1]===h)return m[2]=j[2];if(k[f]=m,m[2]=a(b,c,i))return!0}}}function ua(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function va(a,b,c){for(var d=0,e=b.length;e>d;d++)ga(a,b[d],c);return c}function wa(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(c&&!c(f,d,e)||(g.push(f),j&&b.push(h)));return g}function xa(a,b,c,d,e,f){return d&&!d[u]&&(d=xa(d)),e&&!e[u]&&(e=xa(e,f)),ia(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||va(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:wa(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=wa(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?I(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=wa(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):G.apply(g,r)})}function ya(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=ta(function(a){return a===b},h,!0),l=ta(function(a){return I(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];f>i;i++)if(c=d.relative[a[i].type])m=[ta(ua(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return xa(i>1&&ua(m),i>1&&sa(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(P,"$1"),c,e>i&&ya(a.slice(i,e)),f>e&&ya(a=a.slice(e)),f>e&&sa(a))}m.push(c)}return ua(m)}function za(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,o,q,r=0,s="0",t=f&&[],u=[],v=j,x=f||e&&d.find.TAG("*",k),y=w+=null==v?1:Math.random()||.1,z=x.length;for(k&&(j=g===n||g||k);s!==z&&null!=(l=x[s]);s++){if(e&&l){o=0,g||l.ownerDocument===n||(m(l),h=!p);while(q=a[o++])if(q(l,g||n,h)){i.push(l);break}k&&(w=y)}c&&((l=!q&&l)&&r--,f&&t.push(l))}if(r+=s,c&&s!==r){o=0;while(q=b[o++])q(t,u,g,h);if(f){if(r>0)while(s--)t[s]||u[s]||(u[s]=E.call(i));u=wa(u)}G.apply(i,u),k&&!f&&u.length>0&&r+b.length>1&&ga.uniqueSort(i)}return k&&(w=y,j=v),t};return c?ia(f):f}return h=ga.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=ya(b[c]),f[u]?d.push(f):e.push(f);f=A(a,za(e,d)),f.selector=a}return f},i=ga.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(_,aa),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=V.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(_,aa),$.test(j[0].type)&&qa(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&sa(j),!a)return G.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,!b||$.test(a)&&qa(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ja(function(a){return 1&a.compareDocumentPosition(n.createElement("fieldset"))}),ja(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||ka("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ja(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ka("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ja(function(a){return null==a.getAttribute("disabled")})||ka(J,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),ga}(a);r.find=x,r.expr=x.selectors,r.expr[":"]=r.expr.pseudos,r.uniqueSort=r.unique=x.uniqueSort,r.text=x.getText,r.isXMLDoc=x.isXML,r.contains=x.contains,r.escapeSelector=x.escape;var y=function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&r(a).is(c))break;d.push(a)}return d},z=function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c},A=r.expr.match.needsContext,B=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i,C=/^.[^:#\[\.,]*$/;function D(a,b,c){if(r.isFunction(b))return r.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return r.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(C.test(b))return r.filter(b,a,c);b=r.filter(b,a)}return r.grep(a,function(a){return i.call(b,a)>-1!==c&&1===a.nodeType})}r.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?r.find.matchesSelector(d,a)?[d]:[]:r.find.matches(a,r.grep(b,function(a){return 1===a.nodeType}))},r.fn.extend({find:function(a){var b,c,d=this.length,e=this;if("string"!=typeof a)return this.pushStack(r(a).filter(function(){for(b=0;d>b;b++)if(r.contains(e[b],this))return!0}));for(c=this.pushStack([]),b=0;d>b;b++)r.find(a,e[b],c);return d>1?r.uniqueSort(c):c},filter:function(a){return this.pushStack(D(this,a||[],!1))},not:function(a){return this.pushStack(D(this,a||[],!0))},is:function(a){return!!D(this,"string"==typeof a&&A.test(a)?r(a):a||[],!1).length}});var E,F=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,G=r.fn.init=function(a,b,c){var e,f;if(!a)return this;if(c=c||E,"string"==typeof a){if(e="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:F.exec(a),!e||!e[1]&&b)return!b||b.jquery?(b||c).find(a):this.constructor(b).find(a);if(e[1]){if(b=b instanceof r?b[0]:b,r.merge(this,r.parseHTML(e[1],b&&b.nodeType?b.ownerDocument||b:d,!0)),B.test(e[1])&&r.isPlainObject(b))for(e in b)r.isFunction(this[e])?this[e](b[e]):this.attr(e,b[e]);return this}return f=d.getElementById(e[2]),f&&(this[0]=f,this.length=1),this}return a.nodeType?(this[0]=a,this.length=1,this):r.isFunction(a)?void 0!==c.ready?c.ready(a):a(r):r.makeArray(a,this)};G.prototype=r.fn,E=r(d);var H=/^(?:parents|prev(?:Until|All))/,I={children:!0,contents:!0,next:!0,prev:!0};r.fn.extend({has:function(a){var b=r(a,this),c=b.length;return this.filter(function(){for(var a=0;c>a;a++)if(r.contains(this,b[a]))return!0})},closest:function(a,b){var c,d=0,e=this.length,f=[],g="string"!=typeof a&&r(a);if(!A.test(a))for(;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&r.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?r.uniqueSort(f):f)},index:function(a){return a?"string"==typeof a?i.call(r(a),this[0]):i.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(r.uniqueSort(r.merge(this.get(),r(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function J(a,b){while((a=a[b])&&1!==a.nodeType);return a}r.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return y(a,"parentNode")},parentsUntil:function(a,b,c){return y(a,"parentNode",c)},next:function(a){return J(a,"nextSibling")},prev:function(a){return J(a,"previousSibling")},nextAll:function(a){return y(a,"nextSibling")},prevAll:function(a){return y(a,"previousSibling")},nextUntil:function(a,b,c){return y(a,"nextSibling",c)},prevUntil:function(a,b,c){return y(a,"previousSibling",c)},siblings:function(a){return z((a.parentNode||{}).firstChild,a)},children:function(a){return z(a.firstChild)},contents:function(a){return a.contentDocument||r.merge([],a.childNodes)}},function(a,b){r.fn[a]=function(c,d){var e=r.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=r.filter(d,e)),this.length>1&&(I[a]||r.uniqueSort(e),H.test(a)&&e.reverse()),this.pushStack(e)}});var K=/\S+/g;function L(a){var b={};return r.each(a.match(K)||[],function(a,c){b[c]=!0}),b}r.Callbacks=function(a){a="string"==typeof a?L(a):r.extend({},a);var b,c,d,e,f=[],g=[],h=-1,i=function(){for(e=a.once,d=b=!0;g.length;h=-1){c=g.shift();while(++h-1)f.splice(c,1),h>=c&&h--}),this},has:function(a){return a?r.inArray(a,f)>-1:f.length>0},empty:function(){return f&&(f=[]),this},disable:function(){return e=g=[],f=c="",this},disabled:function(){return!f},lock:function(){return e=g=[],c||b||(f=c=""),this},locked:function(){return!!e},fireWith:function(a,c){return e||(c=c||[],c=[a,c.slice?c.slice():c],g.push(c),b||i()),this},fire:function(){return j.fireWith(this,arguments),this},fired:function(){return!!d}};return j};function M(a){return a}function N(a){throw a}function O(a,b,c){var d;try{a&&r.isFunction(d=a.promise)?d.call(a).done(b).fail(c):a&&r.isFunction(d=a.then)?d.call(a,b,c):b.call(void 0,a)}catch(a){c.call(void 0,a)}}r.extend({Deferred:function(b){var c=[["notify","progress",r.Callbacks("memory"),r.Callbacks("memory"),2],["resolve","done",r.Callbacks("once memory"),r.Callbacks("once memory"),0,"resolved"],["reject","fail",r.Callbacks("once memory"),r.Callbacks("once memory"),1,"rejected"]],d="pending",e={state:function(){return d},always:function(){return f.done(arguments).fail(arguments),this},"catch":function(a){return e.then(null,a)},pipe:function(){var a=arguments;return r.Deferred(function(b){r.each(c,function(c,d){var e=r.isFunction(a[d[4]])&&a[d[4]];f[d[1]](function(){var a=e&&e.apply(this,arguments);a&&r.isFunction(a.promise)?a.promise().progress(b.notify).done(b.resolve).fail(b.reject):b[d[0]+"With"](this,e?[a]:arguments)})}),a=null}).promise()},then:function(b,d,e){var f=0;function g(b,c,d,e){return function(){var h=this,i=arguments,j=function(){var a,j;if(!(f>b)){if(a=d.apply(h,i),a===c.promise())throw new TypeError("Thenable self-resolution");j=a&&("object"==typeof a||"function"==typeof a)&&a.then,r.isFunction(j)?e?j.call(a,g(f,c,M,e),g(f,c,N,e)):(f++,j.call(a,g(f,c,M,e),g(f,c,N,e),g(f,c,M,c.notifyWith))):(d!==M&&(h=void 0,i=[a]),(e||c.resolveWith)(h,i))}},k=e?j:function(){try{j()}catch(a){r.Deferred.exceptionHook&&r.Deferred.exceptionHook(a,k.stackTrace),b+1>=f&&(d!==N&&(h=void 0,i=[a]),c.rejectWith(h,i))}};b?k():(r.Deferred.getStackHook&&(k.stackTrace=r.Deferred.getStackHook()),a.setTimeout(k))}}return r.Deferred(function(a){c[0][3].add(g(0,a,r.isFunction(e)?e:M,a.notifyWith)),c[1][3].add(g(0,a,r.isFunction(b)?b:M)),c[2][3].add(g(0,a,r.isFunction(d)?d:N))}).promise()},promise:function(a){return null!=a?r.extend(a,e):e}},f={};return r.each(c,function(a,b){var g=b[2],h=b[5];e[b[1]]=g.add,h&&g.add(function(){d=h},c[3-a][2].disable,c[0][2].lock),g.add(b[3].fire),f[b[0]]=function(){return f[b[0]+"With"](this===f?void 0:this,arguments),this},f[b[0]+"With"]=g.fireWith}),e.promise(f),b&&b.call(f,f),f},when:function(a){var b=arguments.length,c=b,d=Array(c),e=f.call(arguments),g=r.Deferred(),h=function(a){return function(c){d[a]=this,e[a]=arguments.length>1?f.call(arguments):c,--b||g.resolveWith(d,e)}};if(1>=b&&(O(a,g.done(h(c)).resolve,g.reject),"pending"===g.state()||r.isFunction(e[c]&&e[c].then)))return g.then();while(c--)O(e[c],h(c),g.reject);return g.promise()}});var P=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;r.Deferred.exceptionHook=function(b,c){a.console&&a.console.warn&&b&&P.test(b.name)&&a.console.warn("jQuery.Deferred exception: "+b.message,b.stack,c)};var Q=r.Deferred();r.fn.ready=function(a){return Q.then(a),this},r.extend({isReady:!1,readyWait:1,holdReady:function(a){a?r.readyWait++:r.ready(!0)},ready:function(a){(a===!0?--r.readyWait:r.isReady)||(r.isReady=!0,a!==!0&&--r.readyWait>0||Q.resolveWith(d,[r]))}}),r.ready.then=Q.then;function R(){d.removeEventListener("DOMContentLoaded",R),a.removeEventListener("load",R),r.ready()}"complete"===d.readyState||"loading"!==d.readyState&&!d.documentElement.doScroll?a.setTimeout(r.ready):(d.addEventListener("DOMContentLoaded",R),a.addEventListener("load",R));var S=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===r.type(c)){e=!0;for(h in c)S(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,r.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){ -return j.call(r(a),c)})),b))for(;i>h;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},T=function(a){return 1===a.nodeType||9===a.nodeType||!+a.nodeType};function U(){this.expando=r.expando+U.uid++}U.uid=1,U.prototype={cache:function(a){var b=a[this.expando];return b||(b={},T(a)&&(a.nodeType?a[this.expando]=b:Object.defineProperty(a,this.expando,{value:b,configurable:!0}))),b},set:function(a,b,c){var d,e=this.cache(a);if("string"==typeof b)e[r.camelCase(b)]=c;else for(d in b)e[r.camelCase(d)]=b[d];return e},get:function(a,b){return void 0===b?this.cache(a):a[this.expando]&&a[this.expando][r.camelCase(b)]},access:function(a,b,c){return void 0===b||b&&"string"==typeof b&&void 0===c?this.get(a,b):(this.set(a,b,c),void 0!==c?c:b)},remove:function(a,b){var c,d=a[this.expando];if(void 0!==d){if(void 0!==b){r.isArray(b)?b=b.map(r.camelCase):(b=r.camelCase(b),b=b in d?[b]:b.match(K)||[]),c=b.length;while(c--)delete d[b[c]]}(void 0===b||r.isEmptyObject(d))&&(a.nodeType?a[this.expando]=void 0:delete a[this.expando])}},hasData:function(a){var b=a[this.expando];return void 0!==b&&!r.isEmptyObject(b)}};var V=new U,W=new U,X=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,Y=/[A-Z]/g;function Z(a,b,c){var d;if(void 0===c&&1===a.nodeType)if(d="data-"+b.replace(Y,"-$&").toLowerCase(),c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:X.test(c)?JSON.parse(c):c}catch(e){}W.set(a,b,c)}else c=void 0;return c}r.extend({hasData:function(a){return W.hasData(a)||V.hasData(a)},data:function(a,b,c){return W.access(a,b,c)},removeData:function(a,b){W.remove(a,b)},_data:function(a,b,c){return V.access(a,b,c)},_removeData:function(a,b){V.remove(a,b)}}),r.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=W.get(f),1===f.nodeType&&!V.get(f,"hasDataAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=r.camelCase(d.slice(5)),Z(f,d,e[d])));V.set(f,"hasDataAttrs",!0)}return e}return"object"==typeof a?this.each(function(){W.set(this,a)}):S(this,function(b){var c;if(f&&void 0===b){if(c=W.get(f,a),void 0!==c)return c;if(c=Z(f,a),void 0!==c)return c}else this.each(function(){W.set(this,a,b)})},null,b,arguments.length>1,null,!0)},removeData:function(a){return this.each(function(){W.remove(this,a)})}}),r.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=V.get(a,b),c&&(!d||r.isArray(c)?d=V.access(a,b,r.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=r.queue(a,b),d=c.length,e=c.shift(),f=r._queueHooks(a,b),g=function(){r.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return V.get(a,c)||V.access(a,c,{empty:r.Callbacks("once memory").add(function(){V.remove(a,[b+"queue",c])})})}}),r.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.lengthf;f++)d=a[f],d.style&&(c=d.style.display,b?("none"===c&&(e[f]=V.get(d,"display")||null,e[f]||(d.style.display="")),""===d.style.display&&ba(d)&&(e[f]=fa(d))):"none"!==c&&(e[f]="none",V.set(d,"display",c)));for(f=0;g>f;f++)null!=e[f]&&(a[f].style.display=e[f]);return a}r.fn.extend({show:function(){return ga(this,!0)},hide:function(){return ga(this)},toggle:function(a){return"boolean"==typeof a?a?this.show():this.hide():this.each(function(){ba(this)?r(this).show():r(this).hide()})}});var ha=/^(?:checkbox|radio)$/i,ia=/<([a-z][^\/\0>\x20\t\r\n\f]+)/i,ja=/^$|\/(?:java|ecma)script/i,ka={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};ka.optgroup=ka.option,ka.tbody=ka.tfoot=ka.colgroup=ka.caption=ka.thead,ka.th=ka.td;function la(a,b){var c="undefined"!=typeof a.getElementsByTagName?a.getElementsByTagName(b||"*"):"undefined"!=typeof a.querySelectorAll?a.querySelectorAll(b||"*"):[];return void 0===b||b&&r.nodeName(a,b)?r.merge([a],c):c}function ma(a,b){for(var c=0,d=a.length;d>c;c++)V.set(a[c],"globalEval",!b||V.get(b[c],"globalEval"))}var na=/<|&#?\w+;/;function oa(a,b,c,d,e){for(var f,g,h,i,j,k,l=b.createDocumentFragment(),m=[],n=0,o=a.length;o>n;n++)if(f=a[n],f||0===f)if("object"===r.type(f))r.merge(m,f.nodeType?[f]:f);else if(na.test(f)){g=g||l.appendChild(b.createElement("div")),h=(ia.exec(f)||["",""])[1].toLowerCase(),i=ka[h]||ka._default,g.innerHTML=i[1]+r.htmlPrefilter(f)+i[2],k=i[0];while(k--)g=g.lastChild;r.merge(m,g.childNodes),g=l.firstChild,g.textContent=""}else m.push(b.createTextNode(f));l.textContent="",n=0;while(f=m[n++])if(d&&r.inArray(f,d)>-1)e&&e.push(f);else if(j=r.contains(f.ownerDocument,f),g=la(l.appendChild(f),"script"),j&&ma(g),c){k=0;while(f=g[k++])ja.test(f.type||"")&&c.push(f)}return l}!function(){var a=d.createDocumentFragment(),b=a.appendChild(d.createElement("div")),c=d.createElement("input");c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),b.appendChild(c),o.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,b.innerHTML="",o.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var pa=d.documentElement,qa=/^key/,ra=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,sa=/^([^.]*)(?:\.(.+)|)/;function ta(){return!0}function ua(){return!1}function va(){try{return d.activeElement}catch(a){}}function wa(a,b,c,d,e,f){var g,h;if("object"==typeof b){"string"!=typeof c&&(d=d||c,c=void 0);for(h in b)wa(a,h,c,d,b[h],f);return a}if(null==d&&null==e?(e=c,d=c=void 0):null==e&&("string"==typeof c?(e=d,d=void 0):(e=d,d=c,c=void 0)),e===!1)e=ua;else if(!e)return a;return 1===f&&(g=e,e=function(a){return r().off(a),g.apply(this,arguments)},e.guid=g.guid||(g.guid=r.guid++)),a.each(function(){r.event.add(this,b,e,d,c)})}r.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=V.get(a);if(q){c.handler&&(f=c,c=f.handler,e=f.selector),e&&r.find.matchesSelector(pa,e),c.guid||(c.guid=r.guid++),(i=q.events)||(i=q.events={}),(g=q.handle)||(g=q.handle=function(b){return"undefined"!=typeof r&&r.event.triggered!==b.type?r.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(K)||[""],j=b.length;while(j--)h=sa.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n&&(l=r.event.special[n]||{},n=(e?l.delegateType:l.bindType)||n,l=r.event.special[n]||{},k=r.extend({type:n,origType:p,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&r.expr.match.needsContext.test(e),namespace:o.join(".")},f),(m=i[n])||(m=i[n]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,o,g)!==!1||a.addEventListener&&a.addEventListener(n,g)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),r.event.global[n]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=V.hasData(a)&&V.get(a);if(q&&(i=q.events)){b=(b||"").match(K)||[""],j=b.length;while(j--)if(h=sa.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n){l=r.event.special[n]||{},n=(d?l.delegateType:l.bindType)||n,m=i[n]||[],h=h[2]&&new RegExp("(^|\\.)"+o.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&p!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,o,q.handle)!==!1||r.removeEvent(a,n,q.handle),delete i[n])}else for(n in i)r.event.remove(a,n+b[j],c,d,!0);r.isEmptyObject(i)&&V.remove(a,"handle events")}},dispatch:function(a){var b=r.event.fix(a),c,d,e,f,g,h,i=new Array(arguments.length),j=(V.get(this,"events")||{})[b.type]||[],k=r.event.special[b.type]||{};for(i[0]=b,c=1;cc;c++)f=b[c],e=f.selector+" ",void 0===d[e]&&(d[e]=f.needsContext?r(e,this).index(i)>-1:r.find(e,this,null,[i]).length),d[e]&&d.push(f);d.length&&g.push({elem:i,handlers:d})}return h\x20\t\r\n\f]*)[^>]*)\/>/gi,ya=/\s*$/g;function Ca(a,b){return r.nodeName(a,"table")&&r.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a:a}function Da(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function Ea(a){var b=Aa.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function Fa(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(V.hasData(a)&&(f=V.access(a),g=V.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;d>c;c++)r.event.add(b,e,j[e][c])}W.hasData(a)&&(h=W.access(a),i=r.extend({},h),W.set(b,i))}}function Ga(a,b){var c=b.nodeName.toLowerCase();"input"===c&&ha.test(a.type)?b.checked=a.checked:"input"!==c&&"textarea"!==c||(b.defaultValue=a.defaultValue)}function Ha(a,b,c,d){b=g.apply([],b);var e,f,h,i,j,k,l=0,m=a.length,n=m-1,q=b[0],s=r.isFunction(q);if(s||m>1&&"string"==typeof q&&!o.checkClone&&za.test(q))return a.each(function(e){var f=a.eq(e);s&&(b[0]=q.call(this,e,f.html())),Ha(f,b,c,d)});if(m&&(e=oa(b,a[0].ownerDocument,!1,a,d),f=e.firstChild,1===e.childNodes.length&&(e=f),f||d)){for(h=r.map(la(e,"script"),Da),i=h.length;m>l;l++)j=e,l!==n&&(j=r.clone(j,!0,!0),i&&r.merge(h,la(j,"script"))),c.call(a[l],j,l);if(i)for(k=h[h.length-1].ownerDocument,r.map(h,Ea),l=0;i>l;l++)j=h[l],ja.test(j.type||"")&&!V.access(j,"globalEval")&&r.contains(k,j)&&(j.src?r._evalUrl&&r._evalUrl(j.src):p(j.textContent.replace(Ba,""),k))}return a}function Ia(a,b,c){for(var d,e=b?r.filter(b,a):a,f=0;null!=(d=e[f]);f++)c||1!==d.nodeType||r.cleanData(la(d)),d.parentNode&&(c&&r.contains(d.ownerDocument,d)&&ma(la(d,"script")),d.parentNode.removeChild(d));return a}r.extend({htmlPrefilter:function(a){return a.replace(xa,"<$1>")},clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=r.contains(a.ownerDocument,a);if(!(o.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||r.isXMLDoc(a)))for(g=la(h),f=la(a),d=0,e=f.length;e>d;d++)Ga(f[d],g[d]);if(b)if(c)for(f=f||la(a),g=g||la(h),d=0,e=f.length;e>d;d++)Fa(f[d],g[d]);else Fa(a,h);return g=la(h,"script"),g.length>0&&ma(g,!i&&la(a,"script")),h},cleanData:function(a){for(var b,c,d,e=r.event.special,f=0;void 0!==(c=a[f]);f++)if(T(c)){if(b=c[V.expando]){if(b.events)for(d in b.events)e[d]?r.event.remove(c,d):r.removeEvent(c,d,b.handle);c[V.expando]=void 0}c[W.expando]&&(c[W.expando]=void 0)}}}),r.fn.extend({detach:function(a){return Ia(this,a,!0)},remove:function(a){return Ia(this,a)},text:function(a){return S(this,function(a){return void 0===a?r.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=a)})},null,a,arguments.length)},append:function(){return Ha(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Ca(this,a);b.appendChild(a)}})},prepend:function(){return Ha(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Ca(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return Ha(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return Ha(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(r.cleanData(la(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return r.clone(this,a,b)})},html:function(a){return S(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!ya.test(a)&&!ka[(ia.exec(a)||["",""])[1].toLowerCase()]){a=r.htmlPrefilter(a);try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(r.cleanData(la(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=[];return Ha(this,arguments,function(b){var c=this.parentNode;r.inArray(this,a)<0&&(r.cleanData(la(this)),c&&c.replaceChild(b,this))},a)}}),r.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){r.fn[a]=function(a){for(var c,d=[],e=r(a),f=e.length-1,g=0;f>=g;g++)c=g===f?this:this.clone(!0),r(e[g])[b](c),h.apply(d,c.get());return this.pushStack(d)}});var Ja=/^margin/,Ka=new RegExp("^("+$+")(?!px)[a-z%]+$","i"),La=function(b){var c=b.ownerDocument.defaultView;return c&&c.opener||(c=a),c.getComputedStyle(b)};!function(){function b(){if(i){i.style.cssText="box-sizing:border-box;position:relative;display:block;margin:auto;border:1px;padding:1px;top:1%;width:50%",i.innerHTML="",pa.appendChild(h);var b=a.getComputedStyle(i);c="1%"!==b.top,g="2px"===b.marginLeft,e="4px"===b.width,i.style.marginRight="50%",f="4px"===b.marginRight,pa.removeChild(h),i=null}}var c,e,f,g,h=d.createElement("div"),i=d.createElement("div");i.style&&(i.style.backgroundClip="content-box",i.cloneNode(!0).style.backgroundClip="",o.clearCloneStyle="content-box"===i.style.backgroundClip,h.style.cssText="border:0;width:8px;height:0;top:0;left:-9999px;padding:0;margin-top:1px;position:absolute",h.appendChild(i),r.extend(o,{pixelPosition:function(){return b(),c},boxSizingReliable:function(){return b(),e},pixelMarginRight:function(){return b(),f},reliableMarginLeft:function(){return b(),g}}))}();function Ma(a,b,c){var d,e,f,g,h=a.style;return c=c||La(a),c&&(g=c.getPropertyValue(b)||c[b],""!==g||r.contains(a.ownerDocument,a)||(g=r.style(a,b)),!o.pixelMarginRight()&&Ka.test(g)&&Ja.test(b)&&(d=h.width,e=h.minWidth,f=h.maxWidth,h.minWidth=h.maxWidth=h.width=g,g=c.width,h.width=d,h.minWidth=e,h.maxWidth=f)),void 0!==g?g+"":g}function Na(a,b){return{get:function(){return a()?void delete this.get:(this.get=b).apply(this,arguments)}}}var Oa=/^(none|table(?!-c[ea]).+)/,Pa={position:"absolute",visibility:"hidden",display:"block"},Qa={letterSpacing:"0",fontWeight:"400"},Ra=["Webkit","Moz","ms"],Sa=d.createElement("div").style;function Ta(a){if(a in Sa)return a;var b=a[0].toUpperCase()+a.slice(1),c=Ra.length;while(c--)if(a=Ra[c]+b,a in Sa)return a}function Ua(a,b,c){var d=_.exec(b);return d?Math.max(0,d[2]-(c||0))+(d[3]||"px"):b}function Va(a,b,c,d,e){for(var f=c===(d?"border":"content")?4:"width"===b?1:0,g=0;4>f;f+=2)"margin"===c&&(g+=r.css(a,c+aa[f],!0,e)),d?("content"===c&&(g-=r.css(a,"padding"+aa[f],!0,e)),"margin"!==c&&(g-=r.css(a,"border"+aa[f]+"Width",!0,e))):(g+=r.css(a,"padding"+aa[f],!0,e),"padding"!==c&&(g+=r.css(a,"border"+aa[f]+"Width",!0,e)));return g}function Wa(a,b,c){var d,e=!0,f=La(a),g="border-box"===r.css(a,"boxSizing",!1,f);if(a.getClientRects().length&&(d=a.getBoundingClientRect()[b]),0>=d||null==d){if(d=Ma(a,b,f),(0>d||null==d)&&(d=a.style[b]),Ka.test(d))return d;e=g&&(o.boxSizingReliable()||d===a.style[b]),d=parseFloat(d)||0}return d+Va(a,b,c||(g?"border":"content"),e,f)+"px"}r.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=Ma(a,"opacity");return""===c?"1":c}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":"cssFloat"},style:function(a,b,c,d){if(a&&3!==a.nodeType&&8!==a.nodeType&&a.style){var e,f,g,h=r.camelCase(b),i=a.style;return b=r.cssProps[h]||(r.cssProps[h]=Ta(h)||h),g=r.cssHooks[b]||r.cssHooks[h],void 0===c?g&&"get"in g&&void 0!==(e=g.get(a,!1,d))?e:i[b]:(f=typeof c,"string"===f&&(e=_.exec(c))&&e[1]&&(c=da(a,b,e),f="number"),null!=c&&c===c&&("number"===f&&(c+=e&&e[3]||(r.cssNumber[h]?"":"px")),o.clearCloneStyle||""!==c||0!==b.indexOf("background")||(i[b]="inherit"),g&&"set"in g&&void 0===(c=g.set(a,c,d))||(i[b]=c)),void 0)}},css:function(a,b,c,d){var e,f,g,h=r.camelCase(b);return b=r.cssProps[h]||(r.cssProps[h]=Ta(h)||h),g=r.cssHooks[b]||r.cssHooks[h],g&&"get"in g&&(e=g.get(a,!0,c)),void 0===e&&(e=Ma(a,b,d)),"normal"===e&&b in Qa&&(e=Qa[b]),""===c||c?(f=parseFloat(e),c===!0||isFinite(f)?f||0:e):e}}),r.each(["height","width"],function(a,b){r.cssHooks[b]={get:function(a,c,d){return c?!Oa.test(r.css(a,"display"))||a.getClientRects().length&&a.getBoundingClientRect().width?Wa(a,b,d):ca(a,Pa,function(){return Wa(a,b,d)}):void 0},set:function(a,c,d){var e,f=d&&La(a),g=d&&Va(a,b,d,"border-box"===r.css(a,"boxSizing",!1,f),f);return g&&(e=_.exec(c))&&"px"!==(e[3]||"px")&&(a.style[b]=c,c=r.css(a,b)),Ua(a,c,g)}}}),r.cssHooks.marginLeft=Na(o.reliableMarginLeft,function(a,b){return b?(parseFloat(Ma(a,"marginLeft"))||a.getBoundingClientRect().left-ca(a,{marginLeft:0},function(){return a.getBoundingClientRect().left}))+"px":void 0}),r.each({margin:"",padding:"",border:"Width"},function(a,b){r.cssHooks[a+b]={expand:function(c){for(var d=0,e={},f="string"==typeof c?c.split(" "):[c];4>d;d++)e[a+aa[d]+b]=f[d]||f[d-2]||f[0];return e}},Ja.test(a)||(r.cssHooks[a+b].set=Ua)}),r.fn.extend({css:function(a,b){return S(this,function(a,b,c){var d,e,f={},g=0;if(r.isArray(b)){for(d=La(a),e=b.length;e>g;g++)f[b[g]]=r.css(a,b[g],!1,d);return f}return void 0!==c?r.style(a,b,c):r.css(a,b)},a,b,arguments.length>1)}});function Xa(a,b,c,d,e){return new Xa.prototype.init(a,b,c,d,e)}r.Tween=Xa,Xa.prototype={constructor:Xa,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||r.easing._default,this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(r.cssNumber[c]?"":"px")},cur:function(){var a=Xa.propHooks[this.prop];return a&&a.get?a.get(this):Xa.propHooks._default.get(this)},run:function(a){var b,c=Xa.propHooks[this.prop];return this.options.duration?this.pos=b=r.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration):this.pos=b=a,this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):Xa.propHooks._default.set(this),this}},Xa.prototype.init.prototype=Xa.prototype,Xa.propHooks={_default:{get:function(a){var b;return 1!==a.elem.nodeType||null!=a.elem[a.prop]&&null==a.elem.style[a.prop]?a.elem[a.prop]:(b=r.css(a.elem,a.prop,""),b&&"auto"!==b?b:0)},set:function(a){r.fx.step[a.prop]?r.fx.step[a.prop](a):1!==a.elem.nodeType||null==a.elem.style[r.cssProps[a.prop]]&&!r.cssHooks[a.prop]?a.elem[a.prop]=a.now:r.style(a.elem,a.prop,a.now+a.unit)}}},Xa.propHooks.scrollTop=Xa.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},r.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2},_default:"swing"},r.fx=Xa.prototype.init,r.fx.step={};var Ya,Za,$a=/^(?:toggle|show|hide)$/,_a=/queueHooks$/;function ab(){Za&&(a.requestAnimationFrame(ab),r.fx.tick())}function bb(){return a.setTimeout(function(){Ya=void 0}),Ya=r.now()}function cb(a,b){var c,d=0,e={height:a};for(b=b?1:0;4>d;d+=2-b)c=aa[d],e["margin"+c]=e["padding"+c]=a;return b&&(e.opacity=e.width=a),e}function db(a,b,c){for(var d,e=(gb.tweeners[b]||[]).concat(gb.tweeners["*"]),f=0,g=e.length;g>f;f++)if(d=e[f].call(c,b,a))return d}function eb(a,b,c){var d,e,f,g,h,i,j,k,l="width"in b||"height"in b,m=this,n={},o=a.style,p=a.nodeType&&ba(a),q=V.get(a,"fxshow");c.queue||(g=r._queueHooks(a,"fx"),null==g.unqueued&&(g.unqueued=0,h=g.empty.fire,g.empty.fire=function(){g.unqueued||h()}),g.unqueued++,m.always(function(){m.always(function(){g.unqueued--,r.queue(a,"fx").length||g.empty.fire()})}));for(d in b)if(e=b[d],$a.test(e)){if(delete b[d],f=f||"toggle"===e,e===(p?"hide":"show")){if("show"!==e||!q||void 0===q[d])continue;p=!0}n[d]=q&&q[d]||r.style(a,d)}if(i=!r.isEmptyObject(b),i||!r.isEmptyObject(n)){l&&1===a.nodeType&&(c.overflow=[o.overflow,o.overflowX,o.overflowY],j=q&&q.display,null==j&&(j=V.get(a,"display")),k=r.css(a,"display"),"none"===k&&(j?k=j:(ga([a],!0),j=a.style.display||j,k=r.css(a,"display"),ga([a]))),("inline"===k||"inline-block"===k&&null!=j)&&"none"===r.css(a,"float")&&(i||(m.done(function(){o.display=j}),null==j&&(k=o.display,j="none"===k?"":k)),o.display="inline-block")),c.overflow&&(o.overflow="hidden",m.always(function(){o.overflow=c.overflow[0],o.overflowX=c.overflow[1],o.overflowY=c.overflow[2]})),i=!1;for(d in n)i||(q?"hidden"in q&&(p=q.hidden):q=V.access(a,"fxshow",{display:j}),f&&(q.hidden=!p),p&&ga([a],!0),m.done(function(){p||ga([a]),V.remove(a,"fxshow");for(d in n)r.style(a,d,n[d])})),i=db(p?q[d]:0,d,m),d in q||(q[d]=i.start,p&&(i.end=i.start,i.start=0))}}function fb(a,b){var c,d,e,f,g;for(c in a)if(d=r.camelCase(c),e=b[d],f=a[c],r.isArray(f)&&(e=f[1],f=a[c]=f[0]),c!==d&&(a[d]=f,delete a[c]),g=r.cssHooks[d],g&&"expand"in g){f=g.expand(f),delete a[d];for(c in f)c in a||(a[c]=f[c],b[c]=e)}else b[d]=e}function gb(a,b,c){var d,e,f=0,g=gb.prefilters.length,h=r.Deferred().always(function(){delete i.elem}),i=function(){if(e)return!1;for(var b=Ya||bb(),c=Math.max(0,j.startTime+j.duration-b),d=c/j.duration||0,f=1-d,g=0,i=j.tweens.length;i>g;g++)j.tweens[g].run(f);return h.notifyWith(a,[j,f,c]),1>f&&i?c:(h.resolveWith(a,[j]),!1)},j=h.promise({elem:a,props:r.extend({},b),opts:r.extend(!0,{specialEasing:{},easing:r.easing._default},c),originalProperties:b,originalOptions:c,startTime:Ya||bb(),duration:c.duration,tweens:[],createTween:function(b,c){var d=r.Tween(a,j.opts,b,c,j.opts.specialEasing[b]||j.opts.easing);return j.tweens.push(d),d},stop:function(b){var c=0,d=b?j.tweens.length:0;if(e)return this;for(e=!0;d>c;c++)j.tweens[c].run(1);return b?(h.notifyWith(a,[j,1,0]),h.resolveWith(a,[j,b])):h.rejectWith(a,[j,b]),this}}),k=j.props;for(fb(k,j.opts.specialEasing);g>f;f++)if(d=gb.prefilters[f].call(j,a,k,j.opts))return r.isFunction(d.stop)&&(r._queueHooks(j.elem,j.opts.queue).stop=r.proxy(d.stop,d)),d;return r.map(k,db,j),r.isFunction(j.opts.start)&&j.opts.start.call(a,j),r.fx.timer(r.extend(i,{elem:a,anim:j,queue:j.opts.queue})),j.progress(j.opts.progress).done(j.opts.done,j.opts.complete).fail(j.opts.fail).always(j.opts.always)}r.Animation=r.extend(gb,{tweeners:{"*":[function(a,b){var c=this.createTween(a,b);return da(c.elem,a,_.exec(b),c),c}]},tweener:function(a,b){r.isFunction(a)?(b=a,a=["*"]):a=a.match(K);for(var c,d=0,e=a.length;e>d;d++)c=a[d],gb.tweeners[c]=gb.tweeners[c]||[],gb.tweeners[c].unshift(b)},prefilters:[eb],prefilter:function(a,b){b?gb.prefilters.unshift(a):gb.prefilters.push(a)}}),r.speed=function(a,b,c){var e=a&&"object"==typeof a?r.extend({},a):{complete:c||!c&&b||r.isFunction(a)&&a,duration:a,easing:c&&b||b&&!r.isFunction(b)&&b};return r.fx.off||d.hidden?e.duration=0:e.duration="number"==typeof e.duration?e.duration:e.duration in r.fx.speeds?r.fx.speeds[e.duration]:r.fx.speeds._default,null!=e.queue&&e.queue!==!0||(e.queue="fx"),e.old=e.complete,e.complete=function(){r.isFunction(e.old)&&e.old.call(this),e.queue&&r.dequeue(this,e.queue)},e},r.fn.extend({fadeTo:function(a,b,c,d){return this.filter(ba).css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){var e=r.isEmptyObject(a),f=r.speed(b,c,d),g=function(){var b=gb(this,r.extend({},a),f);(e||V.get(this,"finish"))&&b.stop(!0)};return g.finish=g,e||f.queue===!1?this.each(g):this.queue(f.queue,g)},stop:function(a,b,c){var d=function(a){var b=a.stop;delete a.stop,b(c)};return"string"!=typeof a&&(c=b,b=a,a=void 0),b&&a!==!1&&this.queue(a||"fx",[]),this.each(function(){var b=!0,e=null!=a&&a+"queueHooks",f=r.timers,g=V.get(this);if(e)g[e]&&g[e].stop&&d(g[e]);else for(e in g)g[e]&&g[e].stop&&_a.test(e)&&d(g[e]);for(e=f.length;e--;)f[e].elem!==this||null!=a&&f[e].queue!==a||(f[e].anim.stop(c),b=!1,f.splice(e,1));!b&&c||r.dequeue(this,a)})},finish:function(a){return a!==!1&&(a=a||"fx"),this.each(function(){var b,c=V.get(this),d=c[a+"queue"],e=c[a+"queueHooks"],f=r.timers,g=d?d.length:0;for(c.finish=!0,r.queue(this,a,[]),e&&e.stop&&e.stop.call(this,!0),b=f.length;b--;)f[b].elem===this&&f[b].queue===a&&(f[b].anim.stop(!0),f.splice(b,1));for(b=0;g>b;b++)d[b]&&d[b].finish&&d[b].finish.call(this);delete c.finish})}}),r.each(["toggle","show","hide"],function(a,b){var c=r.fn[b];r.fn[b]=function(a,d,e){return null==a||"boolean"==typeof a?c.apply(this,arguments):this.animate(cb(b,!0),a,d,e)}}),r.each({slideDown:cb("show"),slideUp:cb("hide"),slideToggle:cb("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){r.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),r.timers=[],r.fx.tick=function(){var a,b=0,c=r.timers;for(Ya=r.now();b1)},removeAttr:function(a){return this.each(function(){r.removeAttr(this,a)})}}),r.extend({attr:function(a,b,c){var d,e,f=a.nodeType;if(3!==f&&8!==f&&2!==f)return"undefined"==typeof a.getAttribute?r.prop(a,b,c):(1===f&&r.isXMLDoc(a)||(e=r.attrHooks[b.toLowerCase()]||(r.expr.match.bool.test(b)?hb:void 0)),void 0!==c?null===c?void r.removeAttr(a,b):e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:(a.setAttribute(b,c+""),c):e&&"get"in e&&null!==(d=e.get(a,b))?d:(d=r.find.attr(a,b),null==d?void 0:d))},attrHooks:{type:{set:function(a,b){if(!o.radioValue&&"radio"===b&&r.nodeName(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}}},removeAttr:function(a,b){var c,d=0,e=b&&b.match(K);if(e&&1===a.nodeType)while(c=e[d++])a.removeAttribute(c); -}}),hb={set:function(a,b,c){return b===!1?r.removeAttr(a,c):a.setAttribute(c,c),c}},r.each(r.expr.match.bool.source.match(/\w+/g),function(a,b){var c=ib[b]||r.find.attr;ib[b]=function(a,b,d){var e,f,g=b.toLowerCase();return d||(f=ib[g],ib[g]=e,e=null!=c(a,b,d)?g:null,ib[g]=f),e}});var jb=/^(?:input|select|textarea|button)$/i,kb=/^(?:a|area)$/i;r.fn.extend({prop:function(a,b){return S(this,r.prop,a,b,arguments.length>1)},removeProp:function(a){return this.each(function(){delete this[r.propFix[a]||a]})}}),r.extend({prop:function(a,b,c){var d,e,f=a.nodeType;if(3!==f&&8!==f&&2!==f)return 1===f&&r.isXMLDoc(a)||(b=r.propFix[b]||b,e=r.propHooks[b]),void 0!==c?e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:a[b]=c:e&&"get"in e&&null!==(d=e.get(a,b))?d:a[b]},propHooks:{tabIndex:{get:function(a){var b=r.find.attr(a,"tabindex");return b?parseInt(b,10):jb.test(a.nodeName)||kb.test(a.nodeName)&&a.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),o.optSelected||(r.propHooks.selected={get:function(a){var b=a.parentNode;return b&&b.parentNode&&b.parentNode.selectedIndex,null},set:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex)}}),r.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){r.propFix[this.toLowerCase()]=this});var lb=/[\t\r\n\f]/g;function mb(a){return a.getAttribute&&a.getAttribute("class")||""}r.fn.extend({addClass:function(a){var b,c,d,e,f,g,h,i=0;if(r.isFunction(a))return this.each(function(b){r(this).addClass(a.call(this,b,mb(this)))});if("string"==typeof a&&a){b=a.match(K)||[];while(c=this[i++])if(e=mb(c),d=1===c.nodeType&&(" "+e+" ").replace(lb," ")){g=0;while(f=b[g++])d.indexOf(" "+f+" ")<0&&(d+=f+" ");h=r.trim(d),e!==h&&c.setAttribute("class",h)}}return this},removeClass:function(a){var b,c,d,e,f,g,h,i=0;if(r.isFunction(a))return this.each(function(b){r(this).removeClass(a.call(this,b,mb(this)))});if(!arguments.length)return this.attr("class","");if("string"==typeof a&&a){b=a.match(K)||[];while(c=this[i++])if(e=mb(c),d=1===c.nodeType&&(" "+e+" ").replace(lb," ")){g=0;while(f=b[g++])while(d.indexOf(" "+f+" ")>-1)d=d.replace(" "+f+" "," ");h=r.trim(d),e!==h&&c.setAttribute("class",h)}}return this},toggleClass:function(a,b){var c=typeof a;return"boolean"==typeof b&&"string"===c?b?this.addClass(a):this.removeClass(a):r.isFunction(a)?this.each(function(c){r(this).toggleClass(a.call(this,c,mb(this),b),b)}):this.each(function(){var b,d,e,f;if("string"===c){d=0,e=r(this),f=a.match(K)||[];while(b=f[d++])e.hasClass(b)?e.removeClass(b):e.addClass(b)}else void 0!==a&&"boolean"!==c||(b=mb(this),b&&V.set(this,"__className__",b),this.setAttribute&&this.setAttribute("class",b||a===!1?"":V.get(this,"__className__")||""))})},hasClass:function(a){var b,c,d=0;b=" "+a+" ";while(c=this[d++])if(1===c.nodeType&&(" "+mb(c)+" ").replace(lb," ").indexOf(b)>-1)return!0;return!1}});var nb=/\r/g,ob=/[\x20\t\r\n\f]+/g;r.fn.extend({val:function(a){var b,c,d,e=this[0];{if(arguments.length)return d=r.isFunction(a),this.each(function(c){var e;1===this.nodeType&&(e=d?a.call(this,c,r(this).val()):a,null==e?e="":"number"==typeof e?e+="":r.isArray(e)&&(e=r.map(e,function(a){return null==a?"":a+""})),b=r.valHooks[this.type]||r.valHooks[this.nodeName.toLowerCase()],b&&"set"in b&&void 0!==b.set(this,e,"value")||(this.value=e))});if(e)return b=r.valHooks[e.type]||r.valHooks[e.nodeName.toLowerCase()],b&&"get"in b&&void 0!==(c=b.get(e,"value"))?c:(c=e.value,"string"==typeof c?c.replace(nb,""):null==c?"":c)}}}),r.extend({valHooks:{option:{get:function(a){var b=r.find.attr(a,"value");return null!=b?b:r.trim(r.text(a)).replace(ob," ")}},select:{get:function(a){for(var b,c,d=a.options,e=a.selectedIndex,f="select-one"===a.type,g=f?null:[],h=f?e+1:d.length,i=0>e?h:f?e:0;h>i;i++)if(c=d[i],(c.selected||i===e)&&!c.disabled&&(!c.parentNode.disabled||!r.nodeName(c.parentNode,"optgroup"))){if(b=r(c).val(),f)return b;g.push(b)}return g},set:function(a,b){var c,d,e=a.options,f=r.makeArray(b),g=e.length;while(g--)d=e[g],(d.selected=r.inArray(r.valHooks.option.get(d),f)>-1)&&(c=!0);return c||(a.selectedIndex=-1),f}}}}),r.each(["radio","checkbox"],function(){r.valHooks[this]={set:function(a,b){return r.isArray(b)?a.checked=r.inArray(r(a).val(),b)>-1:void 0}},o.checkOn||(r.valHooks[this].get=function(a){return null===a.getAttribute("value")?"on":a.value})});var pb=/^(?:focusinfocus|focusoutblur)$/;r.extend(r.event,{trigger:function(b,c,e,f){var g,h,i,j,k,m,n,o=[e||d],p=l.call(b,"type")?b.type:b,q=l.call(b,"namespace")?b.namespace.split("."):[];if(h=i=e=e||d,3!==e.nodeType&&8!==e.nodeType&&!pb.test(p+r.event.triggered)&&(p.indexOf(".")>-1&&(q=p.split("."),p=q.shift(),q.sort()),k=p.indexOf(":")<0&&"on"+p,b=b[r.expando]?b:new r.Event(p,"object"==typeof b&&b),b.isTrigger=f?2:3,b.namespace=q.join("."),b.rnamespace=b.namespace?new RegExp("(^|\\.)"+q.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=e),c=null==c?[b]:r.makeArray(c,[b]),n=r.event.special[p]||{},f||!n.trigger||n.trigger.apply(e,c)!==!1)){if(!f&&!n.noBubble&&!r.isWindow(e)){for(j=n.delegateType||p,pb.test(j+p)||(h=h.parentNode);h;h=h.parentNode)o.push(h),i=h;i===(e.ownerDocument||d)&&o.push(i.defaultView||i.parentWindow||a)}g=0;while((h=o[g++])&&!b.isPropagationStopped())b.type=g>1?j:n.bindType||p,m=(V.get(h,"events")||{})[b.type]&&V.get(h,"handle"),m&&m.apply(h,c),m=k&&h[k],m&&m.apply&&T(h)&&(b.result=m.apply(h,c),b.result===!1&&b.preventDefault());return b.type=p,f||b.isDefaultPrevented()||n._default&&n._default.apply(o.pop(),c)!==!1||!T(e)||k&&r.isFunction(e[p])&&!r.isWindow(e)&&(i=e[k],i&&(e[k]=null),r.event.triggered=p,e[p](),r.event.triggered=void 0,i&&(e[k]=i)),b.result}},simulate:function(a,b,c){var d=r.extend(new r.Event,c,{type:a,isSimulated:!0});r.event.trigger(d,null,b)}}),r.fn.extend({trigger:function(a,b){return this.each(function(){r.event.trigger(a,b,this)})},triggerHandler:function(a,b){var c=this[0];return c?r.event.trigger(a,b,c,!0):void 0}}),r.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(a,b){r.fn[b]=function(a,c){return arguments.length>0?this.on(b,null,a,c):this.trigger(b)}}),r.fn.extend({hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}}),o.focusin="onfocusin"in a,o.focusin||r.each({focus:"focusin",blur:"focusout"},function(a,b){var c=function(a){r.event.simulate(b,a.target,r.event.fix(a))};r.event.special[b]={setup:function(){var d=this.ownerDocument||this,e=V.access(d,b);e||d.addEventListener(a,c,!0),V.access(d,b,(e||0)+1)},teardown:function(){var d=this.ownerDocument||this,e=V.access(d,b)-1;e?V.access(d,b,e):(d.removeEventListener(a,c,!0),V.remove(d,b))}}});var qb=a.location,rb=r.now(),sb=/\?/;r.parseXML=function(b){var c;if(!b||"string"!=typeof b)return null;try{c=(new a.DOMParser).parseFromString(b,"text/xml")}catch(d){c=void 0}return c&&!c.getElementsByTagName("parsererror").length||r.error("Invalid XML: "+b),c};var tb=/\[\]$/,ub=/\r?\n/g,vb=/^(?:submit|button|image|reset|file)$/i,wb=/^(?:input|select|textarea|keygen)/i;function xb(a,b,c,d){var e;if(r.isArray(b))r.each(b,function(b,e){c||tb.test(a)?d(a,e):xb(a+"["+("object"==typeof e&&null!=e?b:"")+"]",e,c,d)});else if(c||"object"!==r.type(b))d(a,b);else for(e in b)xb(a+"["+e+"]",b[e],c,d)}r.param=function(a,b){var c,d=[],e=function(a,b){var c=r.isFunction(b)?b():b;d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(null==c?"":c)};if(r.isArray(a)||a.jquery&&!r.isPlainObject(a))r.each(a,function(){e(this.name,this.value)});else for(c in a)xb(c,a[c],b,e);return d.join("&")},r.fn.extend({serialize:function(){return r.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var a=r.prop(this,"elements");return a?r.makeArray(a):this}).filter(function(){var a=this.type;return this.name&&!r(this).is(":disabled")&&wb.test(this.nodeName)&&!vb.test(a)&&(this.checked||!ha.test(a))}).map(function(a,b){var c=r(this).val();return null==c?null:r.isArray(c)?r.map(c,function(a){return{name:b.name,value:a.replace(ub,"\r\n")}}):{name:b.name,value:c.replace(ub,"\r\n")}}).get()}});var yb=/%20/g,zb=/#.*$/,Ab=/([?&])_=[^&]*/,Bb=/^(.*?):[ \t]*([^\r\n]*)$/gm,Cb=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Db=/^(?:GET|HEAD)$/,Eb=/^\/\//,Fb={},Gb={},Hb="*/".concat("*"),Ib=d.createElement("a");Ib.href=qb.href;function Jb(a){return function(b,c){"string"!=typeof b&&(c=b,b="*");var d,e=0,f=b.toLowerCase().match(K)||[];if(r.isFunction(c))while(d=f[e++])"+"===d[0]?(d=d.slice(1)||"*",(a[d]=a[d]||[]).unshift(c)):(a[d]=a[d]||[]).push(c)}}function Kb(a,b,c,d){var e={},f=a===Gb;function g(h){var i;return e[h]=!0,r.each(a[h]||[],function(a,h){var j=h(b,c,d);return"string"!=typeof j||f||e[j]?f?!(i=j):void 0:(b.dataTypes.unshift(j),g(j),!1)}),i}return g(b.dataTypes[0])||!e["*"]&&g("*")}function Lb(a,b){var c,d,e=r.ajaxSettings.flatOptions||{};for(c in b)void 0!==b[c]&&((e[c]?a:d||(d={}))[c]=b[c]);return d&&r.extend(!0,a,d),a}function Mb(a,b,c){var d,e,f,g,h=a.contents,i=a.dataTypes;while("*"===i[0])i.shift(),void 0===d&&(d=a.mimeType||b.getResponseHeader("Content-Type"));if(d)for(e in h)if(h[e]&&h[e].test(d)){i.unshift(e);break}if(i[0]in c)f=i[0];else{for(e in c){if(!i[0]||a.converters[e+" "+i[0]]){f=e;break}g||(g=e)}f=f||g}return f?(f!==i[0]&&i.unshift(f),c[f]):void 0}function Nb(a,b,c,d){var e,f,g,h,i,j={},k=a.dataTypes.slice();if(k[1])for(g in a.converters)j[g.toLowerCase()]=a.converters[g];f=k.shift();while(f)if(a.responseFields[f]&&(c[a.responseFields[f]]=b),!i&&d&&a.dataFilter&&(b=a.dataFilter(b,a.dataType)),i=f,f=k.shift())if("*"===f)f=i;else if("*"!==i&&i!==f){if(g=j[i+" "+f]||j["* "+f],!g)for(e in j)if(h=e.split(" "),h[1]===f&&(g=j[i+" "+h[0]]||j["* "+h[0]])){g===!0?g=j[e]:j[e]!==!0&&(f=h[0],k.unshift(h[1]));break}if(g!==!0)if(g&&a["throws"])b=g(b);else try{b=g(b)}catch(l){return{state:"parsererror",error:g?l:"No conversion from "+i+" to "+f}}}return{state:"success",data:b}}r.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:qb.href,type:"GET",isLocal:Cb.test(qb.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Hb,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":r.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(a,b){return b?Lb(Lb(a,r.ajaxSettings),b):Lb(r.ajaxSettings,a)},ajaxPrefilter:Jb(Fb),ajaxTransport:Jb(Gb),ajax:function(b,c){"object"==typeof b&&(c=b,b=void 0),c=c||{};var e,f,g,h,i,j,k,l,m,n,o=r.ajaxSetup({},c),p=o.context||o,q=o.context&&(p.nodeType||p.jquery)?r(p):r.event,s=r.Deferred(),t=r.Callbacks("once memory"),u=o.statusCode||{},v={},w={},x="canceled",y={readyState:0,getResponseHeader:function(a){var b;if(k){if(!h){h={};while(b=Bb.exec(g))h[b[1].toLowerCase()]=b[2]}b=h[a.toLowerCase()]}return null==b?null:b},getAllResponseHeaders:function(){return k?g:null},setRequestHeader:function(a,b){return null==k&&(a=w[a.toLowerCase()]=w[a.toLowerCase()]||a,v[a]=b),this},overrideMimeType:function(a){return null==k&&(o.mimeType=a),this},statusCode:function(a){var b;if(a)if(k)y.always(a[y.status]);else for(b in a)u[b]=[u[b],a[b]];return this},abort:function(a){var b=a||x;return e&&e.abort(b),A(0,b),this}};if(s.promise(y),o.url=((b||o.url||qb.href)+"").replace(Eb,qb.protocol+"//"),o.type=c.method||c.type||o.method||o.type,o.dataTypes=(o.dataType||"*").toLowerCase().match(K)||[""],null==o.crossDomain){j=d.createElement("a");try{j.href=o.url,j.href=j.href,o.crossDomain=Ib.protocol+"//"+Ib.host!=j.protocol+"//"+j.host}catch(z){o.crossDomain=!0}}if(o.data&&o.processData&&"string"!=typeof o.data&&(o.data=r.param(o.data,o.traditional)),Kb(Fb,o,c,y),k)return y;l=r.event&&o.global,l&&0===r.active++&&r.event.trigger("ajaxStart"),o.type=o.type.toUpperCase(),o.hasContent=!Db.test(o.type),f=o.url.replace(zb,""),o.hasContent?o.data&&o.processData&&0===(o.contentType||"").indexOf("application/x-www-form-urlencoded")&&(o.data=o.data.replace(yb,"+")):(n=o.url.slice(f.length),o.data&&(f+=(sb.test(f)?"&":"?")+o.data,delete o.data),o.cache===!1&&(f=f.replace(Ab,""),n=(sb.test(f)?"&":"?")+"_="+rb++ +n),o.url=f+n),o.ifModified&&(r.lastModified[f]&&y.setRequestHeader("If-Modified-Since",r.lastModified[f]),r.etag[f]&&y.setRequestHeader("If-None-Match",r.etag[f])),(o.data&&o.hasContent&&o.contentType!==!1||c.contentType)&&y.setRequestHeader("Content-Type",o.contentType),y.setRequestHeader("Accept",o.dataTypes[0]&&o.accepts[o.dataTypes[0]]?o.accepts[o.dataTypes[0]]+("*"!==o.dataTypes[0]?", "+Hb+"; q=0.01":""):o.accepts["*"]);for(m in o.headers)y.setRequestHeader(m,o.headers[m]);if(o.beforeSend&&(o.beforeSend.call(p,y,o)===!1||k))return y.abort();if(x="abort",t.add(o.complete),y.done(o.success),y.fail(o.error),e=Kb(Gb,o,c,y)){if(y.readyState=1,l&&q.trigger("ajaxSend",[y,o]),k)return y;o.async&&o.timeout>0&&(i=a.setTimeout(function(){y.abort("timeout")},o.timeout));try{k=!1,e.send(v,A)}catch(z){if(k)throw z;A(-1,z)}}else A(-1,"No Transport");function A(b,c,d,h){var j,m,n,v,w,x=c;k||(k=!0,i&&a.clearTimeout(i),e=void 0,g=h||"",y.readyState=b>0?4:0,j=b>=200&&300>b||304===b,d&&(v=Mb(o,y,d)),v=Nb(o,v,y,j),j?(o.ifModified&&(w=y.getResponseHeader("Last-Modified"),w&&(r.lastModified[f]=w),w=y.getResponseHeader("etag"),w&&(r.etag[f]=w)),204===b||"HEAD"===o.type?x="nocontent":304===b?x="notmodified":(x=v.state,m=v.data,n=v.error,j=!n)):(n=x,!b&&x||(x="error",0>b&&(b=0))),y.status=b,y.statusText=(c||x)+"",j?s.resolveWith(p,[m,x,y]):s.rejectWith(p,[y,x,n]),y.statusCode(u),u=void 0,l&&q.trigger(j?"ajaxSuccess":"ajaxError",[y,o,j?m:n]),t.fireWith(p,[y,x]),l&&(q.trigger("ajaxComplete",[y,o]),--r.active||r.event.trigger("ajaxStop")))}return y},getJSON:function(a,b,c){return r.get(a,b,c,"json")},getScript:function(a,b){return r.get(a,void 0,b,"script")}}),r.each(["get","post"],function(a,b){r[b]=function(a,c,d,e){return r.isFunction(c)&&(e=e||d,d=c,c=void 0),r.ajax(r.extend({url:a,type:b,dataType:e,data:c,success:d},r.isPlainObject(a)&&a))}}),r._evalUrl=function(a){return r.ajax({url:a,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,"throws":!0})},r.fn.extend({wrapAll:function(a){var b;return this[0]&&(r.isFunction(a)&&(a=a.call(this[0])),b=r(a,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstElementChild)a=a.firstElementChild;return a}).append(this)),this},wrapInner:function(a){return r.isFunction(a)?this.each(function(b){r(this).wrapInner(a.call(this,b))}):this.each(function(){var b=r(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=r.isFunction(a);return this.each(function(c){r(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(a){return this.parent(a).not("body").each(function(){r(this).replaceWith(this.childNodes)}),this}}),r.expr.pseudos.hidden=function(a){return!r.expr.pseudos.visible(a)},r.expr.pseudos.visible=function(a){return!!(a.offsetWidth||a.offsetHeight||a.getClientRects().length)},r.ajaxSettings.xhr=function(){try{return new a.XMLHttpRequest}catch(b){}};var Ob={0:200,1223:204},Pb=r.ajaxSettings.xhr();o.cors=!!Pb&&"withCredentials"in Pb,o.ajax=Pb=!!Pb,r.ajaxTransport(function(b){var c,d;return o.cors||Pb&&!b.crossDomain?{send:function(e,f){var g,h=b.xhr();if(h.open(b.type,b.url,b.async,b.username,b.password),b.xhrFields)for(g in b.xhrFields)h[g]=b.xhrFields[g];b.mimeType&&h.overrideMimeType&&h.overrideMimeType(b.mimeType),b.crossDomain||e["X-Requested-With"]||(e["X-Requested-With"]="XMLHttpRequest");for(g in e)h.setRequestHeader(g,e[g]);c=function(a){return function(){c&&(c=d=h.onload=h.onerror=h.onabort=h.onreadystatechange=null,"abort"===a?h.abort():"error"===a?"number"!=typeof h.status?f(0,"error"):f(h.status,h.statusText):f(Ob[h.status]||h.status,h.statusText,"text"!==(h.responseType||"text")||"string"!=typeof h.responseText?{binary:h.response}:{text:h.responseText},h.getAllResponseHeaders()))}},h.onload=c(),d=h.onerror=c("error"),void 0!==h.onabort?h.onabort=d:h.onreadystatechange=function(){4===h.readyState&&a.setTimeout(function(){c&&d()})},c=c("abort");try{h.send(b.hasContent&&b.data||null)}catch(i){if(c)throw i}},abort:function(){c&&c()}}:void 0}),r.ajaxPrefilter(function(a){a.crossDomain&&(a.contents.script=!1)}),r.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(a){return r.globalEval(a),a}}}),r.ajaxPrefilter("script",function(a){void 0===a.cache&&(a.cache=!1),a.crossDomain&&(a.type="GET")}),r.ajaxTransport("script",function(a){if(a.crossDomain){var b,c;return{send:function(e,f){b=r("') - - -@login_required -def create_hostel_leave(request): - - if request.method == 'GET': - return render(request, 'hostelmanagement/create_leave.html') - elif request.method == 'POST': - data = request.POST # Assuming you are sending form data via POST request - student_name = data.get('student_name') - roll_num = data.get('roll_num') - phone_number = data.get('phone_number') # Retrieve phone number from form data - reason = data.get('reason') - start_date = data.get('start_date', timezone.now()) - end_date = data.get('end_date') - - - # Create HostelLeave object and save to the database - leave = HostelLeave.objects.create( - student_name=student_name, - roll_num=roll_num, - phone_number=phone_number, # Include phone number in the object creation - reason=reason, - start_date=start_date, - end_date=end_date, - - ) - caretakers = HallCaretaker.objects.all() - sender = request.user - type = "leave_request" - for caretaker in caretakers: - try: - # Send notification - hostel_notifications(sender, caretaker.staff.id.user, type) - except Exception as e: - # Handle notification sending error - print(f"Error sending notification to caretaker {caretaker.staff.user.username}: {e}") - - return JsonResponse({'message': 'HostelLeave created successfully'}, status=status.HTTP_201_CREATED) - -# hostel_complaints_list caretaker can see all hostel complaints - -@login_required -def hostel_complaint_list(request): - user_id = request.user.id - - try: - # Assuming the user's profile is stored in extrainfo - staff = request.user.extrainfo.id - except AttributeError: - staff = None - - if staff is not None and HallCaretaker.objects.filter(staff_id=staff).exists(): - complaints = HostelComplaint.objects.all() - return render(request, 'hostelmanagement/hostel_complaint.html', {'complaints': complaints}) - else: - return HttpResponse('') - - -@login_required -def get_students(request): - try: - staff = request.user.extrainfo.id - print(staff) - except AttributeError: - staff = None - - if HallCaretaker.objects.filter(staff_id=staff).exists(): - hall_id = HallCaretaker.objects.get(staff_id=staff).hall_id - print(hall_id) - hall_no = Hall.objects.get(id=hall_id) - print(hall_no) - student_details = StudentDetails.objects.filter(hall_id=hall_no) - - return render(request, 'hostelmanagement/student_details.html', {'students': student_details}) - - elif HallWarden.objects.filter(faculty_id=staff).exists(): - hall_id = HallWarden.objects.get(faculty_id=staff).hall_id - student_details = StudentDetails.objects.filter(hall_id=hall_no) - - return render(request, 'hostelmanagement/student_details.html', {'students': student_details}) - else: - return HttpResponse('') - -# Student can post complaints - - -class PostComplaint(APIView): - # Assuming you are using session authentication - authentication_classes = [SessionAuthentication] - # Allow only authenticated users to access the view - permission_classes = [IsAuthenticated] - - def dispatch(self, request, *args, **kwargs): - # print(request.user.username) - if not request.user.is_authenticated: - # Redirect to the login page if user is not authenticated - return redirect('/hostelmanagement') - return super().dispatch(request, *args, **kwargs) - - def get(self, request): - return render(request, 'hostelmanagement/post_complaint_form.html') - - def post(self, request): - hall_name = request.data.get('hall_name') - student_name = request.data.get('student_name') - roll_number = request.data.get('roll_number') - description = request.data.get('description') - contact_number = request.data.get('contact_number') - - # Assuming the student's name is stored in the user object - student_name = request.user.username - - complaint = HostelComplaint.objects.create( - hall_name=hall_name, - student_name=student_name, - roll_number=roll_number, - description=description, - contact_number=contact_number - ) - - # Use JavaScript to display a pop-up message after submission - return HttpResponse('') - - -# // student can see his leave status - -class my_leaves(View): - @method_decorator(login_required, name='dispatch') - def get(self, request, *args, **kwargs): - try: - # Get the user ID from the request's user - user_id = str(request.user) - - # Retrieve leaves registered by the current student based on their roll number - my_leaves = HostelLeave.objects.filter(roll_num__iexact=user_id) - # Construct the context to pass to the template - context = { - 'leaves': my_leaves - } - - # Render the template with the context data - return render(request, 'hostelmanagement/my_leaves.html', context) - - except User.DoesNotExist: - # Handle the case where the user with the given ID doesn't exist - return HttpResponse(f"User with ID {user_id} does not exist.") - - -class HallIdView(APIView): - authentication_classes = [] # Allow public access for testing - permission_classes = [] # Allow any user to access the view - - def get(self, request, *args, **kwargs): - hall_id = HostelAllotment.objects.values('hall_id') - return Response(hall_id, status=status.HTTP_200_OK) - - -@login_required(login_url=LOGIN_URL) -def logout_view(request): - logout(request) - return redirect("/") - - -@method_decorator(user_passes_test(is_superuser), name='dispatch') -class AssignCaretakerView(APIView): - authentication_classes = [SessionAuthentication] - permission_classes = [IsAuthenticated] - template_name = 'hostelmanagement/assign_caretaker.html' - - def get(self, request, *args, **kwargs): - hall = Hall.objects.all() - caretaker_usernames = Staff.objects.all() - return render(request, self.template_name, {'halls': hall, 'caretaker_usernames': caretaker_usernames}) - - def post(self, request, *args, **kwargs): - hall_id = request.data.get('hall_id') - caretaker_username = request.data.get('caretaker_username') - - try: - hall = Hall.objects.get(hall_id=hall_id) - caretaker_staff = Staff.objects.get( - id__user__username=caretaker_username) - - # Retrieve the previous caretaker for the hall, if any - prev_hall_caretaker = HallCaretaker.objects.filter(hall=hall).first() - # print(prev_hall_caretaker.staff.id) - # Delete any previous assignments of the caretaker in HallCaretaker table - HallCaretaker.objects.filter(staff=caretaker_staff).delete() - - # Delete any previous assignments of the caretaker in HostelAllotment table - HostelAllotment.objects.filter( - assignedCaretaker=caretaker_staff).delete() - - # Delete any previously assigned caretaker to the same hall - HallCaretaker.objects.filter(hall=hall).delete() - - # Assign the new caretaker to the hall in HallCaretaker table - hall_caretaker = HallCaretaker.objects.create( - hall=hall, staff=caretaker_staff) - - # # Update the assigned caretaker in Hostelallottment table - hostel_allotments = HostelAllotment.objects.filter(hall=hall) - for hostel_allotment in hostel_allotments: - hostel_allotment.assignedCaretaker = caretaker_staff - hostel_allotment.save() - - # Retrieve the current warden for the hall - current_warden = HallWarden.objects.filter(hall=hall).first() - - try: - history_entry = HostelTransactionHistory.objects.create( - hall=hall, - change_type='Caretaker', - previous_value= prev_hall_caretaker.staff.id if (prev_hall_caretaker and prev_hall_caretaker.staff) else 'None', - new_value=caretaker_username - ) - except Exception as e: - print("Error creating HostelTransactionHistory:", e) - - - # Create hostel history - try: - HostelHistory.objects.create( - hall=hall, - caretaker=caretaker_staff, - batch=hall.assigned_batch, - warden=current_warden.faculty if( current_warden and current_warden.faculty) else None - ) - except Exception as e: - print ("Error creating history",e) - return Response({'message': f'Caretaker {caretaker_username} assigned to Hall {hall_id} successfully'}, status=status.HTTP_201_CREATED) - - except Hall.DoesNotExist: - return Response({'error': f'Hall with ID {hall_id} not found'}, status=status.HTTP_404_NOT_FOUND) - except Staff.DoesNotExist: - return Response({'error': f'Caretaker with username {caretaker_username} not found'}, status=status.HTTP_404_NOT_FOUND) - except Exception as e: - return JsonResponse({'status': 'error', 'error': str(e)}, status=500) - - - -@method_decorator(user_passes_test(is_superuser), name='dispatch') -class AssignBatchView(View): - authentication_classes = [SessionAuthentication] - permission_classes = [IsAuthenticated] - # Assuming the HTML file is directly in the 'templates' folder - template_name = 'hostelmanagement/assign_batch.html' - - def get(self, request, *args, **kwargs): - hall = Hall.objects.all() - return render(request, self.template_name, {'halls': hall}) - - def update_student_hall_allotment(self, hall, assigned_batch): - hall_number = int(''.join(filter(str.isdigit, hall.hall_id))) - students = Student.objects.filter(batch=int(assigned_batch)) - - - for student in students: - student.hall_no = hall_number - student.save() - - - def post(self, request, *args, **kwargs): - try: - with transaction.atomic(): # Start a database transaction - - data = json.loads(request.body.decode('utf-8')) - hall_id = data.get('hall_id') - - hall = Hall.objects.get(hall_id=hall_id) - # previous_batch = hall.assigned_batch # Get the previous batch - previous_batch = hall.assigned_batch if hall.assigned_batch is not None else 0 # Get the previous batch - hall.assigned_batch = data.get('batch') - hall.save() - - - - - - # Update the assignedBatch field in HostelAllotment table for the corresponding hall - room_allotments = HostelAllotment.objects.filter(hall=hall) - for room_allotment in room_allotments: - room_allotment.assignedBatch = hall.assigned_batch - room_allotment.save() - - # retrieve the current caretaker and current warden for the hall - current_caretaker =HallCaretaker.objects.filter(hall=hall).first() - current_warden = HallWarden.objects.filter(hall=hall).first() - - # Record the transaction history - HostelTransactionHistory.objects.create( - hall=hall, - change_type='Batch', - previous_value=previous_batch, - new_value=hall.assigned_batch - ) - - # Create hostel history - try: - HostelHistory.objects.create( - hall=hall, - caretaker=current_caretaker.staff if (current_caretaker and current_caretaker.staff) else None, - - batch=hall.assigned_batch, - warden=current_warden.faculty if( current_warden and current_warden.faculty) else None - - ) - except Exception as e: - print ("Error creating history",e) - - self.update_student_hall_allotment(hall, hall.assigned_batch) - print("batch assigned successssssssssssssssssss") - messages.success(request, 'batch assigned succesfully') - - return JsonResponse({'status': 'success', 'message': 'Batch assigned successfully'}, status=200) - - except Hall.DoesNotExist: - return JsonResponse({'status': 'error', 'error': f'Hall with ID {hall_id} not found'}, status=404) - - except Exception as e: - return JsonResponse({'status': 'error', 'error': str(e)}, status=500) - - def test_func(self): - # Check if the user is a superuser - return self.request.user.is_superuser - - -@method_decorator(user_passes_test(is_superuser), name='dispatch') -class AssignWardenView(APIView): - authentication_classes = [SessionAuthentication] - permission_classes = [IsAuthenticated] - template_name = 'hostelmanagement/assign_warden.html' - - def post(self, request, *args, **kwargs): - hall_id = request.data.get('hall_id') - warden_id = request.data.get('warden_id') - try: - hall = Hall.objects.get(hall_id=hall_id) - warden = Faculty.objects.get(id__user__username=warden_id) - - # Retrieve the previous caretaker for the hall, if any - prev_hall_warden = HallWarden.objects.filter(hall=hall).first() - - # Delete any previous assignments of the warden in Hallwarden table - HallWarden.objects.filter(faculty=warden).delete() - - # Delete any previous assignments of the warden in HostelAllotment table - HostelAllotment.objects.filter(assignedWarden=warden).delete() - - # Delete any previously assigned warden to the same hall - HallWarden.objects.filter(hall=hall).delete() - - # Assign the new warden to the hall in Hallwarden table - hall_warden = HallWarden.objects.create(hall=hall, faculty=warden) - - #current caretker - current_caretaker =HallCaretaker.objects.filter(hall=hall).first() - print(current_caretaker) - - # Update the assigned warden in Hostelallottment table - hostel_allotments = HostelAllotment.objects.filter(hall=hall) - for hostel_allotment in hostel_allotments: - hostel_allotment.assignedWarden = warden - hostel_allotment.save() - - try: - history_entry = HostelTransactionHistory.objects.create( - hall=hall, - change_type='Warden', - previous_value= prev_hall_warden.faculty.id if (prev_hall_warden and prev_hall_warden.faculty) else 'None', - new_value=warden - ) - except Exception as e: - print("Error creating HostelTransactionHistory:", e) - - - # Create hostel history - try: - HostelHistory.objects.create( - hall=hall, - caretaker=current_caretaker.staff if (current_caretaker and current_caretaker.staff) else None, - - batch=hall.assigned_batch, - warden=warden - ) - except Exception as e: - print ("Error creating history",e) - - - return Response({'message': f'Warden {warden_id} assigned to Hall {hall_id} successfully'}, status=status.HTTP_201_CREATED) - - except Hall.DoesNotExist: - return Response({'error': f'Hall with ID {hall_id} not found'}, status=status.HTTP_404_NOT_FOUND) - except Faculty.DoesNotExist: - return Response({'error': f'Warden with username {warden_id} not found'}, status=status.HTTP_404_NOT_FOUND) - except Exception as e: - return JsonResponse({'status': 'error', 'error': str(e)}, status=500) - - -@method_decorator(user_passes_test(is_superuser), name='dispatch') -class AddHostelView(View): - template_name = 'hostelmanagement/add_hostel.html' - - def get(self, request, *args, **kwargs): - form = HallForm() - return render(request, self.template_name, {'form': form}) - - def post(self, request, *args, **kwargs): - form = HallForm(request.POST) - if form.is_valid(): - hall_id = form.cleaned_data['hall_id'] - - # # Check if a hall with the given hall_id already exists - # if Hall.objects.filter(hall_id=hall_id).exists(): - # messages.error(request, f'Hall with ID {hall_id} already exists.') - # return redirect('hostelmanagement:add_hostel') - - # Check if a hall with the given hall_id already exists - if Hall.objects.filter(hall_id=hall_id).exists(): - error_message = f'Hall with ID {hall_id} already exists.' - - return HttpResponse(error_message, status=400) - - # If not, create a new hall - form.save() - messages.success(request, 'Hall added successfully!') - # Redirect to the view showing all hostels - return HttpResponseRedirect(reverse("hostelmanagement:hostel_view")) - # return render(request, 'hostelmanagement/admin_hostel_list.html') - - # If form is not valid, render the form with errors - return render(request, self.template_name, {'form': form}) - - -class CheckHallExistsView(View): - - def get(self, request, *args, **kwargs): - - hall_id = request.GET.get('hall_id') - try: - hall = Hall.objects.get(hall_id=hall_id) - exists = True - except Hall.DoesNotExist: - exists = False - messages.MessageFailure(request, f'Hall {hall_id} already exist.') - return JsonResponse({'exists': exists}) - - -@method_decorator(user_passes_test(is_superuser), name='dispatch') -class AdminHostelListView(View): - template_name = 'hostelmanagement/admin_hostel_list.html' - - def get(self, request, *args, **kwargs): - halls = Hall.objects.all() - # Create a list to store additional details - hostel_details = [] - - # Loop through each hall and fetch assignedCaretaker and assignedWarden - for hall in halls: - try: - caretaker = HallCaretaker.objects.filter(hall=hall).first() - warden = HallWarden.objects.filter(hall=hall).first() - except HostelAllotment.DoesNotExist: - assigned_caretaker = None - assigned_warden = None - - hostel_detail = { - 'hall_id': hall.hall_id, - 'hall_name': hall.hall_name, - 'max_accomodation': hall.max_accomodation, - 'number_students': hall.number_students, - 'assigned_batch': hall.assigned_batch, - 'assigned_caretaker': caretaker.staff.id.user.username if caretaker else None, - 'assigned_warden': warden.faculty.id.user.username if warden else None, - } - - hostel_details.append(hostel_detail) - - return render(request, self.template_name, {'hostel_details': hostel_details}) - - -@method_decorator(user_passes_test(is_superuser), name='dispatch') -class DeleteHostelView(View): - def get(self, request, hall_id, *args, **kwargs): - # Get the hall instance - hall = get_object_or_404(Hall, hall_id=hall_id) - - # Delete related entries in other tables - hostelallotments = HostelAllotment.objects.filter(hall=hall) - hostelallotments.delete() - - # Delete the hall - hall.delete() - messages.success(request, f'Hall {hall_id} deleted successfully.') - - return HttpResponseRedirect(reverse("hostelmanagement:hostel_view")) - - -class HallIdView(APIView): - authentication_classes = [] # Allow public access for testing - permission_classes = [] # Allow any user to access the view - - def get(self, request, *args, **kwargs): - hall_id = HostelAllotment.objects.values('hall_id') - return Response(hall_id, status=status.HTTP_200_OK) - - -@login_required(login_url=LOGIN_URL) -def logout_view(request): - logout(request) - return redirect("/") - - -# //! alloted_rooms -def alloted_rooms(request, hall_id): - """ - This function returns the allotted rooms in a particular hall. - - @param: - request - HttpRequest object containing metadata about the user request. - hall_id - Hall ID for which the allotted rooms need to be retrieved. - - @variables: - allotted_rooms - stores all the rooms allotted in the given hall. - """ - # Query the hall by hall_id - hall = Hall.objects.get(hall_id=hall_id) - # Query all rooms allotted in the given hall - allotted_rooms = HallRoom.objects.filter(hall=hall, room_occupied__gt=0) - # Prepare a list of room details to be returned - room_details = [] - for room in allotted_rooms: - room_details.append({ - 'hall': room.hall.hall_id, - 'room_no': room.room_no, - 'block_no': room.block_no, - 'room_cap': room.room_cap, - 'room_occupied': room.room_occupied - }) - return JsonResponse(room_details, safe=False) - - -def alloted_rooms_main(request): - """ - This function returns the allotted rooms in all halls. - - @param: - request - HttpRequest object containing metadata about the user request. - - @variables: - all_halls - stores all the halls. - all_rooms - stores all the rooms allotted in all halls. - """ - # Query all halls - all_halls = Hall.objects.all() - - # Query all rooms allotted in all halls - all_rooms = [] - for hall in all_halls: - all_rooms.append(HallRoom.objects.filter( - hall=hall, room_occupied__gt=0)) - - # Prepare a list of room details to be returned - room_details = [] - for rooms in all_rooms: - for room in rooms: - room_details.append({ - 'hall': room.hall.hall_name, - 'room_no': room.room_no, - 'block_no': room.block_no, - 'room_cap': room.room_cap, - 'room_occupied': room.room_occupied - }) - - # Return the room_details as JSON response - return render(request, 'hostelmanagement/alloted_rooms_main.html', {'allotted_rooms': room_details, 'halls': all_halls}) - - -# //! all_staff -def all_staff(request, hall_id): - """ - This function returns all staff information for a specific hall. - - @param: - request - HttpRequest object containing metadata about the user request. - hall_id - The ID of the hall for which staff information is requested. - - - @variables: - all_staff - stores all staff information for the specified hall. - """ - - # Query all staff information for the specified hall - all_staff = StaffSchedule.objects.filter(hall_id=hall_id) - - # Prepare a list of staff details to be returned - staff_details = [] - for staff in all_staff: - staff_details.append({ - 'type': staff.staff_type, - 'staff_id': staff.staff_id_id, - 'hall_id': staff.hall_id, - 'day': staff.day, - 'start_time': staff.start_time, - 'end_time': staff.end_time - }) - - # Return the staff_details as JSON response - return JsonResponse(staff_details, safe=False) - - -# //! Edit Stuff schedule -class StaffScheduleView(APIView): - """ - API endpoint for creating or editing staff schedules. - """ - - authentication_classes = [] # Allow public access for testing - permission_classes = [] # Allow any user to access the view - - def patch(self, request, staff_id): - staff = get_object_or_404(Staff, pk=staff_id) - staff_type = request.data.get('staff_type') - start_time = request.data.get('start_time') - end_time = request.data.get('end_time') - day = request.data.get('day') - - - if start_time and end_time and day and staff_type: - # Check if staff schedule exists for the given day - existing_schedule = StaffSchedule.objects.filter( - staff_id=staff_id).first() - if existing_schedule: - existing_schedule.start_time = start_time - existing_schedule.end_time = end_time - existing_schedule.day = day - existing_schedule.staff_type = staff_type - existing_schedule.save() - return Response({"message": "Staff schedule updated successfully."}, status=status.HTTP_200_OK) - else: - # If staff schedule doesn't exist for the given day, return 404 - return Response({"error": "Staff schedule does not exist for the given day."}, status=status.HTTP_404_NOT_FOUND) - - return Response({"error": "Please provide start_time, end_time, and day."}, status=status.HTTP_400_BAD_REQUEST) - - -# //! Hostel Inventory - -@login_required -def get_inventory_form(request): - user_id = request.user - # print("user_id",user_id) - staff = user_id.extrainfo.id - # print("staff",staff) - - # Check if the user is present in the HallCaretaker table - if HallCaretaker.objects.filter(staff_id=staff).exists(): - # If the user is a caretaker, allow access - halls = Hall.objects.all() - return render(request, 'hostelmanagement/inventory_form.html', {'halls': halls}) - else: - # If the user is not a caretaker, redirect to the login page - # return redirect('login') # Adjust 'login' to your login URL name - return HttpResponse(f'') - - -@login_required -def edit_inventory(request, inventory_id): - # Retrieve hostel inventory object - inventory = get_object_or_404(HostelInventory, pk=inventory_id) - - # Check if the user is a caretaker - user_id = request.user - staff_id = user_id.extrainfo.id - - if HallCaretaker.objects.filter(staff_id=staff_id).exists(): - halls = Hall.objects.all() - - # Prepare inventory data for rendering - inventory_data = { - 'inventory_id': inventory.inventory_id, - 'hall_id': inventory.hall_id, - 'inventory_name': inventory.inventory_name, - 'cost': str(inventory.cost), # Convert DecimalField to string - 'quantity': inventory.quantity, - } - - # Render the inventory update form with inventory data - return render(request, 'hostelmanagement/inventory_update_form.html', {'inventory': inventory_data, 'halls': halls}) - else: - # If the user is not a caretaker, show a message and redirect - return HttpResponse('') - - -class HostelInventoryUpdateView(APIView): - authentication_classes = [SessionAuthentication] - permission_classes = [IsAuthenticated] - - @method_decorator(login_required) - def dispatch(self, *args, **kwargs): - return super().dispatch(*args, **kwargs) - - def post(self, request, inventory_id): - user_id = request.user - staff_id = user_id.extrainfo.id - - if not HallCaretaker.objects.filter(staff_id=staff_id).exists(): - return Response({'error': 'You are not authorized to update this hostel inventory'}, status=status.HTTP_401_UNAUTHORIZED) - - hall_id = request.data.get('hall_id') - inventory_name = request.data.get('inventory_name') - cost = request.data.get('cost') - quantity = request.data.get('quantity') - - # Validate required fields - if not all([hall_id, inventory_name, cost, quantity]): - return Response({'error': 'All fields are required'}, status=status.HTTP_400_BAD_REQUEST) - - # Retrieve hostel inventory object - hostel_inventory = get_object_or_404(HostelInventory, pk=inventory_id) - - # Update hostel inventory object - hostel_inventory.hall_id = hall_id - hostel_inventory.inventory_name = inventory_name - hostel_inventory.cost = cost - hostel_inventory.quantity = quantity - hostel_inventory.save() - - # Return success response - return Response({'message': 'Hostel inventory updated successfully'}, status=status.HTTP_200_OK) - - -class HostelInventoryView(APIView): - """ - API endpoint for CRUD operations on hostel inventory. - """ - # permission_classes = [IsAuthenticated] - - # authentication_classes = [] # Allow public access for testing - # permission_classes = [] # Allow any user to access the view - - authentication_classes = [SessionAuthentication] - permission_classes = [IsAuthenticated] - - @method_decorator(login_required) - def dispatch(self, *args, **kwargs): - return super().dispatch(*args, **kwargs) - - def get(self, request, hall_id): - user_id = request.user - staff_id = user_id.extrainfo.id - - if not HallCaretaker.objects.filter(staff_id=staff_id).exists(): - return HttpResponse('') - - # Retrieve hostel inventory objects for the given hall ID - inventories = HostelInventory.objects.filter(hall_id=hall_id) - - # Get all hall IDs - halls = Hall.objects.all() - - # Serialize inventory data - inventory_data = [] - for inventory in inventories: - inventory_data.append({ - 'inventory_id': inventory.inventory_id, - 'hall_id': inventory.hall_id, - 'inventory_name': inventory.inventory_name, - 'cost': str(inventory.cost), # Convert DecimalField to string - 'quantity': inventory.quantity, - }) - - inventory_data.sort(key=lambda x: x['inventory_id']) - - # Return inventory data as JSON response - return render(request, 'hostelmanagement/inventory_list.html', {'halls': halls, 'inventories': inventory_data}) - - def post(self, request): - user_id = request.user - staff_id = user_id.extrainfo.id - - if not HallCaretaker.objects.filter(staff_id=staff_id).exists(): - return Response({'error': 'You are not authorized to create a new hostel inventory'}, status=status.HTTP_401_UNAUTHORIZED) - - # Extract data from request - hall_id = request.data.get('hall_id') - inventory_name = request.data.get('inventory_name') - cost = request.data.get('cost') - quantity = request.data.get('quantity') - - # Validate required fields - if not all([hall_id, inventory_name, cost, quantity]): - return Response({'error': 'All fields are required'}, status=status.HTTP_400_BAD_REQUEST) - - # Create hostel inventory object - try: - hostel_inventory = HostelInventory.objects.create( - hall_id=hall_id, - inventory_name=inventory_name, - cost=cost, - quantity=quantity - ) - return Response({'message': 'Hostel inventory created successfully', 'hall_id': hall_id}, status=status.HTTP_201_CREATED) - except Exception as e: - return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) - - def delete(self, request, inventory_id): - user_id = request.user - staff_id = user_id.extrainfo.id - - if not HallCaretaker.objects.filter(staff_id=staff_id).exists(): - return Response({'error': 'You are not authorized to delete this hostel inventory'}, status=status.HTTP_401_UNAUTHORIZED) - - inventory = get_object_or_404(HostelInventory, pk=inventory_id) - inventory.delete() - return Response({'message': 'Hostel inventory deleted successfully'}, status=status.HTTP_204_NO_CONTENT) - - -def update_allotment(request, pk): - if request.method == 'POST': - try: - allotment = HostelAllottment.objects.get(pk=pk) - except HostelAllottment.DoesNotExist: - return JsonResponse({'error': 'HostelAllottment not found'}, status=404) - - try: - allotment.assignedWarden = Faculty.objects.get( - id=request.POST['warden_id']) - allotment.assignedCaretaker = Staff.objects.get( - id=request.POST['caretaker_id']) - allotment.assignedBatch = request.POST.get( - 'student_batch', allotment.assignedBatch) - allotment.save() - return JsonResponse({'success': 'HostelAllottment updated successfully'}) - except (Faculty.DoesNotExist, Staff.DoesNotExist, IntegrityError): - return JsonResponse({'error': 'Invalid data or integrity error'}, status=400) - - return JsonResponse({'error': 'Invalid request method'}, status=405) - - -@login_required -def request_guest_room(request): - """ - This function is used by the student to book a guest room. - @param: - request - HttpRequest object containing metadata about the user request. - """ - if request.method == "POST": - form = GuestRoomBookingForm(request.POST) - - if form.is_valid(): - # print("Inside valid") - hall = form.cleaned_data['hall'] - guest_name = form.cleaned_data['guest_name'] - guest_phone = form.cleaned_data['guest_phone'] - guest_email = form.cleaned_data['guest_email'] - guest_address = form.cleaned_data['guest_address'] - rooms_required = form.cleaned_data['rooms_required'] - total_guest = form.cleaned_data['total_guest'] - purpose = form.cleaned_data['purpose'] - arrival_date = form.cleaned_data['arrival_date'] - arrival_time = form.cleaned_data['arrival_time'] - departure_date = form.cleaned_data['departure_date'] - departure_time = form.cleaned_data['departure_time'] - nationality = form.cleaned_data['nationality'] - room_type = form.cleaned_data['room_type'] # Add room type - - - max_guests = { - 'single': 1, - 'double': 2, - 'triple': 3, - } - # Fetch available room count based on room type and hall - available_rooms_count = GuestRoom.objects.filter( - hall=hall, room_type=room_type, vacant=True - ).count() - - # Check if there are enough available rooms - if available_rooms_count < rooms_required: - messages.error(request, "Not enough available rooms.") - return HttpResponseRedirect(reverse("hostelmanagement:hostel_view")) - - # Check if the number of guests exceeds the capacity of selected rooms - if total_guest > rooms_required * max_guests.get(room_type, 1): - messages.error(request, "Number of guests exceeds the capacity of selected rooms.") - return HttpResponseRedirect(reverse("hostelmanagement:hostel_view")) - - - newBooking = GuestRoomBooking.objects.create(hall=hall, intender=request.user, guest_name=guest_name, guest_address=guest_address, - guest_phone=guest_phone, guest_email=guest_email, rooms_required=rooms_required, total_guest=total_guest, purpose=purpose, - arrival_date=arrival_date, arrival_time=arrival_time, departure_date=departure_date, departure_time=departure_time, nationality=nationality,room_type=room_type) - newBooking.save() - messages.success(request, "Room request submitted successfully!") - - - # Get the caretaker for the selected hall - hall_caretaker = HallCaretaker.objects.get(hall=hall) - caretaker = hall_caretaker.staff.id.user - # Send notification to caretaker - hostel_notifications(sender=request.user, recipient=caretaker, type='guestRoom_request') - - return HttpResponseRedirect(reverse("hostelmanagement:hostel_view")) - else: - messages.error(request, "Something went wrong") - return HttpResponseRedirect(reverse("hostelmanagement:hostel_view")) - - -@login_required -def update_guest_room(request): - if request.method == "POST": - if 'accept_request' in request.POST: - status = request.POST['status'] - guest_room_request = GuestRoomBooking.objects.get( - pk=request.POST['accept_request']) - guest_room_instance = GuestRoom.objects.get( - hall=guest_room_request.hall, room=request.POST['guest_room_id']) - - # Assign the guest room ID to guest_room_id field - guest_room_request.guest_room_id = str(guest_room_instance.id) - - # Update the assigned guest room's occupancy details - guest_room_instance.occupied_till = guest_room_request.departure_date - guest_room_instance.vacant = False # Mark the room as occupied - guest_room_instance.save() - - # Update the occupied_till field of the room_booked - room_booked = GuestRoom.objects.get( - hall=guest_room_request.hall, room=request.POST['guest_room_id']) - room_booked.occupied_till = guest_room_request.departure_date - room_booked.save() - - # Save the guest room request after updating the fields - guest_room_request.status = status - guest_room_request.save() - messages.success(request, "Request accepted successfully!") - - hostel_notifications(sender=request.user,recipient=guest_room_request.intender,type='guestRoom_accept') - - - elif 'reject_request' in request.POST: - guest_room_request = GuestRoomBooking.objects.get( - pk=request.POST['reject_request']) - guest_room_request.status = 'Rejected' - guest_room_request.save() - - messages.success(request, "Request rejected successfully!") - - hostel_notifications(sender=request.user,recipient=guest_room_request.intender,type='guestRoom_reject') - - else: - messages.error(request, "Invalid request!") - return HttpResponseRedirect(reverse("hostelmanagement:hostel_view")) - - -def available_guestrooms_api(request): - if request.method == 'GET': - - hall_id = request.GET.get('hall_id') - room_type = request.GET.get('room_type') - - if hall_id and room_type: - available_rooms_count = GuestRoom.objects.filter(hall_id=hall_id, room_type=room_type, vacant=True).count() - return JsonResponse({'available_rooms_count': available_rooms_count}) - - return JsonResponse({'error': 'Invalid request'}, status=400) - - -# //Caretaker can approve or reject leave applied by the student -@csrf_exempt -def update_leave_status(request): - if request.method == 'POST': - leave_id = request.POST.get('leave_id') - status = request.POST.get('status') - try: - leave = HostelLeave.objects.get(id=leave_id) - leave.status = status - leave.remark = request.POST.get('remark') - leave.save() - - # Send notification to the student - sender = request.user # Assuming request.user is the caretaker - - student_id = leave.roll_num # Assuming student is a foreign key field in HostelLeave model - recipient = User.objects.get(username=student_id) - type = "leave_accept" if status == "Approved" else "leave_reject" - hostel_notifications(sender, recipient, type) - - return JsonResponse({'status': status,'remarks':leave.remark,'message': 'Leave status updated successfully.'}) - except HostelLeave.DoesNotExist: - return JsonResponse({'status': 'error', 'message': 'Leave not found.'}, status=404) - else: - return JsonResponse({'status': 'error', 'message': 'Only POST requests are allowed.'}, status=405) - - -# //! Manage Fine -# //! Add Fine Functionality - - -@login_required -def show_fine_edit_form(request,fine_id): - user_id = request.user - staff = user_id.extrainfo.id - caretaker = HallCaretaker.objects.get(staff_id=staff) - hall_id = caretaker.hall_id - - fine = HostelFine.objects.filter(fine_id=fine_id) - - - - return render(request, 'hostelmanagement/impose_fine_edit.html', {'fines': fine[0]}) - -@login_required -def update_student_fine(request,fine_id): - if request.method == 'POST': - fine = HostelFine.objects.get(fine_id=fine_id) - print("------------------------------------------------") - print(request.POST) - fine.amount = request.POST.get('amount') - fine.status = request.POST.get('status') - fine.reason = request.POST.get('reason') - fine.save() - - return HttpResponse({'message': 'Fine has edited successfully'}, status=status.HTTP_200_OK) - - -@login_required -def impose_fine_view(request): - user_id = request.user - staff = user_id.extrainfo.id - students = Student.objects.all() - - if HallCaretaker.objects.filter(staff_id=staff).exists(): - return render(request, 'hostelmanagement/impose_fine.html', {'students': students}) - - return HttpResponse(f'') - - -class HostelFineView(APIView): - """ - API endpoint for imposing fines on students. - """ - authentication_classes = [SessionAuthentication] - permission_classes = [IsAuthenticated] - - @method_decorator(login_required) - def dispatch(self, *args, **kwargs): - return super().dispatch(*args, **kwargs) - - def post(self, request): - # Check if the user is a caretaker - user_id = request.user - staff = user_id.extrainfo.id - - try: - caretaker = HallCaretaker.objects.get(staff_id=staff) - except HallCaretaker.DoesNotExist: - return HttpResponse(f'') - - hall_id = caretaker.hall_id - - # Extract data from the request - student_id = request.data.get('student_id') - student_name = request.data.get('student_fine_name') - amount = request.data.get('amount') - reason = request.data.get('reason') - - # Validate the data - if not all([student_id, student_name, amount, reason]): - return HttpResponse({'error': 'Incomplete data provided.'}, status=status.HTTP_400_BAD_REQUEST) - - # Create the HostelFine object - try: - fine = HostelFine.objects.create( - student_id=student_id, - student_name=student_name, - amount=amount, - reason=reason, - hall_id=hall_id - ) - # Sending notification to the student about the imposed fine - - - - recipient = User.objects.get(username=student_id) - - sender = request.user - - type = "fine_imposed" - hostel_notifications(sender, recipient, type) - - return HttpResponse({'message': 'Fine imposed successfully.'}, status=status.HTTP_201_CREATED) - except Exception as e: - return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) - - -@login_required -def get_student_name(request, username): - try: - user = User.objects.get(username=username) - full_name = f"{user.first_name} {user.last_name}" if user.first_name or user.last_name else "" - return JsonResponse({"name": full_name}) - except User.DoesNotExist: - return JsonResponse({"error": "User not found"}, status=404) - - -@login_required -def hostel_fine_list(request): - user_id = request.user - staff = user_id.extrainfo.id - caretaker = HallCaretaker.objects.get(staff_id=staff) - hall_id = caretaker.hall_id - hostel_fines = HostelFine.objects.filter( - hall_id=hall_id).order_by('fine_id') - - if HallCaretaker.objects.filter(staff_id=staff).exists(): - return render(request, 'hostelmanagement/hostel_fine_list.html', {'hostel_fines': hostel_fines}) - - return HttpResponse(f'') - - -@login_required -def student_fine_details(request): - user_id = request.user.username - # print(user_id) - # staff=user_id.extrainfo.id - - # Check if the user_id exists in the Student table - # if HallCaretaker.objects.filter(staff_id=staff).exists(): - # return HttpResponse('') - - if not Student.objects.filter(id_id=user_id).exists(): - return HttpResponse('') - - # # Check if the user_id exists in the HostelFine table - if not HostelFine.objects.filter(student_id=user_id).exists(): - return HttpResponse('') - - # # Retrieve the fines associated with the current student - student_fines = HostelFine.objects.filter(student_id=user_id) - - return render(request, 'hostelmanagement/student_fine_details.html', {'student_fines': student_fines}) - - # return JsonResponse({'message': 'Nice'}, status=status.HTTP_200_OK) - - -class HostelFineUpdateView(APIView): - authentication_classes = [SessionAuthentication] - permission_classes = [IsAuthenticated] - - @method_decorator(login_required) - def dispatch(self, *args, **kwargs): - return super().dispatch(*args, **kwargs) - - def post(self, request, fine_id): - user_id = request.user - staff = user_id.extrainfo.id - - data = request.data - fine_idd = data.get('fine_id') - status_ = data.get('status') - # print("fine_idd",fine_idd) - # print("status_",status_) - - try: - caretaker = HallCaretaker.objects.get(staff_id=staff) - except HallCaretaker.DoesNotExist: - return Response({'error': 'You are not authorized to access this page'}, status=status.HTTP_403_FORBIDDEN) - - hall_id = caretaker.hall_id - - # Convert fine_id to integer - fine_id = int(fine_id) - - # Get hostel fine object - try: - hostel_fine = HostelFine.objects.get( - hall_id=hall_id, fine_id=fine_id) - except HostelFine.DoesNotExist: - raise NotFound(detail="Hostel fine not found") - - # Validate required fields - if status_ not in ['Pending', 'Paid']: - return Response({'error': 'Invalid status value'}, status=status.HTTP_400_BAD_REQUEST) - - # # Update status of the hostel fine - hostel_fine.status = status_ - hostel_fine.save() - - # Return success response - return Response({'message': 'Hostel fine status updated successfully!'}, status=status.HTTP_200_OK) - - def delete(self, request, fine_id): - user_id = request.user - staff = user_id.extrainfo.id - - try: - caretaker = HallCaretaker.objects.get(staff_id=staff) - except HallCaretaker.DoesNotExist: - return Response({'error': 'You are not authorized to access this page'}, status=status.HTTP_403_FORBIDDEN) - - hall_id = caretaker.hall_id - - # Convert fine_id to integer - fine_id = int(fine_id) - - # Get hostel fine object - try: - hostel_fine = HostelFine.objects.get( - hall_id=hall_id, fine_id=fine_id) - hostel_fine.delete() - except HostelFine.DoesNotExist: - raise NotFound(detail="Hostel fine not found") - - return Response({'message': 'Fine deleted successfully.'}, status=status.HTTP_204_NO_CONTENT) - - - - -class EditStudentView(View): - template_name = 'hostelmanagement/edit_student.html' - - def get(self, request, student_id): - student = Student.objects.get(id=student_id) - - context = {'student': student} - return render(request, self.template_name, context) - - def post(self, request, student_id): - student = Student.objects.get(id=student_id) - - # Update student details - student.id.user.first_name = request.POST.get('first_name') - student.id.user.last_name = request.POST.get('last_name') - student.programme = request.POST.get('programme') - student.batch = request.POST.get('batch') - student.hall_no = request.POST.get('hall_number') - student.room_no = request.POST.get('room_number') - student.specialization = request.POST.get('specialization') - - student.save() - - # Update phone number and address from ExtraInfo model - student.id.phone_no = request.POST.get('phone_number') - student.id.address = request.POST.get('address') - student.id.save() - student.save() - messages.success(request, 'Student details updated successfully.') - return redirect("hostelmanagement:hostel_view") - -class RemoveStudentView(View): - def post(self, request, student_id): - try: - student = Student.objects.get(id=student_id) - student.hall_no = 0 - student.save() - messages.success(request, 'Student removed successfully.') - return redirect("hostelmanagement:hostel_view") - return JsonResponse({'status': 'success', 'message': 'Student removed successfully'}) - except Student.DoesNotExist: - return JsonResponse({'status': 'error', 'message': 'Student not found'}, status=404) - except Exception as e: - return JsonResponse({'status': 'error', 'message': str(e)}, status=500) - - def dispatch(self, request, *args, **kwargs): - if request.method != 'POST': - return JsonResponse({'status': 'error', 'message': 'Method Not Allowed'}, status=405) - return super().dispatch(request, *args, **kwargs) - - diff --git a/FusionIIIT/applications/hr2/a.py b/FusionIIIT/applications/hr2/a.py deleted file mode 100644 index a308ef3ad..000000000 --- a/FusionIIIT/applications/hr2/a.py +++ /dev/null @@ -1,75 +0,0 @@ -def reverse_ltc_pre_processing(data): - reversed_data = {} - - # Copying over simple key-value pairs - simple_keys = [ - 'block_year', 'pf_no', 'basic_pay_salary', 'name', 'designation', 'department_info', - 'leave_availability', 'leave_start_date', 'leave_end_date', 'date_of_leave_for_family', - 'nature_of_leave', 'purpose_of_leave', 'hometown_or_not', 'place_of_visit', - 'address_during_leave', 'amount_of_advance_required', 'certified_family_dependents', - 'certified_advance', 'adjusted_month', 'date', 'phone_number_for_contact' - ] - for key in simple_keys: - value = data[key] - reversed_data[key] = value if value != 'None' else '' - - # Reversing array-like values - reversed_data['details_of_family_members_already_done'] = data['details_of_family_members_already_done'].split(',') - - family_members_about_to_avail = data['family_members_about_to_avail'].split(',') - for index, value in enumerate(family_members_about_to_avail): - family_members_about_to_avail[index] = value if value != 'None' else '' - - reversed_data['info_1_1'] = family_members_about_to_avail[0] - reversed_data['info_1_2'] = family_members_about_to_avail[1] - reversed_data['info_1_3'] = family_members_about_to_avail[2] - reversed_data['info_2_1'] = family_members_about_to_avail[3] - reversed_data['info_2_2'] = family_members_about_to_avail[4] - reversed_data['info_2_3'] = family_members_about_to_avail[5] - reversed_data['info_3_1'] = family_members_about_to_avail[6] - reversed_data['info_3_2'] = family_members_about_to_avail[7] - reversed_data['info_3_3'] = family_members_about_to_avail[8] - - # Reversing details_of_dependents - details_of_dependents = data['details_of_dependents'].split(',') - for i in range(1, 7): - for j in range(1, 4): - key = f'd_info_{i}_{j}' - value = details_of_dependents.pop(0) - reversed_data[key] = value if value != 'None' else '' - - return reversed_data - -# Sample data -data = { - 'block_year': '232', 'pf_no': '4324', 'basic_pay_salary': '324', 'name': 'sdf', 'designation': 'fds', - 'department_info': 'dfs', 'leave_availability': 'True', 'leave_start_date': '2024-03-13', - 'leave_end_date': '2024-03-17', 'date_of_leave_for_family': '2024-03-16', 'nature_of_leave': 'erds', - 'purpose_of_leave': 'fds', 'hometown_or_not': 'True', 'place_of_visit': 'fds', 'address_during_leave': 'dfsfsdf', - 'details_of_family_members_already_done': 'fds,dfs,dfs', 'family_members_about_to_avail': '1,dfsf,21,2,dsf,23,3,dfs,12', - 'details_of_dependents': '1,ds,12,2,sds,2,3,ds,13,None,None,None,None,None,None,None,None,None', 'amount_of_advance_required': '1221', - 'certified_family_dependents': '213', 'certified_advance': '213', 'adjusted_month': '213', 'date': '2024-03-15', - 'phone_number_for_contact': '21313123132' -} - -# Reverse processing -reversed_data = reverse_ltc_pre_processing(data) -print(reversed_data) - - - -{'block_year': 232, 'pf_no': None, 'basic_pay_salary': 324, 'name': 'sdf', 'designation': 'fds', - 'department_info': 'dfs', 'leave_availability': True, 'leave_start_date': datetime.date(2024, 3, 13), - 'leave_end_date': datetime.date(2024, 3, 17), 'date_of_leave_for_family': datetime.date(2024, 3, 16), - 'nature_of_leave': 'erds', 'purpose_of_leave': 'fds', 'hometown_or_not': True, 'place_of_visit': 'fds', - 'address_during_leave': 'dfsfsdf', 'amount_of_advance_required': 1221, 'certified_family_dependents': '213', - 'certified_advance': 213, 'adjusted_month': '213', 'date': datetime.date(2024, 3, 15), - 'phone_number_for_contact': 21313123132, - 'details_of_family_members_already_done': ['fds', 'dfs', 'dfs'], - 'info_1_1': '1', 'info_1_2': 'dfsf', 'info_1_3': '21', 'info_2_1': - '2', 'info_2_2': 'dsf', 'info_2_3': '23', 'info_3_1': '3', 'info_3_2': - 'dfs', 'info_3_3': '12', 'd_info_1_1': '1', 'd_info_1_2': 'ds', 'd_info_1_3': - '12', 'd_info_2_1': '2', 'd_info_2_2': 'sds', 'd_info_2_3': '2', 'd_info_3_1': '3', - 'd_info_3_2': 'ds', 'd_info_3_3': '13', 'd_info_4_1': '', 'd_info_4_2': '', 'd_info_4_3': '', - - 'd_info_5_1': '', 'd_info_5_2': '', 'd_info_5_3': '', 'd_info_6_1': '', 'd_info_6_2': '', 'd_info_6_3': ''} diff --git a/FusionIIIT/applications/hr2/admin.py b/FusionIIIT/applications/hr2/admin.py index 3c9b8379a..ddd58944c 100644 --- a/FusionIIIT/applications/hr2/admin.py +++ b/FusionIIIT/applications/hr2/admin.py @@ -1,7 +1,7 @@ from django.contrib import admin -from .models import * -# from .models import CPDAReimbursementform +from .models import Employee,EmpConfidentialDetails,ForeignService,EmpDependents,EmpAppraisalForm,WorkAssignemnt + # Register your models here. admin.site.register(Employee) @@ -9,10 +9,4 @@ admin.site.register(EmpDependents) admin.site.register(ForeignService) admin.site.register(EmpAppraisalForm) -admin.site.register(WorkAssignemnt) -admin.site.register(LeaveBalance) -admin.site.register(LeaveForm) -admin.site.register(LTCform) -admin.site.register(Appraisalform) -admin.site.register(CPDAAdvanceform) -admin.site.register(CPDAReimbursementform) \ No newline at end of file +admin.site.register(WorkAssignemnt) \ No newline at end of file diff --git a/FusionIIIT/applications/hr2/api/form_views.py b/FusionIIIT/applications/hr2/api/form_views.py deleted file mode 100644 index 984e1fbaf..000000000 --- a/FusionIIIT/applications/hr2/api/form_views.py +++ /dev/null @@ -1,546 +0,0 @@ -from .serializers import LTC_serializer, CPDAAdvance_serializer, Appraisal_serializer, CPDAReimbursement_serializer, Leave_serializer, LeaveBalanace_serializer -from rest_framework.views import APIView -from rest_framework.response import Response -from rest_framework import status -# from rest_framework.decorators import permission_classes, api_view -from rest_framework.permissions import IsAuthenticated -from applications.hr2.models import LTCform, CPDAAdvanceform, CPDAReimbursementform, LeaveForm, Appraisalform, LeaveBalance -from django.contrib.auth import get_user_model -from django.core.exceptions import MultipleObjectsReturned -from applications.filetracking.sdk.methods import * -from applications.globals.models import Designation, HoldsDesignation, ExtraInfo -from applications.filetracking.models import * -# from django.contrib.auth.models import User - - -class LTC(APIView): - serializer_class = LTC_serializer - permission_classes = (IsAuthenticated, ) - - def post(self, request): - print("hello") - user_info = request.data[1] - print(request.data[1]) - serializer = self.serializer_class(data=request.data[0]) - if serializer.is_valid(): - serializer.save() - fileId = create_file(uploader=user_info['uploader_name'], uploader_designation=user_info['uploader_designation'], receiver=user_info['receiver_name'], - receiver_designation=user_info['receiver_designation'], src_module="HR", src_object_id=str(serializer.data['id']), file_extra_JSON={"type": "LTC"}, attached_file=None) - # forwarded = forward_file(file_id=fileId, receiver=user_info['receiver_name'], receiver_designation=user_info['receiver_designation'], - # remarks="Forwarded to Receiver Inbox", file_extra_JSON={"type": "LTC"}) - return Response(serializer.data, status=status.HTTP_200_OK) - else: - print(serializer.errors) - return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) - - def get(self, request, *args, **kwargs): - pk = request.query_params.get("name") - try: - forms = LTCform.objects.get(created_by=pk) - serializer = self.serializer_class(forms, many=False) - except MultipleObjectsReturned: - forms = LTCform.objects.filter(created_by=pk) - serializer = self.serializer_class(forms, many=True) - return Response(serializer.data, status=status.HTTP_200_OK) - - def put(self, request, *args, **kwargs): - pk = request.query_params.get("id") - receiver = request.data[0] - # send_to = receiver['receiver'] - # receiver_value = User.objects.get(username=send_to) - # receiver_value_designation = HoldsDesignation.objects.filter( - # user=receiver_value) - # lis = list(receiver_value_designation) - # obj = lis[0].designation - form = LTCform.objects.get(id=pk) - serializer = self.serializer_class(form, data=request.data[1]) - if serializer.is_valid(): - serializer.save() - forward_file(file_id=receiver['file_id'], receiver=receiver['receiver'], receiver_designation=receiver['receiver_designation'], - remarks=receiver['remarks'], file_extra_JSON=receiver['file_extra_JSON']) - return Response(serializer.data, status=status.HTTP_200_OK) - else: - return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) - - def delete(self, request, *args, **kwargs): - id = request.query_params.get("id") - is_archived = archive_file(file_id=id) - if (is_archived): - return Response(status=status.HTTP_200_OK) - else: - return Response(status=status.HTTP_400_BAD_REQUEST) - - -class FormManagement(APIView): - permission_classes = (IsAuthenticated, ) - - def get(self, request, *args, **kwargs): - username = request.query_params.get("username") - designation = request.query_params.get("designation") - inbox = view_inbox(username=username, - designation=designation, src_module="HR") - print(inbox) - return Response(inbox, status=status.HTTP_200_OK) - - def post(self, request, *args, **kwargs): - username = request.data['receiver'] - receiver_value = User.objects.get(username=username) - receiver_value_designation = HoldsDesignation.objects.filter( - user=receiver_value) - lis = list(receiver_value_designation) - obj = lis[0].designation - forward_file(file_id=request.data['file_id'], receiver=request.data['receiver'], receiver_designation=request.data['receiver_designation'], - remarks=request.data['remarks'], file_extra_JSON=request.data['file_extra_JSON']) - return Response(status=status.HTTP_200_OK) - - -class CPDAAdvance(APIView): - serializer_class = CPDAAdvance_serializer - permission_classes = (IsAuthenticated, ) - - def post(self, request): - print(request.data[0]) - user_info = request.data[1] - # receiver_value = User.objects.get(username=user_info['receiver_name']) - # receiver_value_designation = HoldsDesignation.objects.filter( - # user=receiver_value) - # lis = list(receiver_value_designation) - # obj = lis[0].designation - serializer = self.serializer_class(data=request.data[0]) - if serializer.is_valid(): - serializer.save() - print('1') - fileId = create_file(uploader=user_info['uploader_name'], uploader_designation=user_info['uploader_designation'], receiver=user_info['receiver_name'], - receiver_designation=user_info['receiver_designation'], src_module="HR", src_object_id=str(serializer.data['id']), file_extra_JSON={"type": "CPDAAdvance"}, attached_file=None) - # forwarded = forward_file(file_id=fileId, receiver=user_info['receiver_name'], receiver_designation=user_info['receiver_designation'], - # remarks="Forwarded to Receiver Inbox", file_extra_JSON={"type": "CPDAAdvance"}) - return Response(serializer.data, status=status.HTTP_200_OK) - else: - return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) - - def get(self, request, *args, **kwargs): - pk = request.query_params.get("name") - try: - forms = CPDAAdvanceform.objects.get(created_by=pk) - serializer = self.serializer_class(forms, many=False) - except MultipleObjectsReturned: - forms = CPDAAdvanceform.objects.filter(created_by=pk) - serializer = self.serializer_class(forms, many=True) - return Response(serializer.data, status=status.HTTP_200_OK) - - def put(self, request, *args, **kwargs): - pk = request.query_params.get("id") - receiver = request.data[0] - print(request.data) - send_to = receiver['receiver'] - print(send_to) - receiver_value = User.objects.get(username=send_to) - receiver_value_designation = HoldsDesignation.objects.filter( - user=receiver_value) - lis = list(receiver_value_designation) - obj = lis[0].designation - form = CPDAAdvanceform.objects.get(id=pk) - serializer = self.serializer_class(form, data=request.data[1]) - if serializer.is_valid(): - serializer.save() - forward_file(file_id=receiver['file_id'], receiver=receiver['receiver'], receiver_designation=receiver['receiver_designation'], - remarks=receiver['remarks'], file_extra_JSON=receiver['file_extra_JSON']) - return Response(serializer.data, status=status.HTTP_200_OK) - else: - print(serializer.errors) - return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) - - def delete(self, request, *args, **kwargs): - id = request.query_params.get("id") - is_archived = archive_file(file_id=id) - if (is_archived): - return Response(status=status.HTTP_200_OK) - else: - return Response(status=status.HTTP_400_BAD_REQUEST) - - -class CPDAReimbursement(APIView): - serializer_class = CPDAReimbursement_serializer - permission_classes = (IsAuthenticated, ) - - def post(self, request): - user_info = request.data[1] - serializer = self.serializer_class(data=request.data[0]) - if serializer.is_valid(): - serializer.save() - fileId = create_file(uploader=user_info['uploader_name'], uploader_designation=user_info['uploader_designation'], receiver=user_info['receiver_name'], - receiver_designation=user_info['receiver_designation'], src_module="HR", src_object_id=str(serializer.data['id']), file_extra_JSON={"type": "CPDAReimbursement"}, attached_file=None) - # forwarded = forward_file(file_id=fileId, receiver=user_info['receiver_name'], receiver_designation=user_info['receiver_designation'], - # remarks="Forwarded to Receiver Inbox", file_extra_JSON={"type": "CPDAReimbursement"}) - return Response(serializer.data, status=status.HTTP_200_OK) - else: - print(serializer.errors) - return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) - - def get(self, request, *args, **kwargs): - pk = request.query_params.get("name") - try: - forms = CPDAReimbursementform.objects.get(created_by=pk) - serializer = self.serializer_class(forms, many=False) - except MultipleObjectsReturned: - forms = CPDAReimbursementform.objects.filter(created_by=pk) - serializer = self.serializer_class(forms, many=True) - return Response(serializer.data, status=status.HTTP_200_OK) - - def put(self, request, *args, **kwargs): - pk = request.query_params.get("id") - print(request.data) - receiver = request.data[0] - # send_to = receiver['receiver'] - # receiver_value = User.objects.get(username=send_to) - # receiver_value_designation = HoldsDesignation.objects.filter( - # user=receiver_value) - # lis = list(receiver_value_designation) - # obj = lis[0].designation - form = CPDAReimbursementform.objects.get(id=pk) - serializer = self.serializer_class(form, data=request.data[1]) - if serializer.is_valid(): - serializer.save() - forward_file(file_id=receiver['file_id'], receiver=receiver['receiver'], receiver_designation=receiver['receiver_designation'], - remarks=receiver['remarks'], file_extra_JSON=receiver['file_extra_JSON']) - return Response(serializer.data, status=status.HTTP_200_OK) - else: - print(serializer.errors) - return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) - - def delete(self, request, *args, **kwargs): - id = request.query_params.get("id") - is_archived = archive_file(file_id=id) - if (is_archived): - return Response(status=status.HTTP_200_OK) - else: - return Response(status=status.HTTP_400_BAD_REQUEST) - - -class Leave(APIView): - serializer_class = Leave_serializer - permission_classes = (IsAuthenticated, ) - - def post(self, request): - user_info = request.data[1] - serializer = self.serializer_class(data=request.data[0]) - if serializer.is_valid(): - serializer.save() - fileId = create_file(uploader=user_info['uploader_name'], uploader_designation=user_info['uploader_designation'], receiver=user_info['receiver_name'], - receiver_designation=user_info['receiver_designation'], src_module="HR", src_object_id=str(serializer.data['id']), file_extra_JSON={"type": "Leave"}, attached_file=None) - # forwarded = forward_file(file_id=fileId, receiver=user_info['receiver_name'], receiver_designation=user_info['receiver_designation'], - # remarks="Forwarded to Receiver Inbox", file_extra_JSON={"type": "Leave"}) - return Response(serializer.data, status=status.HTTP_200_OK) - else: - print(serializer.errors) - return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) - - def get(self, request, *args, **kwargs): - pk = request.query_params.get("name") - try: - forms = LeaveForm.objects.get(created_by=pk) - serializer = self.serializer_class(forms, many=False) - except MultipleObjectsReturned: - forms = LeaveForm.objects.filter(created_by=pk) - serializer = self.serializer_class(forms, many=True) - return Response(serializer.data, status=status.HTTP_200_OK) - - def put(self, request, *args, **kwargs): - pk = request.query_params.get("id") - receiver = request.data[0] - # send_to = receiver['receiver'] - # receiver_value = User.objects.get(username=send_to) - # receiver_value_designation = HoldsDesignation.objects.filter( - # user=receiver_value) - # lis = list(receiver_value_designation) - # obj = lis[0].designation - form = LeaveForm.objects.get(id=pk) - serializer = self.serializer_class(form, data=request.data[1]) - if serializer.is_valid(): - serializer.save() - forward_file(file_id=receiver['file_id'], receiver=receiver['receiver'], receiver_designation=receiver['receiver_designation'], - remarks=receiver['remarks'], file_extra_JSON=receiver['file_extra_JSON']) - return Response(serializer.data, status=status.HTTP_200_OK) - else: - return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) - - def delete(self, request, *args, **kwargs): - id = request.query_params.get("id") - is_archived = archive_file(file_id=id) - if (is_archived): - return Response(status=status.HTTP_200_OK) - else: - return Response(status=status.HTTP_400_BAD_REQUEST) - - -class Appraisal(APIView): - serializer_class = Appraisal_serializer - permission_classes = (IsAuthenticated, ) - - def post(self, request): - user_info = request.data[1] - print(request.data) - serializer = self.serializer_class(data=request.data[0]) - if serializer.is_valid(): - serializer.save() - fileId = create_file(uploader=user_info['uploader_name'], uploader_designation=user_info['uploader_designation'], receiver=user_info['receiver_name'], - receiver_designation=user_info['receiver_designation'], src_module="HR", src_object_id=str(serializer.data['id']), file_extra_JSON={"type": "Appraisal"}, attached_file=None) - # forwarded = forward_file(file_id=fileId, receiver=user_info['receiver_name'], receiver_designation=user_info['receiver_designation'], - # remarks="Forwarded to Receiver Inbox", file_extra_JSON={"type": "Appraisal"}) - return Response(serializer.data, status=status.HTTP_200_OK) - else: - print(serializer.errors) - return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) - - def get(self, request, *args, **kwargs): - pk = request.query_params.get("name") - try: - forms = Appraisalform.objects.get(created_by=pk) - serializer = self.serializer_class(forms, many=False) - except MultipleObjectsReturned: - forms = Appraisalform.objects.filter(created_by=pk) - serializer = self.serializer_class(forms, many=True) - return Response(serializer.data, status=status.HTTP_200_OK) - - def put(self, request, *args, **kwargs): - pk = request.query_params.get("id") - print(request.data) - form = Appraisalform.objects.get(id=pk) - receiver = request.data[0] - send_to = receiver['receiver_name'] - receiver_value = User.objects.get(username=send_to) - receiver_value_designation = HoldsDesignation.objects.filter( - user=receiver_value) - lis = list(receiver_value_designation) - obj = lis[0].designation - serializer = self.serializer_class(form, data=request.data[1]) - if serializer.is_valid(): - forward_file(file_id=receiver['file_id'], receiver=send_to, receiver_designation=receiver['receiver_designation'], - remarks=receiver['remarks'], file_extra_JSON=receiver['file_extra_JSON']) - serializer.save() - return Response(serializer.data, status=status.HTTP_200_OK) - else: - return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) - - def delete(self, request, *args, **kwargs): - id = request.query_params.get("id") - is_archived = archive_file(file_id=id) - if (is_archived): - return Response(status=status.HTTP_200_OK) - else: - return Response(status=status.HTTP_400_BAD_REQUEST) - -# class Forward(APIView): -# def post(self, request, *args, **kwargs): -# forward_file(file_id = request.data['file_id'], receiver = request.data['receiver'], receiver_designation = 'hradmin', remarks = request.data['remarks'], file_extra_JSON = request.data['file_extra_JSON']) -# return Response(status = status.HTTP_200_OK) - - -class GetFormHistory(APIView): - permission_classes = (IsAuthenticated, ) - - def get(self, request, *args, **kwargs): - print(request.query_params) - form_type = request.query_params.get("type") - id = request.query_params.get("id") - person = User.objects.get(username=id) - print(type(person)) - id = person - if form_type == "LTC": - try: - forms = LTCform.objects.get(created_by=id) - serializer = LTC_serializer(forms, many=False) - return Response([serializer.data], status=status.HTTP_200_OK) - except MultipleObjectsReturned: - forms = LTCform.objects.filter(created_by=id) - serializer = LTC_serializer(forms, many=True) - return Response(serializer.data, status=status.HTTP_200_OK) - except LTCform.DoesNotExist: - return Response([], status=status.HTTP_200_OK) - elif form_type == "CPDAReimbursement": - try: - forms = CPDAReimbursementform.objects.get(created_by=id) - serializer = CPDAReimbursement_serializer(forms, many=False) - return Response([serializer.data], status=status.HTTP_200_OK) - except MultipleObjectsReturned: - forms = CPDAReimbursementform.objects.filter(created_by=id) - serializer = CPDAReimbursement_serializer(forms, many=True) - return Response(serializer.data, status=status.HTTP_200_OK) - except CPDAReimbursementform.DoesNotExist: - return Response([], status=status.HTTP_200_OK) - elif form_type == "CPDAAdvance": - try: - forms = CPDAAdvanceform.objects.get(created_by=id) - serializer = CPDAAdvance_serializer(forms, many=False) - return Response([serializer.data], status=status.HTTP_200_OK) - except MultipleObjectsReturned: - forms = CPDAAdvanceform.objects.filter(created_by=id) - serializer = CPDAAdvance_serializer(forms, many=True) - return Response(serializer.data, status=status.HTTP_200_OK) - except CPDAAdvanceform.DoesNotExist: - return Response([], status=status.HTTP_200_OK) - elif form_type == "Appraisal": - try: - forms = Appraisalform.objects.get(created_by=id) - serializer = Appraisal_serializer(forms, many=False) - return Response([serializer.data], status=status.HTTP_200_OK) - except MultipleObjectsReturned: - forms = Appraisalform.objects.filter(created_by=id) - serializer = Appraisal_serializer(forms, many=True) - return Response(serializer.data, status=status.HTTP_200_OK) - except Appraisalform.DoesNotExist: - return Response([], status=status.HTTP_200_OK) - elif form_type == "Leave": - try: - forms = LeaveForm.objects.get(created_by=id) - serializer = Leave_serializer(forms, many=False) - return Response([serializer.data], status=status.HTTP_200_OK) - except MultipleObjectsReturned: - forms = LeaveForm.objects.filter(created_by=id) - serializer = Leave_serializer(forms, many=True) - return Response(serializer.data, status=status.HTTP_200_OK) - except LeaveForm.DoesNotExist: - return Response([], status=status.HTTP_200_OK) - - -class TrackProgress(APIView): - permission_classes = (IsAuthenticated, ) - - def get(self, request, *args, **kwargs): - file_id = request.query_params.get("id") - progress = view_history(file_id) - return Response({"status": progress}, status=status.HTTP_200_OK) - - -class FormFetch(APIView): - permission_classes = (IsAuthenticated, ) - - def get(self, request, *args, **kwargs): - fileId = request.query_params.get("file_id") - print(fileId) - form_id = request.query_params.get("id") - form_type = request.query_params.get("type") - if form_type == "LTC": - forms = LTCform.objects.get(id=form_id) - serializer = LTC_serializer(forms, many=False) - form = serializer.data - user = User.objects.get(id=int(form["created_by"])) - owner = Tracking.objects.filter(file_id=fileId) - current_owner = owner.last() - current_owner = current_owner.receiver_id - current_owner = current_owner.username - elif form_type == "CPDAReimbursement": - forms = CPDAReimbursementform.objects.get(id=form_id) - serializer = CPDAReimbursement_serializer(forms, many=False) - form = serializer.data - user = User.objects.get(id=int(form["created_by"])) - owner = Tracking.objects.filter(file_id=fileId) - current_owner = owner.last() - current_owner = current_owner.receiver_id - current_owner = current_owner.username - elif form_type == "CPDAAdvance": - forms = CPDAAdvanceform.objects.get(id=form_id) - serializer = CPDAAdvance_serializer(forms, many=False) - form = serializer.data - user = User.objects.get(id=int(form["created_by"])) - owner = Tracking.objects.filter(file_id=fileId) - current_owner = owner.last() - current_owner = current_owner.receiver_id - current_owner = current_owner.username - elif form_type == "Appraisal": - forms = Appraisalform.objects.get(id=form_id) - serializer = Appraisal_serializer(forms, many=False) - form = serializer.data - user = User.objects.get(id=int(form["created_by"])) - owner = Tracking.objects.filter(file_id=fileId) - current_owner = owner.last() - current_owner = current_owner.receiver_id - current_owner = current_owner.username - elif form_type == "Leave": - forms = LeaveForm.objects.get(id=form_id) - serializer = Leave_serializer(forms, many=False) - form = serializer.data - user = User.objects.get(id=int(form["created_by"])) - owner = Tracking.objects.filter(file_id=fileId) - current_owner = owner.last() - current_owner = current_owner.receiver_id - current_owner = current_owner.username - return Response({"form": serializer.data, "creator": user.username, "current_owner": current_owner}, status=status.HTTP_200_OK) - - -class CheckLeaveBalance(APIView): - permission_classes = (IsAuthenticated, ) - serializer_class = LeaveBalanace_serializer - - def get(self, request, *args, **kwargs): - name = request.query_params.get("name") - person = User.objects.get(username=name) - extrainfo = ExtraInfo.objects.get(user=person) - leave_balance = LeaveBalance.objects.get(employeeId=extrainfo) - serializer = self.serializer_class(leave_balance, many=False) - return Response(serializer.data, status=status.HTTP_200_OK) - # return Response([], status=status.HTTP_200_OK) - - def put(self, request, *args, **kwargs): - name = request.query_params.get("name") - # print(request.data) - person = User.objects.get(username=name) - extrainfo = ExtraInfo.objects.get(user=person) - leave_balance = LeaveBalance.objects.get(employeeId=extrainfo) - data1 = request.data - data1['employeeId'] = extrainfo.id - serializer = self.serializer_class(leave_balance, data=data1) - if serializer.is_valid(): - serializer.save() - return Response(serializer.data, status=status.HTTP_200_OK) - else: - print(serializer.error_messages) - return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) - - -class DropDown(APIView): - permission_classes = (IsAuthenticated, ) - - def get(self, request, *args, **kwargs): - user_id = request.query_params.get("username") - user = User.objects.get(username=user_id) - designations = HoldsDesignation.objects.filter(user=user.id) - designation_list = [] - - for design in designations: - design = design.designation - design = design.name - designation_list.append(design) - # print(designation_list) - return Response(designation_list, status=status.HTTP_200_OK) - - -class UserById(APIView): - permission_classes = (IsAuthenticated, ) - - def get(self, request, *args, **kwargs): - user_id = request.query_params.get("id") - user = User.objects.get(id=user_id) - return Response({"username": user.username}, status=status.HTTP_200_OK) - - -class ViewArchived(APIView): - permission_classes = (IsAuthenticated, ) - - def get(self, request, *args, **kwargs): - user_name = request.query_params.get("username") - user_designation = request.query_params.get("designation") - archived_inbox = view_archived( - username=user_name, designation=user_designation, src_module="HR") - return Response(archived_inbox, status=status.HTTP_200_OK) - - -class GetOutbox(APIView): - permission_classes = (IsAuthenticated, ) - - def get(self, request, *args, **kwargs): - name = request.query_params.get("username") - user_designation = request.query_params.get("designation") - outbox = view_outbox( - username=name, designation=user_designation, src_module="HR") - return Response(outbox, status=status.HTTP_200_OK) diff --git a/FusionIIIT/applications/hr2/api/serializers.py b/FusionIIIT/applications/hr2/api/serializers.py deleted file mode 100644 index 63efc27cc..000000000 --- a/FusionIIIT/applications/hr2/api/serializers.py +++ /dev/null @@ -1,65 +0,0 @@ -from rest_framework import serializers -from applications.hr2.models import LTCform, CPDAAdvanceform, CPDAReimbursementform, LeaveForm, Appraisalform, LeaveBalance - - -class LTC_serializer(serializers.ModelSerializer): - class Meta: - model = LTCform - fields = '__all__' - - def create(self, validated_data): - return LTCform.objects.create(**validated_data) - - -class CPDAAdvance_serializer(serializers.ModelSerializer): - class Meta: - model = CPDAAdvanceform - fields = '__all__' - - def create(self, validated_data): - return CPDAAdvanceform.objects.create(**validated_data) - - -class Appraisal_serializer(serializers.ModelSerializer): - class Meta: - model = Appraisalform - fields = '__all__' - - def create(self, validated_data): - return Appraisalform.objects.create(**validated_data) - - -class CPDAReimbursement_serializer(serializers.ModelSerializer): - class Meta: - model = CPDAReimbursementform - fields = '__all__' - - def create(self, validated_data): - return CPDAReimbursementform.objects.create(**validated_data) - - -class Leave_serializer(serializers.ModelSerializer): - class Meta: - model = LeaveForm - fields = '__all__' - - def create(self, validated_data): - return LeaveForm.objects.create(**validated_data) - - -class LeaveBalanace_serializer(serializers.ModelSerializer): - class Meta: - model = LeaveBalance - fields = '__all__' - - def create(self, validated_data): - return LeaveBalance.objects.create(**validated_data) - - -# class Deignations(serializers.ModelSerializer): -# class Meta: -# model = Deignations -# fields = '__all__' - -# def create(self,validated_data): -# return diff --git a/FusionIIIT/applications/hr2/api/urls.py b/FusionIIIT/applications/hr2/api/urls.py deleted file mode 100644 index 835434b76..000000000 --- a/FusionIIIT/applications/hr2/api/urls.py +++ /dev/null @@ -1,42 +0,0 @@ -from django.conf.urls import url -from django.urls import path -# from . import views -from . import form_views - - -app_name = 'hr2' - -urlpatterns = [ - # LTC form - url('ltc/', form_views.LTC.as_view(), name='LTC_form'), - # cpda advance form - url('cpdaadv/', form_views.CPDAAdvance.as_view(), name='CPDAAdvance_form'), - # appraisal form - url('appraisal/', form_views.Appraisal.as_view(), name='Appraisal_form'), - # cpda reimbursement form - url('cpdareim/', form_views.CPDAReimbursement.as_view(), - name='CPDAReimbursement_form'), - # leave form - url('leave/', form_views.Leave.as_view(), name='Leave_form'), - url('formManagement/', form_views.FormManagement.as_view(), name='formManagement'), - url('tracking/', form_views.TrackProgress.as_view(), name='tracking'), - url('formFetch/', form_views.FormFetch.as_view(), name='fetch_form'), - # create for GetForms - url('getForms/', form_views.GetFormHistory.as_view(), name='getForms'), - url('leaveBalance/', form_views.CheckLeaveBalance.as_view(), name='leaveBalance'), - url('getDesignations/', form_views.DropDown.as_view(), name="designations"), - url('getOutbox/', form_views.GetOutbox.as_view(), name='outbox'), - url('getArchive/', form_views.ViewArchived.as_view(), name='archive'), - url('getuserbyid/', form_views.UserById.as_view(), name='userById'), - # url(r'^$', views.service_book, name='hr2'), - # url(r'^hradmin/$', views.hr_admin, name='hradmin'), - # url(r'^edit/(?P\d+)/$', views.edit_employee_details, - # name='editEmployeeDetails'), - # url(r'^viewdetails/(?P\d+)/$', - # views.view_employee_details, name='viewEmployeeDetails'), - # url(r'^editServiceBook/(?P\d+)/$', - # views.edit_employee_servicebook, name='editServiceBook'), - # url(r'^administrativeProfile/$', views.administrative_profile, - # name='administrativeProfile'), - # url(r'^addnew/$', views.add_new_user, name='addnew'), -] diff --git a/FusionIIIT/applications/hr2/form_views.py b/FusionIIIT/applications/hr2/form_views.py deleted file mode 100644 index 3cc08fb93..000000000 --- a/FusionIIIT/applications/hr2/form_views.py +++ /dev/null @@ -1,323 +0,0 @@ -from .serializers import LTC_serializer, CPDAAdvance_serializer, Appraisal_serializer, CPDAReimbursement_serializer, Leave_serializer -from rest_framework.views import APIView -from rest_framework.response import Response -from rest_framework import status -from rest_framework.decorators import permission_classes, api_view -from rest_framework.permissions import IsAuthenticated, AllowAny -from .models import LTCform, CPDAAdvanceform, CPDAReimbursementform, Leaveform, Appraisalform -from django.contrib.auth import get_user_model -from django.core.exceptions import MultipleObjectsReturned -from applications.filetracking.sdk.methods import * -from applications.globals.models import Designation, HoldsDesignation - -class LTC(APIView): - serializer_class = LTC_serializer - permission_classes = (AllowAny, ) - def post(self, request): - if 'Mobile-OS' in request.META: - user_info = request.data[0] - serializer = self.serializer_class(data = request.data[1]) - if serializer.is_valid(): - serializer.save() - file_id = create_file(uploader = user_info['uploader_name'], uploader_designation = user_info['uploader_designation'], receiver = "21BCS140", receiver_designation="hradmin", src_module="HR", src_object_id= str(serializer.data['id']), file_extra_JSON= {"type": "LTC"}, attached_file= None) - return Response(serializer.data, status= status.HTTP_200_OK) - else: - return Response(serializer.errors, status= status.HTTP_400_BAD_REQUEST) - id = request.query_params.get("id") - try: - employee = ExtraInfo.objects.get(user__id=id) - except: - raise Http404("Post does not exist! id doesnt exist") - - print(employee.user_type) - - - if(employee.user_type == 'faculty'): - template = 'hr2Module/ltc_form.html' - - if request.method == "POST": - family_mem_a = request.POST.get('id_family_mem_a', '') - family_mem_b = request.POST.get('id_family_mem_b', '') - family_mem_c = request.POST.get('id_family_mem_c', '') - - - detailsOfFamilyMembers = ', '.join(filter(None, [family_mem_a, family_mem_b, family_mem_c])) - - - request.POST = request.POST.copy() - request.POST['detailsOfFamilyMembersAlreadyDone'] = detailsOfFamilyMembers - - - family_members = [] - for i in range(1, 7): # Loop through input fields for each family member - name = request.POST.get(f'info_{i}_2', '') # Get the name - age = request.POST.get(f'info_{i}_3', '') # Get the age - if name and age: # Check if both name and age are provided - family_members.append(f"{name} ({age} years)") # Concatenate name and age - - family_members_str = ', '.join(family_members) - - # Populate the form with concatenated family member details - request.POST['familyMembersAboutToAvail'] = family_members_str - - dependents = [] - for i in range(1, 7): # Loop through input fields for each dependent - name = request.POST.get(f'd_info_{i}_2', '') # Get the name - age = request.POST.get(f'd_info_{i}_3', '') # Get the age - why_dependent = request.POST.get(f'd_info_{i}_4', '') # Get the reason for dependency - if name and age: # Check if both name and age are provided - dependents.append(f"{name} ({age} years), {why_dependent}") # Concatenate name, age, and reason - - - # Concatenate all dependent strings into a single string - dependents_str = ', '.join(dependents) - - # Populate the form with concatenated dependent details - request.POST['detailsOfDependents'] = dependents_str - - # print("first",request.POST['familyMembersAboutToAvail']) - pf_no = int(request.POST.get('pf_no')) if request.POST.get('pf_no') else None - basicPay = int(request.POST.get('basicPay')) if request.POST.get('basicPay') else None - amountOfAdvanceRequired = int(request.POST.get('amountOfAdvanceRequired')) if request.POST.get('amountOfAdvanceRequired') else None - phoneNumberForContact = int(request.POST.get('phoneNumberForContact')) if request.POST.get('phoneNumberForContact') else None - - - try: - ltc_request = LTCform.objects.create( - employee_id = id, - detailsOfFamilyMembersAlreadyDone=request.POST.get('detailsOfFamilyMembersAlreadyDone', ''), - familyMembersAboutToAvail=request.POST.get('familyMembersAboutToAvail', ''), - detailsOfDependents=request.POST.get('detailsOfDependents', ''), - name=request.POST.get('name', ''), - blockYear=request.POST.get('blockYear', ''), - pf_no=request.POST.get('pf_no', ''), - basicPay=request.POST.get('basicPay', ''), - designation=request.POST.get('designation', ''), - departmentInfo=request.POST.get('departmentInfo', ''), - leaveAvailability=request.POST.get('leaveAvailability', ''), - leaveStartDate=request.POST.get('leaveStartDate', ''), - leaveEndDate=request.POST.get('leaveEndDate', ''), - dateOfLeaveForFamily=request.POST.get('dateOfLeaveForFamily', ''), - natureOfLeave=request.POST.get('natureOfLeave', ''), - purposeOfLeave=request.POST.get('purposeOfLeave', ''), - hometownOrNot=request.POST.get('hometownOrNot', ''), - placeOfVisit=request.POST.get('placeOfVisit', ''), - addressDuringLeave=request.POST.get('addressDuringLeave', ''), - modeForVacation=request.POST.get('modeForVacation', ''), - detailsOfFamilyMembers=request.POST.get('detailsOfFamilyMembers', ''), - amountOfAdvanceRequired=request.POST.get('amountOfAdvanceRequired', ''), - certifiedFamilyDependents=request.POST.get('certifiedFamilyDependents', ''), - certifiedAdvance =request.POST.get('certifiedAdvance ', ''), - adjustedMonth=request.POST.get('adjustedMonth', ''), - date=request.POST.get('date', ''), - phoneNumberForContact=request.POST.get('phoneNumberForContact', '') - ) - print("done") - messages.success(request, "Ltc form filled successfully") - except Exception as e: - print("error" , e) - messages.warning(request, "Fill not correctly") - context = {'employee': employee} - return render(request, template, context) - - - # Query all LTC requests - ltc_requests = LTCform.objects.filter(employee_id=id) - - context = {'employee': employee, 'ltc_requests': ltc_requests} - - return render(request, template, context) - else: - return render(request, 'hr2Module/edit.html') - - - - def get(self, request, *args, **kwargs): - pk = request.query_params.get("name") - print(pk) - try: - forms = LTCform.objects.get(name = pk) - serializer = self.serializer_class(forms, many = False) - except MultipleObjectsReturned: - forms = LTCform.objects.filter(name = pk) - serializer = self.serializer_class(forms, many = True) - return Response(serializer.data, status = status.HTTP_200_OK) - - def put(self, request, *args, **kwargs): - pk = request.query_params.get("id") - print(pk) - form = LTCform.objects.get(id = pk) - print(form) - serializer = self.serializer_class(form, data = request.data) - if serializer.is_valid(): - serializer.save() - return Response(serializer.data, status = status.HTTP_200_OK) - else: - return Response(serializer.errors, status = status.HTTP_400_BAD_REQUEST) - -class FormManagement(APIView): - permission_classes = (AllowAny, ) - def get(self, request, *args, **kwargs): - username = request.query_params.get("username") - designation = request.query_params.get("designation") - inbox = view_inbox(username = username, designation = designation, src_module = "HR") - return Response(inbox, status = status.HTTP_200_OK) - - def post(self, request, *args, **kwargs): - username = request.data['receiver'] - receiver_value = User.objects.get(username=username) - receiver_value_designation= HoldsDesignation.objects.filter(user=receiver_value) - lis = list(receiver_value_designation) - obj=lis[0].designation - forward_file(file_id = request.data['file_id'], receiver = request.data['receiver'], receiver_designation = obj.name, remarks = request.data['remarks'], file_extra_JSON = request.data['file_extra_JSON']) - return Response(status = status.HTTP_200_OK) - - -class CPDAAdvance(APIView): - serializer_class = CPDAAdvance_serializer - permission_classes = (AllowAny, ) - def post(self, request): - user_info = request.data[0] - serializer = self.serializer_class(data = request.data[1]) - if serializer.is_valid(): - serializer.save() - file_id = create_file(uploader = user_info['uploader_name'], uploader_designation = user_info['uploader_designation'], receiver = "vkjain", receiver_designation="CSE HOD", src_module="HR", src_object_id= str(serializer.data['id']), file_extra_JSON= {"type": "CPDAAdvance"}, attached_file= None) - return Response(serializer.data, status= status.HTTP_200_OK) - else: - return Response(serializer.errors, status= status.HTTP_400_BAD_REQUEST) - - def get(self, request, *args, **kwargs): - pk = request.query_params.get("name") - print(pk) - try: - forms = CPDAAdvanceform.objects.get(name = pk) - serializer = self.serializer_class(forms, many = False) - except MultipleObjectsReturned: - forms = CPDAAdvanceform.objects.filter(name = pk) - serializer = self.serializer_class(forms, many = True) - return Response(serializer.data, status = status.HTTP_200_OK) - - def put(self, request, *args, **kwargs): - pk = request.query_params.get("id") - print(pk) - form = CPDAAdvanceform.objects.get(id = pk) - print(form) - serializer = self.serializer_class(form, data = request.data) - if serializer.is_valid(): - serializer.save() - return Response(serializer.data, status = status.HTTP_200_OK) - else: - return Response(serializer.errors, status = status.HTTP_400_BAD_REQUEST) - -class CPDAReimbursement(APIView): - serializer_class = CPDAReimbursement_serializer - permission_classes = (AllowAny, ) - def post(self, request): - user_info = request.data[0] - serializer = self.serializer_class(data = request.data[1]) - if serializer.is_valid(): - serializer.save() - file_id = create_file(uploader = user_info['uploader_name'], uploader_designation = user_info['uploader_designation'], receiver = "vkjain", receiver_designation="CSE HOD", src_module="HR", src_object_id= str(serializer.data['id']), file_extra_JSON= {"type": "CPDAReimbursement"}, attached_file= None) - return Response(serializer.data, status= status.HTTP_200_OK) - else: - return Response(serializer.errors, status= status.HTTP_400_BAD_REQUEST) - - def get(self, request, *args, **kwargs): - pk = request.query_params.get("name") - print(pk) - try: - forms = CPDAReimbursementform.objects.get(name = pk) - serializer = self.serializer_class(forms, many = False) - except MultipleObjectsReturned: - forms = CPDAReimbursementform.objects.filter(name = pk) - serializer = self.serializer_class(forms, many = True) - return Response(serializer.data, status = status.HTTP_200_OK) - - def put(self, request, *args, **kwargs): - pk = request.query_params.get("id") - print(pk) - form = CPDAReimbursementform.objects.get(id = pk) - print(form) - serializer = self.serializer_class(form, data = request.data) - if serializer.is_valid(): - serializer.save() - return Response(serializer.data, status = status.HTTP_200_OK) - else: - return Response(serializer.errors, status = status.HTTP_400_BAD_REQUEST) - -class Leave(APIView): - serializer_class = Leave_serializer - permission_classes = (AllowAny, ) - def post(self, request): - user_info = request.data[0] - serializer = self.serializer_class(data = request.data[1]) - if serializer.is_valid(): - serializer.save() - file_id = create_file(uploader = user_info['uploader_name'], uploader_designation = user_info['uploader_designation'], receiver = "vkjain", receiver_designation="CSE HOD", src_module="HR", src_object_id= str(serializer.data['id']), file_extra_JSON= {"type": "Leave"}, attached_file= None) - return Response(serializer.data, status= status.HTTP_200_OK) - else: - return Response(serializer.errors, status= status.HTTP_400_BAD_REQUEST) - - def get(self, request, *args, **kwargs): - pk = request.query_params.get("name") - print(pk) - try: - forms = Leaveform.objects.get(name = pk) - serializer = self.serializer_class(forms, many = False) - except MultipleObjectsReturned: - forms = Leaveform.objects.filter(name = pk) - serializer = self.serializer_class(forms, many = True) - return Response(serializer.data, status = status.HTTP_200_OK) - - def put(self, request, *args, **kwargs): - pk = request.query_params.get("id") - print(pk) - form = Leaveform.objects.get(id = pk) - print(form) - serializer = self.serializer_class(form, data = request.data) - if serializer.is_valid(): - serializer.save() - return Response(serializer.data, status = status.HTTP_200_OK) - else: - return Response(serializer.errors, status = status.HTTP_400_BAD_REQUEST) - -class Appraisal(APIView): - serializer_class = Appraisal_serializer - permission_classes = (AllowAny, ) - def post(self, request): - user_info = request.data[0] - serializer = self.serializer_class(data = request.data[1]) - if serializer.is_valid(): - serializer.save() - file_id = create_file(uploader = user_info['uploader_name'], uploader_designation = user_info['uploader_designation'], receiver = "vkjain", receiver_designation="CSE HOD", src_module="HR", src_object_id= str(serializer.data['id']), file_extra_JSON= {"type": "Appraisal"}, attached_file= None) - return Response(serializer.data, status= status.HTTP_200_OK) - else: - return Response(serializer.errors, status= status.HTTP_400_BAD_REQUEST) - - def get(self, request, *args, **kwargs): - pk = request.query_params.get("name") - print(pk) - try: - forms = Appraisalform.objects.get(name = pk) - serializer = self.serializer_class(forms, many = False) - except MultipleObjectsReturned: - forms = Appraisalform.objects.filter(name = pk) - serializer = self.serializer_class(forms, many = True) - return Response(serializer.data, status = status.HTTP_200_OK) - - def put(self, request, *args, **kwargs): - pk = request.query_params.get("id") - print(pk) - form = Appraisalform.objects.get(id = pk) - print(form) - serializer = self.serializer_class(form, data = request.data) - if serializer.is_valid(): - serializer.save() - return Response(serializer.data, status = status.HTTP_200_OK) - else: - return Response(serializer.errors, status = status.HTTP_400_BAD_REQUEST) - -class AssignerReviewer(APIView): - def post(self, request, *args, **kwargs): - forward_file(file_id = request.data['file_id'], receiver = "21BCS140", receiver_designation = 'hradmin', remarks = request.data['remarks'], file_extra_JSON = request.data['file_extra_JSON']) - return Response(status = status.HTTP_200_OK) \ No newline at end of file diff --git a/FusionIIIT/applications/hr2/migrations/0001_initial.py b/FusionIIIT/applications/hr2/migrations/0001_initial.py index 78ce3a457..83b197c8f 100644 --- a/FusionIIIT/applications/hr2/migrations/0001_initial.py +++ b/FusionIIIT/applications/hr2/migrations/0001_initial.py @@ -1,6 +1,5 @@ -# Generated by Django 3.1.5 on 2024-07-16 15:44 +# Generated by Django 3.1.5 on 2023-03-15 18:53 -from django.conf import settings import django.core.validators from django.db import migrations, models import django.db.models.deletion @@ -12,7 +11,6 @@ class Migration(migrations.Migration): dependencies = [ ('globals', '0001_initial'), - migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] operations = [ @@ -27,79 +25,6 @@ class Migration(migrations.Migration): ('extra_info', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='globals.extrainfo')), ], ), - migrations.CreateModel( - name='LTCform', - fields=[ - ('id', models.AutoField(primary_key=True, serialize=False)), - ('employeeId', models.IntegerField()), - ('name', models.CharField(max_length=100, null=True)), - ('blockYear', models.TextField()), - ('pfNo', models.IntegerField(max_length=50)), - ('basicPaySalary', models.IntegerField(null=True)), - ('designation', models.CharField(max_length=50)), - ('departmentInfo', models.CharField(max_length=50)), - ('leaveRequired', models.BooleanField(default=False, null=True)), - ('leaveStartDate', models.DateField(blank=True, null=True)), - ('leaveEndDate', models.DateField(blank=True, null=True)), - ('dateOfDepartureForFamily', models.DateField(blank=True, null=True)), - ('natureOfLeave', models.TextField(blank=True, null=True)), - ('purposeOfLeave', models.TextField(blank=True, null=True)), - ('hometownOrNot', models.BooleanField(default=False)), - ('placeOfVisit', models.TextField(blank=True, max_length=100, null=True)), - ('addressDuringLeave', models.TextField(null=True)), - ('modeofTravel', models.TextField(blank=True, max_length=10, null=True)), - ('detailsOfFamilyMembersAlreadyDone', models.JSONField(blank=True, null=True)), - ('detailsOfFamilyMembersAboutToAvail', models.JSONField(blank=True, max_length=100, null=True)), - ('detailsOfDependents', models.JSONField(blank=True, null=True)), - ('amountOfAdvanceRequired', models.IntegerField(blank=True, null=True)), - ('certifiedThatFamilyDependents', models.BooleanField(blank=True, null=True)), - ('certifiedThatAdvanceTakenOn', models.DateField(blank=True, null=True)), - ('adjustedMonth', models.TextField(blank=True, max_length=50, null=True)), - ('submissionDate', models.DateField(null=True)), - ('phoneNumberForContact', models.BigIntegerField()), - ('approved', models.BooleanField(null=True)), - ('approvedDate', models.DateField(auto_now_add=True, null=True)), - ('approved_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='LTC_approved_by', to=settings.AUTH_USER_MODEL)), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='LTC_created_by', to=settings.AUTH_USER_MODEL)), - ], - ), - migrations.CreateModel( - name='LeaveForm', - fields=[ - ('id', models.AutoField(primary_key=True, serialize=False)), - ('employeeId', models.IntegerField(max_length=22, null=True)), - ('name', models.CharField(max_length=40, null=True)), - ('designation', models.CharField(max_length=40, null=True)), - ('submissionDate', models.DateField(blank=True, null=True)), - ('pfNo', models.IntegerField(max_length=30, null=True)), - ('departmentInfo', models.CharField(max_length=40, null=True)), - ('natureOfLeave', models.TextField(max_length=40, null=True)), - ('leaveStartDate', models.DateField(blank=True, null=True)), - ('leaveEndDate', models.DateField(blank=True, null=True)), - ('purposeOfLeave', models.TextField(max_length=40, null=True)), - ('addressDuringLeave', models.TextField(blank=True, max_length=40, null=True)), - ('academicResponsibility', models.TextField(blank=True, max_length=40, null=True)), - ('addministrativeResponsibiltyAssigned', models.TextField(max_length=40, null=True)), - ('approved', models.BooleanField(null=True)), - ('approvedDate', models.DateField(auto_now_add=True, null=True)), - ('approved_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='Leave_approved_by', to=settings.AUTH_USER_MODEL)), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='Leave_created_by', to=settings.AUTH_USER_MODEL)), - ], - ), - migrations.CreateModel( - name='LeaveBalance', - fields=[ - ('id', models.AutoField(primary_key=True, serialize=False)), - ('casualLeave', models.IntegerField(default=0)), - ('specialCasualLeave', models.IntegerField(default=0)), - ('earnedLeave', models.IntegerField(default=0)), - ('commutedLeave', models.IntegerField(default=0)), - ('restrictedHoliday', models.IntegerField(default=0)), - ('stationLeave', models.IntegerField(default=0)), - ('vacationLeave', models.IntegerField(default=0)), - ('employeeId', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='globals.extrainfo')), - ], - ), migrations.CreateModel( name='ForeignService', fields=[ @@ -163,84 +88,4 @@ class Migration(migrations.Migration): ('extra_info', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='globals.extrainfo')), ], ), - migrations.CreateModel( - name='CPDAReimbursementform', - fields=[ - ('id', models.AutoField(primary_key=True, serialize=False)), - ('employeeId', models.IntegerField(max_length=22, null=True)), - ('name', models.CharField(max_length=50)), - ('designation', models.CharField(max_length=50)), - ('pfNo', models.IntegerField(max_length=20)), - ('advanceTaken', models.IntegerField()), - ('purpose', models.TextField()), - ('adjustmentSubmitted', models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True)), - ('balanceAvailable', models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True)), - ('advanceDueAdjustment', models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True)), - ('advanceAmountPDA', models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True)), - ('amountCheckedInPDA', models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True)), - ('submissionDate', models.DateField(auto_now_add=True)), - ('approved', models.BooleanField(null=True)), - ('approvedDate', models.DateField(auto_now_add=True, null=True)), - ('approved_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='CPDAR_approved_by', to=settings.AUTH_USER_MODEL)), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='CPDAR_created_by', to=settings.AUTH_USER_MODEL)), - ], - ), - migrations.CreateModel( - name='CPDAAdvanceform', - fields=[ - ('id', models.AutoField(primary_key=True, serialize=False)), - ('employeeId', models.IntegerField(max_length=22, null=True)), - ('name', models.CharField(max_length=40, null=True)), - ('designation', models.CharField(max_length=40, null=True)), - ('pfNo', models.IntegerField(max_length=30, null=True)), - ('purpose', models.TextField(max_length=40, null=True)), - ('amountRequired', models.IntegerField(max_length=30, null=True)), - ('advanceDueAdjustment', models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True)), - ('submissionDate', models.DateField(blank=True, null=True)), - ('balanceAvailable', models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True)), - ('advanceAmountPDA', models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True)), - ('amountCheckedInPDA', models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True)), - ('approved', models.BooleanField(null=True)), - ('approvedDate', models.DateField(auto_now_add=True, null=True)), - ('approved_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='CPDA_approved_by', to=settings.AUTH_USER_MODEL)), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='CPDA_created_by', to=settings.AUTH_USER_MODEL)), - ], - ), - migrations.CreateModel( - name='Appraisalform', - fields=[ - ('id', models.AutoField(primary_key=True, serialize=False)), - ('employeeId', models.IntegerField(max_length=22, null=True)), - ('name', models.CharField(max_length=22)), - ('designation', models.CharField(max_length=50)), - ('disciplineInfo', models.CharField(max_length=22, null=True)), - ('specificFieldOfKnowledge', models.TextField(max_length=40, null=True)), - ('currentResearchInterests', models.TextField(max_length=40, null=True)), - ('coursesTaught', models.JSONField(max_length=100, null=True)), - ('newCoursesIntroduced', models.JSONField(max_length=100, null=True)), - ('newCoursesDeveloped', models.JSONField(max_length=100, null=True)), - ('otherInstructionalTasks', models.TextField(max_length=100, null=True)), - ('thesisSupervision', models.JSONField(max_length=100, null=True)), - ('sponsoredReseachProjects', models.JSONField(max_length=100, null=True)), - ('otherResearchElement', models.TextField(max_length=40, null=True)), - ('publication', models.TextField(max_length=40, null=True)), - ('referredConference', models.TextField(max_length=40, null=True)), - ('conferenceOrganised', models.TextField(max_length=40, null=True)), - ('membership', models.TextField(max_length=40, null=True)), - ('honours', models.TextField(max_length=40, null=True)), - ('editorOfPublications', models.TextField(max_length=40, null=True)), - ('expertLectureDelivered', models.TextField(max_length=40, null=True)), - ('membershipOfBOS', models.TextField(max_length=40, null=True)), - ('otherExtensionTasks', models.TextField(max_length=40, null=True)), - ('administrativeAssignment', models.TextField(max_length=40, null=True)), - ('serviceToInstitute', models.TextField(max_length=40, null=True)), - ('otherContribution', models.TextField(max_length=40, null=True)), - ('performanceComments', models.TextField(max_length=100, null=True)), - ('submissionDate', models.DateField(max_length=6, null=True)), - ('approved', models.BooleanField(null=True)), - ('approvedDate', models.DateField(auto_now_add=True, null=True)), - ('approved_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='Appraisal_approved_by', to=settings.AUTH_USER_MODEL)), - ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='Appraisal_created_by', to=settings.AUTH_USER_MODEL)), - ], - ), ] diff --git a/FusionIIIT/applications/hr2/migrations/0002_auto_20241020_1126.py b/FusionIIIT/applications/hr2/migrations/0002_auto_20241020_1126.py deleted file mode 100644 index 5b99015f7..000000000 --- a/FusionIIIT/applications/hr2/migrations/0002_auto_20241020_1126.py +++ /dev/null @@ -1,64 +0,0 @@ -# Generated by Django 3.1.5 on 2024-10-20 11:26 - -import django.core.validators -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('hr2', '0001_initial'), - ] - - operations = [ - migrations.AlterField( - model_name='appraisalform', - name='employeeId', - field=models.IntegerField(null=True), - ), - migrations.AlterField( - model_name='cpdaadvanceform', - name='amountRequired', - field=models.IntegerField(null=True), - ), - migrations.AlterField( - model_name='cpdaadvanceform', - name='employeeId', - field=models.IntegerField(null=True), - ), - migrations.AlterField( - model_name='cpdaadvanceform', - name='pfNo', - field=models.IntegerField(null=True), - ), - migrations.AlterField( - model_name='cpdareimbursementform', - name='employeeId', - field=models.IntegerField(null=True), - ), - migrations.AlterField( - model_name='cpdareimbursementform', - name='pfNo', - field=models.IntegerField(), - ), - migrations.AlterField( - model_name='empconfidentialdetails', - name='aadhar_no', - field=models.BigIntegerField(default=0, validators=[django.core.validators.MaxValueValidator(999999999999), django.core.validators.MinValueValidator(99999999999)]), - ), - migrations.AlterField( - model_name='leaveform', - name='employeeId', - field=models.IntegerField(null=True), - ), - migrations.AlterField( - model_name='leaveform', - name='pfNo', - field=models.IntegerField(null=True), - ), - migrations.AlterField( - model_name='ltcform', - name='pfNo', - field=models.IntegerField(), - ), - ] diff --git a/FusionIIIT/applications/hr2/models.py b/FusionIIIT/applications/hr2/models.py index e225ca076..cf7e0ee8c 100644 --- a/FusionIIIT/applications/hr2/models.py +++ b/FusionIIIT/applications/hr2/models.py @@ -1,7 +1,6 @@ from django.db import models from applications.globals.models import ExtraInfo from django.core.validators import MaxValueValidator, MinValueValidator -from django.contrib.auth.models import User class Constants: # Class for various choices on the enumerations @@ -49,7 +48,6 @@ class Constants: ) - # Employee model class Employee(models.Model): """ @@ -78,7 +76,7 @@ class EmpConfidentialDetails(models.Model): table for employee confidential details """ extra_info = models.OneToOneField(ExtraInfo, on_delete=models.CASCADE) - aadhar_no = models.BigIntegerField(default=0, + aadhar_no = models.BigIntegerField(default=0, max_length=12, validators=[MaxValueValidator(999999999999),MinValueValidator(99999999999)]) maritial_status = models.CharField( @@ -143,154 +141,3 @@ class WorkAssignemnt(models.Model): end_date = models.DateField(max_length=6, null=True, blank=True) job_title = models.CharField(max_length=50, default='') orders_copy = models.FileField(blank=True, null=True) - -class LTCform(models.Model): - id = models.AutoField(primary_key=True) - employeeId = models.IntegerField() - name = models.CharField(max_length=100, null=True) - blockYear = models.TextField() # - pfNo = models.IntegerField() - basicPaySalary = models.IntegerField(null=True) - designation = models.CharField(max_length=50) - departmentInfo = models.CharField(max_length=50) - leaveRequired = models.BooleanField(default=False,null=True) # - leaveStartDate = models.DateField(null=True, blank=True) - leaveEndDate = models.DateField(null=True, blank=True) - dateOfDepartureForFamily = models.DateField(null=True, blank=True) # - natureOfLeave = models.TextField(null=True,blank=True) - purposeOfLeave = models.TextField(null=True,blank=True) - hometownOrNot = models.BooleanField(default=False) - placeOfVisit = models.TextField(max_length=100, null=True, blank=True) - addressDuringLeave = models.TextField(null=True) - modeofTravel = models.TextField(max_length=10, null=True,blank=True) # - detailsOfFamilyMembersAlreadyDone = models.JSONField(null=True,blank=True) - detailsOfFamilyMembersAboutToAvail = models.JSONField(max_length=100, null=True,blank=True) - detailsOfDependents = models.JSONField(blank=True,null=True) - amountOfAdvanceRequired = models.IntegerField(null=True, blank=True) - certifiedThatFamilyDependents = models.BooleanField(blank=True,null=True) - certifiedThatAdvanceTakenOn = models.DateField(null=True, blank=True) - adjustedMonth = models.TextField(max_length=50, null=True,blank=True) - submissionDate = models.DateField(null=True) - phoneNumberForContact = models.BigIntegerField() - approved = models.BooleanField(null=True) - approvedDate = models.DateField(auto_now_add=True, null=True) - created_by = models.ForeignKey(User, on_delete=models.CASCADE, null=True, related_name='LTC_created_by') - approved_by = models.ForeignKey(User, on_delete=models.CASCADE, null=True, related_name='LTC_approved_by') - - - -class CPDAAdvanceform(models.Model): - id = models.AutoField(primary_key=True) - employeeId = models.IntegerField(null=True) - name = models.CharField(max_length=40,null=True) - designation = models.CharField(max_length=40,null=True) - pfNo = models.IntegerField(null=True) - purpose = models.TextField(max_length=40, null=True) - amountRequired = models.IntegerField(null=True) - advanceDueAdjustment = models.DecimalField(max_digits=10, decimal_places=2, null=True,blank=True) - - submissionDate = models.DateField(blank=True, null=True) - - balanceAvailable = models.DecimalField(max_digits=10, decimal_places=2, blank=True, null=True) - advanceAmountPDA = models.DecimalField(max_digits=10, decimal_places=2, blank=True, null=True) - amountCheckedInPDA = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True) - - approved = models.BooleanField(null=True) - approvedDate = models.DateField(auto_now_add=True, null=True) - created_by = models.ForeignKey(User, on_delete=models.CASCADE, null=True, related_name='CPDA_created_by') - approved_by = models.ForeignKey(User, on_delete=models.CASCADE, null=True, related_name='CPDA_approved_by') - -class LeaveForm(models.Model): - id = models.AutoField(primary_key=True) - employeeId = models.IntegerField(null=True) - name = models.CharField(max_length=40,null=True) - designation = models.CharField(max_length=40,null=True) - submissionDate = models.DateField(blank=True, null=True) - pfNo = models.IntegerField(null=True) - departmentInfo = models.CharField(max_length=40,null=True) - natureOfLeave = models.TextField(max_length=40,null=True) - leaveStartDate = models.DateField(blank=True, null=True) - leaveEndDate = models.DateField(blank=True, null=True) - - purposeOfLeave = models.TextField(max_length=40,null=True) - addressDuringLeave = models.TextField(max_length=40, blank=True, null=True) - - academicResponsibility = models.TextField(max_length=40, blank=True, null=True) - addministrativeResponsibiltyAssigned = models.TextField(max_length=40,null=True) - - approved = models.BooleanField(null=True) - approvedDate = models.DateField(auto_now_add=True, null=True) - created_by = models.ForeignKey(User, on_delete=models.CASCADE, null=True, related_name='Leave_created_by') - approved_by = models.ForeignKey(User, on_delete=models.CASCADE, null=True, related_name='Leave_approved_by') - -class LeaveBalance(models.Model): - id = models.AutoField(primary_key=True) - employeeId = models.OneToOneField(ExtraInfo, on_delete=models.CASCADE) - casualLeave = models.IntegerField(default=0) - specialCasualLeave = models.IntegerField(default=0) - earnedLeave = models.IntegerField(default=0) - commutedLeave = models.IntegerField(default=0) - restrictedHoliday = models.IntegerField(default=0) - stationLeave = models.IntegerField(default=0) - vacationLeave = models.IntegerField(default=0) - - -class Appraisalform(models.Model): - id = models.AutoField(primary_key=True) - employeeId = models.IntegerField(null=True) - name = models.CharField(max_length=22) - designation = models.CharField(max_length=50) - disciplineInfo = models.CharField(max_length=22, null=True) - specificFieldOfKnowledge = models.TextField(max_length=40, null=True) - currentResearchInterests = models.TextField(max_length=40, null=True) - coursesTaught = models.JSONField(max_length=100, null=True) - newCoursesIntroduced = models.JSONField(max_length=100, null=True) - newCoursesDeveloped = models.JSONField(max_length=100, null=True) - otherInstructionalTasks = models.TextField(max_length=100, null=True) - thesisSupervision = models.JSONField(max_length=100, null=True) - sponsoredReseachProjects = models.JSONField(max_length=100, null=True) - otherResearchElement = models.TextField(max_length=40, null=True) - publication = models.TextField(max_length=40, null=True) - referredConference = models.TextField(max_length=40, null=True) - conferenceOrganised = models.TextField(max_length=40, null=True) - membership = models.TextField(max_length=40, null=True) - honours = models.TextField(max_length=40, null=True) - editorOfPublications = models.TextField(max_length=40, null=True) - expertLectureDelivered = models.TextField(max_length=40, null=True) - membershipOfBOS = models.TextField(max_length=40, null=True) - otherExtensionTasks = models.TextField(max_length=40, null=True) - administrativeAssignment = models.TextField(max_length=40, null=True) - serviceToInstitute = models.TextField(max_length=40, null=True) - otherContribution = models.TextField(max_length=40, null=True) - performanceComments = models.TextField(max_length=100, null=True) - submissionDate = models.DateField(max_length=6, null=True) - - approved = models.BooleanField(null=True) - approvedDate = models.DateField(auto_now_add=True, null=True) - created_by = models.ForeignKey(User, on_delete=models.CASCADE, null=True, related_name='Appraisal_created_by') - approved_by = models.ForeignKey(User, on_delete=models.CASCADE, null=True, related_name='Appraisal_approved_by') - - -class CPDAReimbursementform(models.Model): - id = models.AutoField(primary_key=True) - employeeId = models.IntegerField(null=True) - name = models.CharField(max_length=50) - designation = models.CharField(max_length=50) - pfNo = models.IntegerField() - advanceTaken = models.IntegerField() - purpose = models.TextField() - adjustmentSubmitted = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True) - balanceAvailable = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True) - advanceDueAdjustment = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True) - advanceAmountPDA = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True) - amountCheckedInPDA = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True) - submissionDate = models.DateField(auto_now_add=True) - approved = models.BooleanField(null=True) - approvedDate = models.DateField(auto_now_add=True, null=True) - created_by = models.ForeignKey(User, on_delete=models.CASCADE, null=True, related_name='CPDAR_created_by') - approved_by = models.ForeignKey(User, on_delete=models.CASCADE, null=True, related_name='CPDAR_approved_by') - - - - - diff --git a/FusionIIIT/applications/hr2/normal.py b/FusionIIIT/applications/hr2/normal.py deleted file mode 100644 index 0e344ff51..000000000 --- a/FusionIIIT/applications/hr2/normal.py +++ /dev/null @@ -1,36 +0,0 @@ -'block_year': ['232'], - 'pf_no': ['222'], - 'basic_pay_salary': ['2322'], - 'name': ['dsds'], - 'designation':['dsdsd'], - 'department_info': ['ds'], - 'leave_availability': ['True', 'True'], - 'leave_start_date': ['2024-02-22'], - 'leave_end_date': ['2024-02-22'], - 'date_of_leave_for_family': ['2024-02-22'], - 'nature_of_leave': ['dsds'], - 'purpose_of_leave': ['dsdsd'], - 'hometown_or_not': ['True'], - 'place_of_visit': [''], - 'address_during_leave': ['full street address'], - 'details_of_family_members_already_done': ['sds', 'dsd', 'dsd'], - 'info_1_1': ['1'], 'info_1_2': ['dsds'], 'info_1_3': ['12'], - 'info_2_1': ['2'], 'info_2_2': ['sds'], 'info_2_3': ['121'], - 'info_3_1': ['3'], 'info_3_2': ['dsds'], 'info_3_3': ['21'], - 'info_4_1': [''], 'info_4_2': [''], 'info_4_3': [''], - 'info_5_1': [''], 'info_5_2': [''], 'info_5_3': [''], - 'info_6_1': [''], 'info_6_2': [''], 'info_6_3': [''], - - 'd_info_1_1': ['1'], 'd_info_1_2': ['sds'], 'd_info_1_3': ['21'], 'd_info_1_4': ['sdd'], - - 'd_info_2_1': ['2'], 'd_info_2_2': ['dsd'], 'd_info_2_3': ['23'], 'd_info_2_4': ['sds'], - 'd_info_3_1': ['3'], 'd_info_3_2': ['sd'], 'd_info_3_3': ['21'], 'd_info_3_4': ['dds'], - 'd_info_4_1': [''], 'd_info_4_2': [''], 'd_info_4_3': [''], 'd_info_4_4': [''], - 'd_info_5_1': [''], 'd_info_5_2': [''], 'd_info_5_3': [''], 'd_info_5_4': [''], - 'd_info_6_1': [''], 'd_info_6_2': [''], 'd_info_6_3': [''], 'd_info_6_4': [''], - 'amount_of_advance_required': ['211'], - 'certified_family_dependents': ['dqwd'], - 'certified_advance': ['dqwd'], - 'adjusted_month': ['qwdwd'], - 'date': ['2024-02-22'], - 'phone_number_for_contact': ['2312123'] \ No newline at end of file diff --git a/FusionIIIT/applications/hr2/serializers.py b/FusionIIIT/applications/hr2/serializers.py deleted file mode 100644 index b28cdcc45..000000000 --- a/FusionIIIT/applications/hr2/serializers.py +++ /dev/null @@ -1,42 +0,0 @@ -from rest_framework import serializers -from .models import LTCform, CPDAAdvanceform, CPDAReimbursementform, Leaveform, Appraisalform - -class LTC_serializer(serializers.ModelSerializer): - class Meta: - model = LTCform - fields = '__all__' - - def create(self, validated_data): - return LTCform.objects.create(**validated_data) - -class CPDAAdvance_serializer(serializers.ModelSerializer): - class Meta: - model = CPDAAdvanceform - fields = '__all__' - - def create(self, validated_data): - return CPDAAdvanceform.objects.create(**validated_data) - -class Appraisal_serializer(serializers.ModelSerializer): - class Meta: - model = Appraisalform - fields = '__all__' - - def create(self, validated_data): - return Appraisalform.objects.create(**validated_data) - -class CPDAReimbursement_serializer(serializers.ModelSerializer): - class Meta: - model = CPDAReimbursementform - fields = '__all__' - - def create(self, validated_data): - return CPDAReimbursementform.objects.create(**validated_data) - -class Leave_serializer(serializers.ModelSerializer): - class Meta: - model = Leaveform - fields = '__all__' - - def create(self, validated_data): - return Leaveform.objects.create(**validated_data) \ No newline at end of file diff --git a/FusionIIIT/applications/hr2/test.py b/FusionIIIT/applications/hr2/test.py deleted file mode 100644 index 89ec7917f..000000000 --- a/FusionIIIT/applications/hr2/test.py +++ /dev/null @@ -1,116 +0,0 @@ -def ltc_pre_processing(request): - ltc_form_data = {} - - # Extract general information - ltc_form_data['name'] = request.POST['name'] - ltc_form_data['block_year'] = int(request.POST['block_year']) - ltc_form_data['pf_no'] = int(request.POST['pf_no']) - ltc_form_data['basic_pay_salary'] = int(request.POST['basic_pay_salary']) - ltc_form_data['designation'] = request.POST['designation'] - ltc_form_data['department_info'] = request.POST['department_info'] - ltc_form_data['leave_availability'] = request.POST.getlist('leave_availability') == ['True', 'True'] - ltc_form_data['leave_start_date'] = request.POST['leave_start_date'] - ltc_form_data['leave_end_date'] = request.POST['leave_end_date'] - ltc_form_data['date_of_leave_for_family'] = request.POST['date_of_leave_for_family'] - ltc_form_data['nature_of_leave'] = request.POST['nature_of_leave'] - ltc_form_data['purpose_of_leave'] = request.POST['purpose_of_leave'] - ltc_form_data['hometown_or_not'] = request.POST.get('hometown_or_not') == 'True' - ltc_form_data['place_of_visit'] = request.POST['place_of_visit'] - ltc_form_data['address_during_leave'] = request.POST['address_during_leave'] - - # Extract details of family members - family_members = [] - for i in range(1, 7): - if request.POST.get(f'info_{i}_1'): - family_member = ','.join(request.POST.getlist(f'info_{i}_{j}')[0] for j in range(1, 4)) - family_members.append(family_member) - ltc_form_data['details_of_family_members_already_done'] = ','.join(family_members) - - # Extract details of dependents - dependents = [] - for i in range(1, 7): - if request.POST.get(f'd_info_{i}_1'): - dependent = ','.join(request.POST.getlist(f'd_info_{i}_{j}')[0] for j in range(1, 5)) - dependents.append(dependent) - ltc_form_data['details_of_dependents'] = ','.join(dependents) - - # Extract remaining fields - ltc_form_data['amount_of_advance_required'] = int(request.POST['amount_of_advance_required']) - ltc_form_data['certified_family_dependents'] = request.POST['certified_family_dependents'] - ltc_form_data['certified_advance'] = int(request.POST['certified_advance']) - ltc_form_data['adjusted_month'] = request.POST['adjusted_month'] - ltc_form_data['date'] = request.POST['date'] - ltc_form_data['phone_number_for_contact'] = int(request.POST['phone_number_for_contact']) - - return ltc_form_data - -# Example usage -request_data = { - 'csrfmiddlewaretoken': ['yLyPMZMWRBnDU3hSh5kPGq6AgOFNY5WTK1HaZxAuiozCzXBf8qfOML5irZJd8MkM'], - 'block_year': ['232'], - 'pf_no': ['222'], - 'basic_pay_salary': ['2322'], - 'name': ['dsds'], - 'designation': ['dsdsd'], - 'department_info': ['ds'], - 'leave_availability': ['True', 'True'], - 'leave_start_date': ['2024-02-22'], - 'leave_end_date': ['2024-02-22'], - 'date_of_leave_for_family': ['2024-02-22'], - 'nature_of_leave': ['dsds'], - 'purpose_of_leave': ['dsdsd'], - 'hometown_or_not': ['True'], - 'place_of_visit': [''], - 'address_during_leave': ['full street address'], - 'details_of_family_members_already_done': ['sds', 'dsd', 'dsd'], - 'info_1_1': ['1'], - 'info_1_2': ['dsds'], - 'info_1_3': ['12'], - 'info_2_1': ['2'], - 'info_2_2': ['sds'], - 'info_2_3': ['121'], - 'info_3_1': ['3'], - 'info_3_2': ['dsds'], - 'info_3_3': ['21'], - 'info_4_1': [''], - 'info_4_2': [''], - 'info_4_3': [''], - 'info_5_1': [''], - 'info_5_2': [''], - 'info_5_3': [''], - 'info_6_1': [''], - 'info_6_2': [''], - 'info_6_3': [''], - 'd_info_1_1': ['1'], - 'd_info_1_2': ['sds'], - 'd_info_1_3': ['21'], - 'd_info_1_4': ['sdd'], - 'd_info_2_1': ['2'], - 'd_info_2_2': ['dsd'], - 'd_info_2_3': ['23'], - 'd_info_2_4': ['sds'], - 'd_info_3_1': ['3'], - 'd_info_3_2': ['sd'], - 'd_info_3_3': ['21'], - 'd_info_3_4': ['dds'], - 'd_info_4_1': [''], - 'd_info_4_2': [''], - 'd_info_4_3': [''], - 'd_info_4_4': [''], - 'd_info_5_1': [''], - 'd_info_5_2': [''], - 'd_info_5_3': [''], - 'd_info_5_4': [''], - 'd_info_6_1': [''], - 'd_info_6_2': [''], - 'd_info_6_3': [''], - 'd_info_6_4': [''], - 'amount_of_advance_required': ['211'], - 'certified_family_dependents': ['dqwd'], - 'certified_advance': ['dqwd'], - 'adjusted_month': ['qwdwd'], - 'date': ['2024-02-22'], - 'phone_number_for_contact': ['2312123'] -} - -print(ltc_pre_processing(request_data)) diff --git a/FusionIIIT/applications/hr2/urls.py b/FusionIIIT/applications/hr2/urls.py index cfaddcec6..56918efb7 100644 --- a/FusionIIIT/applications/hr2/urls.py +++ b/FusionIIIT/applications/hr2/urls.py @@ -1,7 +1,6 @@ -from django.conf.urls import url, include +from django.conf.urls import url -from applications.hr2 import views -from applications.hr2.api import form_views +from . import views app_name = 'hr2' @@ -18,83 +17,5 @@ url(r'^administrativeProfile/$', views.administrative_profile, name='administrativeProfile'), url(r'^addnew/$', views.add_new_user, name='addnew'), - url(r'^ltc_form/(?P\d+)/$', views.ltc_form, - name='ltcForm'), - - url(r'^view_ltc_form/(?P\d+)/$', views.view_ltc_form, - name='view_ltc_form'), - url(r'^form_mangement_ltc/',views.form_mangement_ltc, name='form_mangement_ltc'), - url(r'dashboard/', views.dashboard, name='dashboard'), - url(r'^form_mangement_ltc_hr/(?P\d+)/$',views.form_mangement_ltc_hr, name='form_mangement_ltc_hr'), - url(r'^form_mangement_ltc_hod/',views.form_mangement_ltc_hod, name='form_mangement_ltc_hod'), - url(r'^search_employee/', views.search_employee, name='search_employee'), - url(r'^track_file/(?P\d+)/$', views.track_file, name='track_file'), - url('form_view_ltc/(?P\d+)/$', views.form_view_ltc, name='form_view_ltc'), - # url('file_handle/', views.file_handle, name='file_handle'), - url('file_handle_cpda/', views.file_handle_cpda, name='file_handle_cpda'), - url('file_handle_leave/', views.file_handle_leave, name='file_handle_leave'), - url('file_handle_ltc/', views.file_handle_ltc, name='file_handle_ltc'), - url('file_handle_appraisal/', views.file_handle_appraisal, name='file_handle_appraisal'), - url('file_handle_cpda_reimbursement/', views.file_handle_cpda_reimbursement, name='file_handle_cpda_reimbursement'), - - url(r'^cpda_form/(?P\d+)/$', views.cpda_form,name='cpdaForm'), - url(r'^view_cpda_form/(?P\d+)/$', views.view_cpda_form,name='view_cpda_form'), - url(r'^form_mangement_cpda/',views.form_mangement_cpda, name='form_mangement_cpda'), - url(r'^form_mangement_cpda_hr/(?P\d+)/$',views.form_mangement_cpda_hr, name='form_mangement_cpda_hr'), - url(r'^form_mangement_cpda_hod/',views.form_mangement_cpda_hod, name='form_mangement_cpda_hod'), - url('form_view_cpda/(?P\d+)/$', views.form_view_cpda, name='form_view_cpda'), - url(r'^api/',include('applications.hr2.api.urls')), - - url(r'^cpda_reimbursement_form/(?P\d+)/$', views.cpda_reimbursement_form,name='cpdaReimbursementForm'), - url(r'^view_cpda_reimbursement_form/(?P\d+)/$', views.view_cpda_reimbursement_form,name='view_cpda_reimbursement_form'), - url(r'form_view_cpda_reimbursement/(?P\d+)/$', views.form_view_cpda_reimbursement, name='form_view_cpda_reimbursement'), - url(r'^form_mangement_cpda_reimbursement/',views.form_mangement_cpda_reimbursement, name='form_mangement_cpda_reimbursement'), - url(r'^form_mangement_cpda_reimbursement_hr/(?P\d+)/$',views.form_mangement_cpda_reimbursement_hr, name='form_mangement_cpda_reimbursement_hr'), - url(r'^form_mangement_cpda_reimbursement_hod/',views.form_mangement_cpda_reimbursement_hod, name='form_mangement_cpda_reimbursement_hod'), - - url(r'^leave_form/(?P\d+)/$', views.leave_form,name='leaveForm'), - url(r'^view_leave_form/(?P\d+)/$', views.view_leave_form,name='view_leave_form'), - url(r'^form_mangement_leave/',views.form_mangement_leave, name='form_mangement_leave'), - url(r'^form_mangement_leave_hr/(?P\d+)/$',views.form_mangement_leave_hr, name='form_mangement_leave_hr'), - url(r'^form_mangement_leave_hod/',views.form_mangement_leave_hod, name='form_mangement_leave_hod'), - url('form_view_leave/(?P\d+)/$', views.form_view_leave, name='form_view_leave'), - - - - url(r'^appraisal_form/(?P\d+)/$', views.appraisal_form,name='appraisalForm'), - url(r'^view_appraisal_form/(?P\d+)/$', views.view_appraisal_form,name='view_appraisal_form'), - url(r'^form_mangement_appraisal/',views.form_mangement_appraisal, name='form_mangement_appraisal'), - url(r'^form_mangement_appraisal_hr/(?P\d+)/$',views.form_mangement_appraisal_hr, name='form_mangement_appraisal_hr'), - - url(r'^form_view_appraisal/(?P\d+)/$', views.form_view_appraisal, name='form_view_appraisal'), - url(r'^getform/$', views.getform , name='getform'), - url(r'^getformcpdaAdvance/$', views.getformcpdaAdvance , name='getformcpdaAdvance'), - url(r'^getformLeave/$', views.getformLeave , name='getformLeave'), - url(r'^getformAppraisal/$', views.getformAppraisal , name='getformAppraisal'), - url(r'^getformcpdaReimbursement/$', views.getformcpdaReimbursement , name='getformcpdaReimbursement'), - - - - - - - - - - - - - - - - - - - - - - - - ] diff --git a/FusionIIIT/applications/hr2/views.py b/FusionIIIT/applications/hr2/views.py index 939564d30..f2f9d4fcd 100644 --- a/FusionIIIT/applications/hr2/views.py +++ b/FusionIIIT/applications/hr2/views.py @@ -1,5 +1,4 @@ -import json -from django.shortcuts import render, get_object_or_404 +from django.shortcuts import render from .models import * from applications.globals.models import ExtraInfo from applications.globals.models import * @@ -12,23 +11,15 @@ from applications.establishment.models import * from applications.establishment.views import * from applications.eis.models import * -from applications.globals.models import ExtraInfo, HoldsDesignation, DepartmentInfo, Designation - +from applications.globals.models import ExtraInfo, HoldsDesignation, DepartmentInfo from html import escape from io import BytesIO import re -from rest_framework import status -from decimal import Decimal - from django.contrib.auth.models import User from django.http import HttpResponse, HttpResponseRedirect from django.shortcuts import (get_object_or_404, redirect, render, render) -from django.http import JsonResponse -from applications.filetracking.sdk.methods import * -from django.core.files.base import File as DjangoFile -from django.views.decorators.csrf import csrf_exempt def edit_employee_details(request, id): @@ -331,11 +322,15 @@ def add_new_user(request): if request.method == "POST": form = NewUserForm(request.POST) eform = AddExtraInfo(request.POST) - + # t_pass = request.POST['password1'] + # t_user = request.POST['username'] + if form.is_valid(): user = form.save() messages.success(request, "New User added Successfully") else: + print(form.errors) + # print(request.POST['password1']) t_pass = '0000' if 'password1' in request.POST: t_pass = request.POST['password1'] @@ -352,6 +347,7 @@ def add_new_user(request): eform.save() messages.success(request, "Extra info of user saved successfully") elif not eform.is_valid: + print(eform.errors) messages.error(request,"Some error occured") form = NewUserForm @@ -362,2153 +358,19 @@ def add_new_user(request): except: raise Http404("Post does not exist") - + # if request.method == "POST": + # form = EditServiceBookForm(request.POST, request.FILES) + + # if form.is_valid(): + # form.save() + # messages.success( + # request, "Employee Service Book details edited successfully") + # else: + # messages.warning(request, "Error in submitting form") + # pass + + # form = EditServiceBookForm(initial={'extra_info': employee.id}) context = {'employee': employee, "register_form": form, "eform": eform } return render(request, template, context) - - - -def ltc_pre_processing(request): - data = {} - detailsOfFamilyMembersAlreadyDone = "" - - for memeber in request.POST.getlist('detailsOfFamilyMembersAlreadyDone'): - if(memeber == ""): - detailsOfFamilyMembersAlreadyDone = detailsOfFamilyMembersAlreadyDone + 'None' + ',' - else: - detailsOfFamilyMembersAlreadyDone = detailsOfFamilyMembersAlreadyDone + memeber + ',' - - data['detailsOfFamilyMembersAlreadyDone'] = detailsOfFamilyMembersAlreadyDone.rstrip(',') - - - detailsOfFamilyMembersAboutToAvail = "" - - for i in range(1,4): - for j in range(1,4): - key_is = f'info_{i}_{j}' - - if(request.POST.get(key_is) == ""): - detailsOfFamilyMembersAboutToAvail = detailsOfFamilyMembersAboutToAvail + 'None' + ',' - else: - detailsOfFamilyMembersAboutToAvail = detailsOfFamilyMembersAboutToAvail + request.POST.get(key_is) + ',' - - data['detailsOfFamilyMembersAboutToAvail'] = detailsOfFamilyMembersAboutToAvail.rstrip(',') - - - detailsOfDependents = "" - - for i in range(1,7): - for j in range(1,5): - key_is = f'd_info_{i}_{j}' - if(request.POST.get(key_is) == ""): - detailsOfDependents = detailsOfDependents + 'None' + ',' - else: - detailsOfDependents = detailsOfDependents + request.POST.get(key_is) + ',' - - data['detailsOfDependents'] = detailsOfDependents.rstrip(',') - - return data - - -def reverse_ltc_pre_processing(data): - reversed_data = {} - - # Copying over simple key-value pairs - simple_keys = [ - 'blockYear', - 'pfNo', - 'basicPaySalary', - 'name', - 'designation', - 'departmentInfo', - 'leaveRequired', - 'leaveStartDate', - 'leaveEndDate', - 'dateOfDepartureForFamily', - 'natureOfLeave', - 'purposeOfLeave', - 'hometownOrNot', - 'placeOfVisit', - 'addressDuringLeave', - 'amountOfAdvanceRequired', - 'certifiedThatFamilyDependents', - 'certifiedThatAdvanceTakenOn', - 'adjustedMonth', - 'submissionDate', - 'phoneNumberForContact' - ] - - - for key in simple_keys: - value = getattr(data, key) - reversed_data[key] = value if value != 'None' else '' - - # Reversing array-like values - reversed_data['detailsOfFamilyMembersAlreadyDone'] = getattr(data,'detailsOfFamilyMembersAlreadyDone').split(',') - - detailsOfFamilyMembersAboutToAvail = getattr(data,'detailsOfFamilyMembersAboutToAvail').split(',') - for index, value in enumerate(detailsOfFamilyMembersAboutToAvail): - detailsOfFamilyMembersAboutToAvail[index] = value if value != 'None' else '' - - reversed_data['info_1_1'] = detailsOfFamilyMembersAboutToAvail[0] - reversed_data['info_1_2'] = detailsOfFamilyMembersAboutToAvail[1] - reversed_data['info_1_3'] = detailsOfFamilyMembersAboutToAvail[2] - reversed_data['info_2_1'] = detailsOfFamilyMembersAboutToAvail[3] - reversed_data['info_2_2'] = detailsOfFamilyMembersAboutToAvail[4] - reversed_data['info_2_3'] = detailsOfFamilyMembersAboutToAvail[5] - reversed_data['info_3_1'] = detailsOfFamilyMembersAboutToAvail[6] - reversed_data['info_3_2'] = detailsOfFamilyMembersAboutToAvail[7] - reversed_data['info_3_3'] = detailsOfFamilyMembersAboutToAvail[8] - - # # Reversing details_of_dependents - detailsOfDependents = getattr(data,'detailsOfDependents').split(',') - for i in range(1, 7): - for j in range(1, 5): - key = f'd_info_{i}_{j}' - value = detailsOfDependents.pop(0) - reversed_data[key] = value if value != 'None' else '' - - return reversed_data - -def get_designation_by_user_id(user_id): - try: - # Query HoldsDesignation model to get the user's designation - designation_objs = HoldsDesignation.objects.filter(user=user_id) - return designation_objs.first().designation - except ExtraInfo.DoesNotExist: - return None - except HoldsDesignation.DoesNotExist: - return None - -def search_employee(request): - search_text = request.GET.get('search', '') - data = {'designation': 'Assistant Professor'} - try: - - employee = User.objects.get(username = search_text) - - - holds_designation = HoldsDesignation.objects.filter(user=employee) - holds_designation = list(holds_designation) - - - - data['designation'] = str(holds_designation[0].designation) - except ExtraInfo.DoesNotExist: - data = {'error': "Employee doesn't exist"} - - return JsonResponse(data) - -def ltc_form(request, id): - """ Views for edit details""" - try: - employee = ExtraInfo.objects.get(user__id=id) - except: - raise Http404("Employee does not exist! id doesnt exist") - user_id = id - creator = User.objects.get(id = user_id) - - if(employee.user_type == 'faculty' or employee.user_type == 'staff' or employee.user_type == 'student'): - template = 'hr2Module/ltc_form.html' - - if request.method == "POST": - try: - - data = ltc_pre_processing(request) - - - form1 = { - 'employeeId': id, - 'name': request.POST.get('name'), - 'blockYear': request.POST.get('blockYear'), - 'basicPaySalary': request.POST.get('basicPaySalary'), - 'designation': request.POST.get('designation'), - 'pfNo': request.POST.get('pfNo'), - 'departmentInfo': request.POST.get('departmentInfo'), - 'leaveRequired': request.POST.get('leaveRequired'), - 'leaveStartDate': request.POST.get('leaveStartDate'), - 'leaveEndDate': request.POST.get('leaveEndDate'), - 'dateOfDepartureForFamily': request.POST.get('dateOfDepartureForFamily'), - 'natureOfLeave': request.POST.get('natureOfLeave'), - 'purposeOfLeave': request.POST.get('purposeOfLeave'), - 'hometownOrNot': request.POST.get('hometownOrNot'), - 'placeOfVisit': request.POST.get('placeOfVisit'), - 'addressDuringLeave': request.POST.get('addressDuringLeave'), - 'detailsOfFamilyMembersAlreadyDone': data['detailsOfFamilyMembersAlreadyDone'], - 'detailsOfFamilyMembersAboutToAvail': data['detailsOfFamilyMembersAboutToAvail'], - 'detailsOfDependents': data['detailsOfDependents'], - 'amountOfAdvanceRequired': request.POST.get('amountOfAdvanceRequired'), - 'certifiedThatFamilyDependents': request.POST.get('certifiedThatFamilyDependents'), - 'certifiedThatAdvanceTakenOn': request.POST.get('certifiedThatAdvanceTakenOn'), - 'adjustedMonth': request.POST.get('adjustedMonth'), - 'submissionDate': request.POST.get('submissionDate'), - 'phoneNumberForContact': request.POST.get('phoneNumberForContact'), - 'username_employee': request.POST.get('username_employee'), - 'designation_employee': request.POST.get('designation_employee'), - 'created_by' : creator, - } - - - try: - ltc_form = LTCform.objects.create( - employeeId=id, - name=request.POST.get('name'), - blockYear=request.POST.get('blockYear'), - pfNo=request.POST.get('pfNo'), - basicPaySalary=request.POST.get('basicPaySalary'), - designation=request.POST.get('designation'), - departmentInfo=request.POST.get('departmentInfo'), - leaveRequired=request.POST.get('leaveAvailability'), - leaveStartDate=request.POST.get('leaveStartDate'), - leaveEndDate=request.POST.get('leaveEndDate'), - dateOfDepartureForFamily=request.POST.get('dateOfLeaveForFamily'), - natureOfLeave=request.POST.get('natureOfLeave'), - purposeOfLeave=request.POST.get('purposeOfLeave'), - hometownOrNot=request.POST.get('hometownOrNot'), - placeOfVisit=request.POST.get('placeOfVisit'), - addressDuringLeave=request.POST.get('addressDuringLeave'), - detailsOfFamilyMembersAlreadyDone=data['detailsOfFamilyMembersAlreadyDone'], - detailsOfFamilyMembersAboutToAvail=data['detailsOfFamilyMembersAboutToAvail'], - detailsOfDependents=data['detailsOfDependents'], - amountOfAdvanceRequired=request.POST.get('amountOfAdvanceRequired'), - certifiedThatFamilyDependents=request.POST.get('certifiedThatFamilyDependents'), - certifiedThatAdvanceTakenOn=request.POST.get('certifiedThatAdvanceTakenOn'), - adjustedMonth=request.POST.get('adjustedMonth'), - submissionDate=request.POST.get('submissionDate'), - phoneNumberForContact=request.POST.get('phoneNumberForContact'), - created_by=creator, - ) - - except Exception as e: - - print("An error occurred while creating the LTC form:", e) - - - - - uploader = employee.user - uploader_designation = 'Assistant Professor' - - get_designation = get_designation_by_user_id(employee.user) - if(get_designation): - uploader_designation = get_designation - - receiver = request.POST.get('username_employee') - receiver_designation = request.POST.get('designation_employee') - src_module = "HR" - src_object_id = str(ltc_form.id) - file_extra_JSON = {"type": "LTC"} - - - # Create a file representing the LTC form and send it to HR admin - file_id = create_file( - uploader=uploader, - uploader_designation=uploader_designation, - receiver=receiver, - receiver_designation=receiver_designation, - src_module=src_module, - src_object_id=src_object_id, - file_extra_JSON=file_extra_JSON, - attached_file=None # Attach any file if necessary - ) - - - messages.success(request, "Ltc form filled successfully!") - - - return redirect(request.path_info) - - except Exception as e: - messages.warning(request, "Fill not correctly") - context = {'employee': employee} - return render(request, template, context) - - - - # Query all LTC requests - ltc_requests = LTCform.objects.filter(employeeId=id) - - username = employee.user - uploader_designation = 'Assistant Professor' - - - designation = get_designation_by_user_id(employee.user) - if(designation): - uploader_designation = designation - - - inbox = view_inbox(username = username, designation = uploader_designation, src_module = "HR") - archived_files = view_archived(username = username, designation = uploader_designation, src_module = "HR") - filtered_inbox = [] - for i in inbox: - item = i.get('file_extra_JSON', {}) - if item.get('type') == 'LTC': - filtered_inbox.append(i) - - filtered_archived_files = [] - for i in archived_files: - item = i.get('file_extra_JSON', {}) - if item.get('type') == 'LTC': - filtered_archived_files.append(i) - - - - - - - context = {'employee': employee, 'ltc_requests': ltc_requests, 'inbox': filtered_inbox , 'designation':designation, 'archived_files': filtered_archived_files , 'user_id': user_id} - - return render(request, template, context) - else: - return render(request, 'hr2Module/edit.html') - -def form_view_ltc(request , id): - ltc_request = get_object_or_404(LTCform, id=id) - - user_id = ltc_request.created_by.id - - from_user = request.GET.get('param1') - from_designation = request.GET.get('param2') - file_id = request.GET.get('param3') - - template = 'hr2Module/view_ltc_form.html' - ltc_request = reverse_ltc_pre_processing(ltc_request) - - context = {'ltc_request' : ltc_request , "button" : 1 , "file_id" : file_id, "from_user" :from_user , "from_designation" : from_designation ,"id" : id, "user_id" : user_id} - - return render(request , template , context) - -def track_file(request, id): - # Assuming file_history is a list of dictionaries - template = 'hr2Module/ltc_form_trackfile.html' - file_history = view_history(file_id=id) - - - context = {'file_history': file_history} - - # Create a JSON response - return render(request ,template , context) - -def get_current_file_owner(file_id: int) -> User: - ''' - This functions returns the current owner of the file. - The current owner is the latest recipient of the file - ''' - latest_tracking = Tracking.objects.filter( - file_id=file_id).order_by('-receive_date').first() - latest_recipient = latest_tracking.receiver_id - return latest_recipient - -def file_handle_leave(request): - if request.method == 'POST': - form_data2 = request.POST - form_data=request.POST.get('context') - action = form_data2.get('action') - - form_data=json.loads(form_data) - form_id = form_data['form_id'] - file_id = form_data['file_id'] - from_user = form_data['from_user'] - from_designation = form_data['from_designation'] - username_employee = form_data['username_employee'] - designation_employee = form_data['designation_employee'] - - remark = form_data['remark_id'] - - - #database - leave_form = LeaveForm.objects.get(id=form_id) - - leave_form.save() - - #database - try: - leave_form = LeaveForm.objects.get(id=form_id) - except LeaveForm.DoesNotExist: - return JsonResponse({"error": "LeaveForm object with the provided ID does not exist"}, status=404) - - - - current_owner = get_current_file_owner(file_id) - - # if action value is 0 then forward the file - # if action value is 1 then reject the file - # if action value is 3 then approve the file - # otherwise archive the file - - if(action == '0'): - if(remark == ""): - track_id = forward_file(file_id = file_id, receiver = username_employee, receiver_designation = designation_employee,remarks = f"Forwarded by {current_owner} to {username_employee}", file_extra_JSON = "None") - else: - track_id = forward_file(file_id = file_id, receiver = username_employee, receiver_designation = designation_employee,remarks = f"Forwarded by {current_owner} to {username_employee}, Reason : {remark}", file_extra_JSON = "None") - messages.success(request, "File forwarded successfully") - elif(action == '1'): - if(remark == ""): - track_id = forward_file(file_id = file_id, receiver = leave_form.name, receiver_designation = leave_form.designation, remarks = f"Rejected by {current_owner}", file_extra_JSON = "None") - else: - track_id = forward_file(file_id = file_id, receiver = leave_form.name, receiver_designation = leave_form.designation, remarks = f"Rejected by {current_owner}, Reason : {remark}", file_extra_JSON = "None") - messages.success(request, "File rejected successfully") - elif(action == '2'): - if(remark == ""): - track_id = forward_file(file_id = file_id, receiver = leave_form.name, receiver_designation = leave_form.designation, remarks = f"Approved by {current_owner}", file_extra_JSON = "None") - else: - track_id = forward_file(file_id = file_id, receiver = leave_form.name, receiver_designation = leave_form.designation, remarks = f"Approved by {current_owner}, Reason : {remark}", file_extra_JSON = "None") - leave_form.approved = True - leave_form.approvedDate = timezone.now() - leave_form.approved_by = current_owner - leave_form.save() - messages.success(request, "File approved successfully") - else: - is_archived = archive_file(file_id=file_id) - if( is_archived ): - messages.error(request, "Error in file archived") - else: - messages.success(request, "Success in file archived") - - - return HttpResponse("Success") - else: - - return HttpResponse("Failure") - - -def file_handle_cpda(request): - if request.method == 'POST': - form_data2 = request.POST - form_data=request.POST.get('context') - action = form_data2.get('action') - - form_data=json.loads(form_data) - form_id = form_data['form_id'] - file_id = form_data['file_id'] - from_user = form_data['from_user'] - from_designation = form_data['from_designation'] - username_employee = form_data['username_employee'] - designation_employee = form_data['designation_employee'] - - advanceAmountPDA = form_data['advanceAmountPDA'] - balanceAvailable = form_data['balanceAvailable'] - amountCheckedInPDA = form_data['amountCheckedInPDA'] - - remark = form_data['remark_id'] - #change - - - #database - try: - cpda_form = CPDAAdvanceform.objects.get(id=form_id) - except CPDAAdvanceform.DoesNotExist: - return JsonResponse({"error": "CPDAform object with the provided ID does not exist"}, status=404) - - - if advanceAmountPDA == "": - advanceAmountPDA = None - else: - advanceAmountPDA = Decimal(advanceAmountPDA) - - if balanceAvailable == "": - balanceAvailable = None - else: - balanceAvailable = Decimal(balanceAvailable) - - if amountCheckedInPDA == "": - amountCheckedInPDA = None - else: - amountCheckedInPDA = Decimal(amountCheckedInPDA) - - - - - # Update the attribute - setattr(cpda_form, "advanceAmountPDA", advanceAmountPDA) - setattr(cpda_form, "balanceAvailable", balanceAvailable) - setattr(cpda_form, "amountCheckedInPDA", amountCheckedInPDA) - cpda_form.save() - - #database - try: - cpda_form = CPDAAdvanceform.objects.get(id=form_id) - except CPDAAdvanceform.DoesNotExist: - return JsonResponse({"error": "CPDAform object with the provided ID does not exist"}, status=404) - - - current_owner = get_current_file_owner(file_id) - - # if action value is 0 then forward the file - # if action value is 1 then reject the file - # if action value is 3 then approve the file - # otherwise archive the file - - if(action == '0'): - if(remark == ""): - track_id = forward_file(file_id = file_id, receiver = username_employee, receiver_designation = designation_employee,remarks = f"Forwarded by {current_owner} to {username_employee}", file_extra_JSON = "None") - else: - track_id = forward_file(file_id = file_id, receiver = username_employee, receiver_designation = designation_employee,remarks = f"Forwarded by {current_owner} to {username_employee}, Reason : {remark}", file_extra_JSON = "None") - messages.success(request, "File forwarded successfully") - elif(action == '1'): - if(remark == ""): - track_id = forward_file(file_id = file_id, receiver = cpda_form.name, receiver_designation = cpda_form.designation, remarks = f"Rejected by {current_owner}", file_extra_JSON = "None") - else: - track_id = forward_file(file_id = file_id, receiver = cpda_form.name, receiver_designation = cpda_form.designation, remarks = f"Rejected by {current_owner}, Reason : {remark}", file_extra_JSON = "None") - messages.success(request, "File rejected successfully") - elif(action == '2'): - if(remark == ""): - track_id = forward_file(file_id = file_id, receiver = cpda_form.name, receiver_designation = cpda_form.designation, remarks = f"Approved by {current_owner}", file_extra_JSON = "None") - else: - track_id = forward_file(file_id = file_id, receiver = cpda_form.name, receiver_designation = cpda_form.designation, remarks = f"Approved by {current_owner}, Reason : {remark}", file_extra_JSON = "None") - cpda_form.approved = True - cpda_form.approvedDate = timezone.now() - cpda_form.approved_by = current_owner - cpda_form.save() - messages.success(request, "File approved successfully") - else: - is_archived = archive_file(file_id=file_id) - if( is_archived ): - messages.error(request, "Error in file archived") - else: - messages.success(request, "Success in file archived") - - - return HttpResponse("Success") - else: - - return HttpResponse("Failure") - - - -def file_handle_cpda_reimbursement(request): - if request.method == 'POST': - form_data2 = request.POST - form_data=request.POST.get('context') - action = form_data2.get('action') - - form_data=json.loads(form_data) - form_id = form_data['form_id'] - file_id = form_data['file_id'] - from_user = form_data['from_user'] - from_designation = form_data['from_designation'] - username_employee = form_data['username_employee'] - designation_employee = form_data['designation_employee'] - - advanceDueAdjustment = form_data['advanceDueAdjustment'] - balanceAvailable = form_data['balanceAvailable'] - amountCheckedInPDA = form_data['amountCheckedInPDA'] - - remark = form_data['remark_id'] - #change - - - #database - try: - cpda_form = CPDAReimbursementform.objects.get(id=form_id) - except CPDAReimbursementform.DoesNotExist: - return JsonResponse({"error": "CPDAReimbursementform object with the provided ID does not exist"}, status=404) - - - if advanceDueAdjustment == "": - advanceDueAdjustment = None - else: - advanceDueAdjustment = Decimal(advanceDueAdjustment) - - if balanceAvailable == "": - balanceAvailable = None - else: - balanceAvailable = Decimal(balanceAvailable) - - if amountCheckedInPDA == "": - amountCheckedInPDA = None - else: - amountCheckedInPDA = Decimal(amountCheckedInPDA) - - - # Update the attribute - setattr(cpda_form, "advanceDueAdjustment", advanceDueAdjustment) - setattr(cpda_form, "balanceAvailable", balanceAvailable) - setattr(cpda_form, "amountCheckedInPDA", amountCheckedInPDA) - cpda_form.save() - - #database - try: - cpda_form = CPDAReimbursementform.objects.get(id=form_id) - except CPDAReimbursementform.DoesNotExist: - return JsonResponse({"error": "CPDAReimbursementform object with the provided ID does not exist"}, status=404) - - - current_owner = get_current_file_owner(file_id) - - # if action value is 0 then forward the file - # if action value is 1 then reject the file - # if action value is 3 then approve the file - # otherwise archive the file - - if(action == '0'): - if(remark == ""): - track_id = forward_file(file_id = file_id, receiver = username_employee, receiver_designation = designation_employee,remarks = f"Forwarded by {current_owner} to {username_employee}", file_extra_JSON = "None") - else: - track_id = forward_file(file_id = file_id, receiver = username_employee, receiver_designation = designation_employee,remarks = f"Forwarded by {current_owner} to {username_employee}, Reason : {remark}", file_extra_JSON = "None") - messages.success(request, "File forwarded successfully") - elif(action == '1'): - if(remark == ""): - track_id = forward_file(file_id = file_id, receiver = cpda_form.name, receiver_designation = cpda_form.designation, remarks = f"Rejected by {current_owner}", file_extra_JSON = "None") - else: - track_id = forward_file(file_id = file_id, receiver = cpda_form.name, receiver_designation = cpda_form.designation, remarks = f"Rejected by {current_owner}, Reason : {remark}", file_extra_JSON = "None") - messages.success(request, "File rejected successfully") - elif(action == '2'): - if(remark == ""): - track_id = forward_file(file_id = file_id, receiver = cpda_form.name, receiver_designation = cpda_form.designation, remarks = f"Approved by {current_owner}", file_extra_JSON = "None") - else: - track_id = forward_file(file_id = file_id, receiver = cpda_form.name, receiver_designation = cpda_form.designation, remarks = f"Approved by {current_owner}, Reason : {remark}", file_extra_JSON = "None") - cpda_form.approved = True - cpda_form.approvedDate = timezone.now() - cpda_form.approved_by = current_owner - cpda_form.save() - messages.success(request, "File approved successfully") - else: - is_archived = archive_file(file_id=file_id) - if( is_archived ): - messages.error(request, "Error in file archived") - else: - messages.success(request, "Success in file archived") - - - return HttpResponse("Success") - else: - - return HttpResponse("Failure") - - - - -def file_handle_ltc(request): - if request.method == 'POST': - form_data2 = request.POST - form_data=request.POST.get('context') - action = form_data2.get('action') - - form_data=json.loads(form_data) - form_id = form_data['form_id'] - file_id = form_data['file_id'] - from_user = form_data['from_user'] - from_designation = form_data['from_designation'] - username_employee = form_data['username_employee'] - designation_employee = form_data['designation_employee'] - - remark = form_data['remark_id'] - #change - - - #database - try: - ltc_form = LTCform.objects.get(id=form_id) - except LTCform.DoesNotExist: - return JsonResponse({"error": "LTCform object with the provided ID does not exist"}, status=404) - - - ltc_form.save() - - - current_owner = get_current_file_owner(file_id) - - - # if action value is 0 then forward the file - # if action value is 1 then reject the file - # if action value is 3 then approve the file - # otherwise archive the file - - if(action == '0'): - if(remark == ""): - track_id = forward_file(file_id = file_id, receiver = username_employee, receiver_designation = designation_employee,remarks = f"Forwarded by {current_owner} to {username_employee}", file_extra_JSON = "None") - else: - track_id = forward_file(file_id = file_id, receiver = username_employee, receiver_designation = designation_employee,remarks = f"Forwarded by {current_owner} to {username_employee}, Reason : {remark}", file_extra_JSON = "None") - messages.success(request, "File forwarded successfully") - elif(action == '1'): - if(remark == ""): - track_id = forward_file(file_id = file_id, receiver = ltc_form.name, receiver_designation = ltc_form.designation, remarks = f"Rejected by {current_owner}", file_extra_JSON = "None") - else: - track_id = forward_file(file_id = file_id, receiver = ltc_form.name, receiver_designation = ltc_form.designation, remarks = f"Rejected by {current_owner}, Reason : {remark}", file_extra_JSON = "None") - messages.success(request, "File rejected successfully") - elif(action == '2'): - if(remark == ""): - track_id = forward_file(file_id = file_id, receiver = ltc_form.name, receiver_designation = ltc_form.designation, remarks = f"Approved by {current_owner}", file_extra_JSON = "None") - else: - track_id = forward_file(file_id = file_id, receiver = ltc_form.name, receiver_designation = ltc_form.designation, remarks = f"Approved by {current_owner}, Reason : {remark}", file_extra_JSON = "None") - ltc_form.approved = True - ltc_form.approvedDate = timezone.now() - ltc_form.approved_by = current_owner - ltc_form.save() - messages.success(request, "File approved successfully") - else: - is_archived = archive_file(file_id=file_id) - if( is_archived ): - messages.error(request, "Error in file archived") - else: - messages.success(request, "Success in file archived") - - - return HttpResponse("Success") - else: - - return HttpResponse("Failure") - -def file_handle_appraisal(request): - if request.method == 'POST': - form_data2 = request.POST - form_data=request.POST.get('context') - action = form_data2.get('action') - - form_data=json.loads(form_data) - form_id = form_data['form_id'] - file_id = form_data['file_id'] - from_user = form_data['from_user'] - from_designation = form_data['from_designation'] - username_employee = form_data['username_employee'] - designation_employee = form_data['designation_employee'] - - - remark = form_data['remark_id'] - try: - appraisal_form = Appraisalform.objects.get(id=form_id) - except Appraisalform.DoesNotExist: - return JsonResponse({"error": "Appraisalform object with the provided ID does not exist"}, status=404) - - - # Update the attribute - setattr(appraisal_form, "form_id", form_id) - - appraisal_form.save() - - current_owner = get_current_file_owner(file_id) - - #database - try: - appraisal_form = Appraisalform.objects.get(id=form_id) - except Appraisalform.DoesNotExist: - return JsonResponse({"error": "Appraisalform object with the provided ID does not exist"}, status=404) - - - # if action value is 0 then forward the file - # if action value is 1 then reject the file - # if action value is 3 then approve the file - # otherwise archive the file - - if(action == '0'): - if(remark == ""): - track_id = forward_file(file_id = file_id, receiver = username_employee, receiver_designation = designation_employee,remarks = f"Forwarded by {current_owner} to {username_employee}", file_extra_JSON = "None") - else: - track_id = forward_file(file_id = file_id, receiver = username_employee, receiver_designation = designation_employee,remarks = f"Forwarded by {current_owner} to {username_employee}, Reason : {remark}", file_extra_JSON = "None") - messages.success(request, "File forwarded successfully") - elif(action == '1'): - if(remark == ""): - track_id = forward_file(file_id = file_id, receiver = appraisal_form.name, receiver_designation = appraisal_form.designation, remarks = f"Rejected by {current_owner}", file_extra_JSON = "None") - else: - track_id = forward_file(file_id = file_id, receiver = appraisal_form.name, receiver_designation = appraisal_form.designation, remarks = f"Rejected by {current_owner}, Reason : {remark}", file_extra_JSON = "None") - messages.success(request, "File rejected successfully") - elif(action == '2'): - if(remark == ""): - track_id = forward_file(file_id = file_id, receiver = appraisal_form.name, receiver_designation = appraisal_form.designation, remarks = f"Approved by {current_owner}", file_extra_JSON = "None") - else: - track_id = forward_file(file_id = file_id, receiver = appraisal_form.name, receiver_designation = appraisal_form.designation, remarks = f"Approved by {current_owner}, Reason : {remark}", file_extra_JSON = "None") - appraisal_form.approved = True - appraisal_form.approvedDate = timezone.now() - appraisal_form.approved_by = current_owner - appraisal_form.save() - messages.success(request, "File approved successfully") - else: - is_archived = archive_file(file_id=file_id) - if( is_archived ): - messages.error(request, "Error in file archived") - else: - messages.success(request, "Success in file archived") - - - return HttpResponse("Success") - else: - - return HttpResponse("Failure") - - -def view_ltc_form(request, id): - ltc_request = get_object_or_404(LTCform, id=id) - - ltc_request = reverse_ltc_pre_processing(ltc_request) - - - context = { - 'ltc_request': ltc_request - } - return render(request,'hr2Module/view_ltc_form.html',context) - -def form_mangement_ltc(request): - if(request.method == "GET"): - username = "21BCS185" - designation = "hradmin" - inbox = view_inbox(username = username, designation = designation, src_module = "HR") - - - # Extract src_object_id values - src_object_ids = [item['src_object_id'] for item in inbox] - - ltc_requests = [] - - for src_object_id in src_object_ids: - ltc_request = get_object_or_404(LTCform, id=src_object_id) - ltc_requests.append(ltc_request) - - context= { - 'ltc_requests' : ltc_requests, - 'hr' : "1", - } - - - return render(request, 'hr2Module/ltc_form.html',context) - - -def form_mangement_ltc_hr(request,id): - uploader = "21BCS183" - uploader_designation = "student" - receiver = "21BCS181" - receiver_designation = "HOD" - src_module = "HR" - src_object_id = id, - file_extra_JSON = {"key": "value"} - - # Create a file representing the LTC form and send it to HR admin - file_id = create_file( - uploader=uploader, - uploader_designation=uploader_designation, - receiver=receiver, - receiver_designation=receiver_designation, - src_module=src_module, - src_object_id=src_object_id, - file_extra_JSON=file_extra_JSON, - attached_file=None # Attach any file if necessary - ) - - - messages.success(request, "Ltc form filled successfully") - - return HttpResponse("Sucess") - -def form_mangement_ltc_hod(request): - if(request.method == "GET"): - username = "21BCS181" - designation = "HOD" - inbox = view_inbox(username = username, designation = designation, src_module = "HR") - - - # Extract src_object_id values - src_object_ids = [item['src_object_id'] for item in inbox] - - ltc_requests = [] - - for src_object_id in src_object_ids: - ltc_request = get_object_or_404(LTCform, id=src_object_id) - ltc_requests.append(ltc_request) - - context= { - 'ltc_requests' : ltc_requests, - 'hr' : "1", - } - - - return render(request, 'hr2Module/ltc_form.html',context) - - - -@login_required(login_url='/accounts/login') -def dashboard(request): - user = request.user - - user_id = ExtraInfo.objects.get(user=user).user_id - context = {'user_id': user_id} - return render(request, 'hr2Module/dashboard.html',context) - - -# cpda form ----------------------------------------------------------- - -def reverse_cpda_pre_processing(data): - reversed_data = {} - - simple_keys = [ - 'name', 'designation', 'pfNo', 'purpose', 'amountRequired', 'advanceDueAdjustment', - 'submissionDate', - 'balanceAvailable', 'advanceAmountPDA' ,'amountCheckedInPDA', - ] - - - for key in simple_keys: - value = getattr(data, key) - reversed_data[key] = value if value != 'None' else '' - - return reversed_data - - -def cpda_form(request, id): - """ Views for edit details""" - try: - employee = ExtraInfo.objects.get(user__id=id) - except: - raise Http404("Employee does not exist! id doesnt exist") - - user_id = id - creator = User.objects.get(id = user_id) - - if(employee.user_type == 'faculty' or employee.user_type == 'staff' or employee.user_type == 'student' ): - template = 'hr2Module/cpda_form.html' - - if request.method == "POST": - try: - advanceAmountPDA = request.POST.get('advanceAmountPDA') - if advanceAmountPDA == "": - advanceAmountPDA = None - else: - advanceAmountPDA = Decimal(advanceAmountPDA) - - balanceAvailable = request.POST.get('balanceAvailable') - if balanceAvailable == "": - balanceAvailable = None - else: - balanceAvailable = Decimal(balanceAvailable) - - amountCheckedInPDA = request.POST.get('amountCheckedInPDA') - if amountCheckedInPDA == "": - amountCheckedInPDA = None - else: - amountCheckedInPDA = Decimal(amountCheckedInPDA) - - - form_2 = { - 'employeeId' : id, - 'name' : request.POST.get('name'), - 'designation' : request.POST.get('designation'), - 'pfNo' : request.POST.get('pfNo'), - 'purpose' : request.POST.get('purpose'), - 'amountRequired' : request.POST.get('amountRequired'), - 'advanceDueAdjustment' : request.POST.get('advanceDueAdjustment'), - 'submissionDate' : request.POST.get('submissionDate'), - 'balanceAvailable' : request.POST.get('balanceAvailable'), - 'advanceAmountPDA' : request.POST.get('advanceAmountPDA'), - 'amountCheckedInPDA' : request.POST.get('amountCheckedInPDA'), - 'created_by' : creator, - } - - cpda_form = CPDAAdvanceform.objects.create( - employeeId = id, - name = request.POST.get('name'), - designation = request.POST.get('designation'), - pfNo = request.POST.get('pfNo'), - purpose = request.POST.get('purpose'), - amountRequired = request.POST.get('amountRequired'), - advanceDueAdjustment = request.POST.get('advanceDueAdjustment'), - submissionDate = request.POST.get('submissionDate'), - balanceAvailable = request.POST.get('balanceAvailable'), - advanceAmountPDA = request.POST.get('advanceAmountPDA'), - amountCheckedInPDA = request.POST.get('amountCheckedInPDA'), - created_by=creator, - - ) - - - uploader = employee.user - uploader_designation = 'Assistant Professor' - - get_designation = get_designation_by_user_id(employee.user) - if(get_designation): - uploader_designation = get_designation - - receiver = request.POST.get('username_employee') - receiver_designation = request.POST.get('designation_employee') - src_module = "HR" #dikkat - src_object_id = str(cpda_form.id) - file_extra_JSON = {"type": "CPDAAdvance"} - - # Create a file representing the CPDA form - file_id = create_file( - uploader=uploader, - uploader_designation=uploader_designation, - receiver=receiver, - receiver_designation=receiver_designation, - src_module=src_module, - src_object_id=src_object_id, - file_extra_JSON=file_extra_JSON, - attached_file=None # Attach any file if necessary - ) - - - - messages.success(request, "CPDA form filled successfully") - - return redirect(request.path_info) - - except Exception as e: - messages.warning(request, "Fill not correctly") - context = {'employee': employee} - return render(request, template, context) - - cpda_requests = CPDAAdvanceform.objects.filter(employeeId=id) - - username = employee.user - uploader_designation = 'Assistant Professor' - - - designation = get_designation_by_user_id(employee.user) - if(designation): - uploader_designation = designation - - - inbox = view_inbox(username = username, designation = uploader_designation, src_module = "HR") - - archived_files = view_archived(username = username, designation = uploader_designation, src_module = "HR") - - filtered_inbox = [] - for i in inbox: - item = i.get('file_extra_JSON', {}) - if item.get('type') == 'CPDAAdvance': - filtered_inbox.append(i) - - filtered_archived_files = [] - for i in archived_files: - item = i.get('file_extra_JSON', {}) - if item.get('type') == 'CPDAAdvance': - filtered_archived_files.append(i) - - context = {'employee': employee, 'cpda_requests': cpda_requests, 'inbox': filtered_inbox , 'designation':designation, 'archived_files': filtered_archived_files,'user_id':user_id} - - - messages.success(request, "CPDA form filled successfully!") - return render(request, template, context) - else: - return render(request, 'hr2Module/edit.html') - - -def form_view_cpda(request , id): - cpda_request = get_object_or_404(CPDAAdvanceform, id=id) - user_id = cpda_request.created_by.id - from_user = request.GET.get('param1') - from_designation = request.GET.get('param2') - file_id = request.GET.get('param3') - - - - template = 'hr2Module/view_cpda_form.html' - cpda_request = reverse_cpda_pre_processing(cpda_request) - - context = {'cpda_request' : cpda_request , "button" : 1 , "file_id" : file_id, "from_user" :from_user , "from_designation" : from_designation,"id":id,"user_id":user_id} - - return render(request , template , context) - - -def view_cpda_form(request, id): - cpda_request = get_object_or_404(CPDAAdvanceform, id=id) - - cpda_request = reverse_cpda_pre_processing(cpda_request) - - - context = { - 'cpda_request': cpda_request - } - return render(request,'hr2Module/view_cpda_form.html',context) - - -def form_mangement_cpda(request): - if(request.method == "GET"): - username = "21BCS185" - designation = "hradmin" - inbox = view_inbox(username = username, designation = designation, src_module = "HR") - - - # Extract src_object_id values - src_object_ids = [item['src_object_id'] for item in inbox] - - - cpda_requests = [] - - for src_object_id in src_object_ids: - cpda_request = get_object_or_404(CPDAAdvanceform, id=src_object_id) - cpda_requests.append(cpda_request) - - context= { - 'cpda_requests' : cpda_requests, - 'hr' : "1", - } - - - return render(request, 'hr2Module/cpda_form.html',context) - -def form_mangement_cpda_hr(request,id): - uploader = "21BCS183" - uploader_designation = "student" - receiver = "21BCS181" - receiver_designation = "HOD" - src_module = "HR" - src_object_id = id, - file_extra_JSON = {"key": "value"} - - # Create a file representing the CPDA form and send it to HR admin - file_id = create_file( - uploader=uploader, - uploader_designation=uploader_designation, - receiver=receiver, - receiver_designation=receiver_designation, - src_module=src_module, - src_object_id=src_object_id, - file_extra_JSON=file_extra_JSON, - attached_file=None # Attach any file if necessary - ) - - - messages.success(request, "CPda form filled successfully") - - - return HttpResponse("Success") - - -def form_mangement_cpda_hod(request): - if(request.method == "GET"): - username = "21BCS181" - designation = "HOD" - inbox = view_inbox(username = username, designation = designation, src_module = "HR") - - - # Extract src_object_id values - src_object_ids = [item['src_object_id'] for item in inbox] - - - cpda_requests = [] - - for src_object_id in src_object_ids: - cpda_request = get_object_or_404(CPDAAdvanceform, id=src_object_id) - cpda_requests.append(cpda_request) - - context= { - 'cpda_requests' : cpda_requests, - 'hr' : "1", - } - - - return render(request, 'hr2Module/cpda_form.html',context) - - -# Leave form ------------------------------------------------------------- - -def reverse_leave_pre_processing(data): - reversed_data = {} - - # Copying over simple key-value pairs - simple_keys = [ - 'name', 'designation', 'submissionDate', 'pfNo', 'departmentInfo', 'natureOfLeave', - 'leaveStartDate', 'leaveEndDate', 'purposeOfLeave', 'addressDuringLeave', 'academicResponsibility', - 'addministrativeResponsibiltyAssigned' - ] - - - for key in simple_keys: - value = getattr(data, key) - reversed_data[key] = value if value != 'None' else '' - - return reversed_data - - -def leave_form(request, id): - """ Views for edit details""" - try: - employee = ExtraInfo.objects.get(user__id=id) - except: - raise Http404("Employee does not exist! id doesnt exist") - - user_id = id - creator = User.objects.get(id = user_id) - - if(employee.user_type == 'faculty' or employee.user_type == 'student' or employee.user_type == 'staff'): - template = 'hr2Module/leave_form.html' - - if request.method == "POST": - try: - - - form_3 = { - 'employeeId' : id, - 'name' : request.POST.get('name'), - 'designation' : request.POST.get('designation'), - 'submissionDate' : request.POST.get('submissionDate'), - 'pfNo' : request.POST.get('pfNo'), - 'departmentInfo' : request.POST.get('departmentInfo'), - 'natureOfLeave' : request.POST.get('natureOfLeave'), - 'leaveStartDate' : request.POST.get('leaveStartDate'), - 'leaveEndDate' : request.POST.get('leaveEndDate'), - 'purposeOfLeave' : request.POST.get('purposeOfLeave'), - 'addressDuringLeave' : request.POST.get('addressDuringLeave'), - 'academicResponsibility' : request.POST.get('academicResponsibility'), - 'addministrativeResponsibiltyAssigned' : request.POST.get('addministrativeResponsibiltyAssigned'), - 'created_by' : creator, - } - - leave_form = LeaveForm.objects.create( - employeeId = id, - name = request.POST.get('name'), - designation = request.POST.get('designation'), - submissionDate = request.POST.get('submissionDate'), - pfNo = request.POST.get('pfNo'), - departmentInfo = request.POST.get('departmentInfo'), - leaveStartDate = request.POST.get('leaveStartDate'), - leaveEndDate = request.POST.get('leaveEndDate'), - natureOfLeave = request.POST.get('natureOfLeave'), - purposeOfLeave = request.POST.get('purposeOfLeave'), - addressDuringLeave = request.POST.get('addressDuringLeave'), - academicResponsibility = request.POST.get('academicResponsibility'), - addministrativeResponsibiltyAssigned = request.POST.get('addministrativeResponsibiltyAssigned'), - created_by=creator, - ) - - - uploader = employee.user - uploader_designation = 'Assistant Professor' - - - get_designation = get_designation_by_user_id(employee.user) - if(get_designation): - uploader_designation = get_designation - - receiver = request.POST.get('username_employee') - receiver_designation = request.POST.get('designation_employee') - src_module = "HR" - src_object_id = str(leave_form.id) - file_extra_JSON = {"type": "Leave"} - - - # Create a file representing the CPDA form - file_id = create_file( - uploader=uploader, - uploader_designation=uploader_designation, - receiver=receiver, - receiver_designation=receiver_designation, - src_module=src_module, - src_object_id=src_object_id, - file_extra_JSON=file_extra_JSON, - attached_file=None # Attach any file if necessary - ) - - - messages.success(request, "Leave form filled successfully") - - return redirect(request.path_info) - - except Exception as e: - messages.warning(request, "Fill not correctly") - context = {'employee': employee} - return render(request, template, context) - - # Query all Leave requests - leave_requests = LeaveForm.objects.filter(employeeId=id) - - username = employee.user - uploader_designation = 'Assistant Professor' - - designation = get_designation_by_user_id(employee.user) - if(designation): - uploader_designation = designation - - - inbox = view_inbox(username = username, designation = uploader_designation, src_module = "HR") - - archived_files = view_archived(username = username, designation = uploader_designation, src_module = "HR") - - filtered_inbox = [] - for i in inbox: - item = i.get('file_extra_JSON', {}) - if item.get('type') == 'Leave': - filtered_inbox.append(i) - - filtered_archived_files = [] - for i in archived_files: - item = i.get('file_extra_JSON', {}) - if item.get('type') == 'Leave': - filtered_archived_files.append(i) - - - - context = {'employee': employee, 'leave_requests': leave_requests, 'inbox': filtered_inbox , 'designation':designation, 'archived_files': filtered_archived_files,'user_id':user_id} - - messages.success(request, "Leave form filled successfully!") - return render(request, template, context) - else: - return render(request, 'hr2Module/edit.html') - - - -def form_view_leave(request , id): - - leave_request = get_object_or_404(LeaveForm, id=id) - user_id = leave_request.created_by.id - from_user = request.GET.get('param1') - from_designation = request.GET.get('param2') - file_id = request.GET.get('param3') - - - template = 'hr2Module/view_leave_form.html' - leave_request = reverse_leave_pre_processing(leave_request) - - context = {'leave_request' : leave_request , "button" : 1 , "file_id" : file_id, "from_user" :from_user , "from_designation" : from_designation, "id" : id,"user_id":user_id} - - return render(request , template , context) - -# ek or bna lena -def view_leave_form(request, id): - leave_request = get_object_or_404(LeaveForm, id=id) - - - - leave_request = reverse_leave_pre_processing(leave_request) - - - context = { - 'leave_request': leave_request - } - return render(request,'hr2Module/view_leave_form.html',context) - - -def form_mangement_leave(request): - if(request.method == "GET"): - username = "21BCS185" - designation = "hradmin" - inbox = view_inbox(username = username, designation = designation, src_module = "HR") - - - # Extract src_object_id values - src_object_ids = [item['src_object_id'] for item in inbox] - - leave_requests = [] - - for src_object_id in src_object_ids: - leave_request = get_object_or_404(LeaveForm, id=src_object_id) - leave_requests.append(leave_request) - - context= { - 'leave_requests' : leave_requests, - 'hr' : "1", - } - - - return render(request, 'hr2Module/leave_form.html',context) - - -def form_mangement_leave_hr(request,id): - uploader = "21BCS183" - uploader_designation = "student" - receiver = "21BCS181" - receiver_designation = "HOD" - src_module = "HR" - src_object_id = id, - file_extra_JSON = {"key": "value"} - - # Create a file representing the Leave form and send it to HR admin - file_id = create_file( - uploader=uploader, - uploader_designation=uploader_designation, - receiver=receiver, - receiver_designation=receiver_designation, - src_module=src_module, - src_object_id=src_object_id, - file_extra_JSON=file_extra_JSON, - attached_file=None # Attach any file if necessary - ) - - - messages.success(request, "Leave form filled successfully") - - return HttpResponse("Sucess") - -def form_mangement_leave_hod(request): - if(request.method == "GET"): - username = "21BCS181" - designation = "HOD" - inbox = view_inbox(username = username, designation = designation, src_module = "HR") - - - # Extract src_object_id values - src_object_ids = [item['src_object_id'] for item in inbox] - - - leave_requests = [] - - for src_object_id in src_object_ids: - leave_request = get_object_or_404(LeaveForm, id=src_object_id) - leave_requests.append(leave_request) - - context= { - 'leave_requests' : leave_requests, - 'hr' : "1", - } - - - return render(request, 'hr2Module/leave_form.html',context) - - - -def appraisal_form(request, id): - """ Views for edit details""" - try: - employee = ExtraInfo.objects.get(user__id=id) - except: - raise Http404("Employee does not exist! id doesnt exist") - - user_id = id - creator = User.objects.get(id = user_id) - - if(employee.user_type == 'faculty' or employee.user_type == 'staff' or employee.user_type == 'student'): - template = 'hr2Module/appraisal_form.html' - - if request.method == "POST": - try: - - data = appraisal_pre_processing(request) - - - form_4 = { - 'employeeId': id, - 'name': request.POST.get('name'), - 'designation': request.POST.get('designation'), - 'disciplineInfo': request.POST.get('disciplineInfo'), - 'specificFieldOfKnowledge': request.POST.get('specificFieldOfKnowledge'), - 'currentResearchInterests': request.POST.get('currentResearchInterests'), - 'coursesTaught': data['coursesTaught'], - 'newCoursesIntroduced': data['newCoursesIntroduced'], - 'newCoursesDeveloped': data['newCoursesDeveloped'], - 'otherInstructionalTasks': request.POST.get('otherInstructionalTasks'), - 'thesisSupervision': data['thesisSupervision'], - 'sponsoredReseachProjects': data['sponsoredReseachProjects'], - 'otherResearchElement': request.POST.get('otherResearchElement'), - 'publication': request.POST.get('publication'), - 'referredConference': request.POST.get('referredConference'), - 'conferenceOrganised': request.POST.get('conferenceOrganised'), - 'membership': request.POST.get('membership'), - 'honours ' : request.POST.get('honours'), - 'editorOfPublications': request.POST.get('editorOfPublications'), - 'expertLectureDelivered': request.POST.get('expertLectureDelivered'), - 'membershipOfBOS': request.POST.get('membershipOfBOS'), - 'otherExtensionTasks': request.POST.get('otherExtensionTasks'), - 'administrativeAssignment': request.POST.get('administrativeAssignment'), - 'serviceToInstitute': request.POST.get('serviceToInstitute'), - 'otherContribution': request.POST.get('otherContribution'), - 'performanceComments' : request.POST.get('performanceComments'), - 'submissionDate' : request.POST.get('submissionDate'), - 'approved' : request.POST.get('approved'), - 'approvedDate' : request.POST.get('approvedDate'), - 'created_by' : creator, - - } - - - appraisal_form = Appraisalform.objects.create( - employeeId= id, - name= request.POST.get('name'), - designation= request.POST.get('designation'), - disciplineInfo= request.POST.get('disciplineInfo'), - specificFieldOfKnowledge= request.POST.get('specificFieldOfKnowledge'), - currentResearchInterests= request.POST.get('currentResearchInterests'), - coursesTaught= data['coursesTaught'], - newCoursesIntroduced= data['newCoursesIntroduced'], - newCoursesDeveloped= data['newCoursesDeveloped'], - otherInstructionalTasks= request.POST.get('otherInstructionalTasks'), - thesisSupervision= data['thesisSupervision'], - sponsoredReseachProjects= data['sponsoredReseachProjects'], - otherResearchElement= request.POST.get('otherResearchElement'), - publication= request.POST.get('publication'), - referredConference= request.POST.get('referredConference'), - conferenceOrganised= request.POST.get('conferenceOrganised'), - membership= request.POST.get('membership'), - honours = request.POST.get('honours'), - editorOfPublications= request.POST.get('editorOfPublications'), - expertLectureDelivered= request.POST.get('expertLectureDelivered'), - membershipOfBOS= request.POST.get('membershipOfBOS'), - otherExtensionTasks= request.POST.get('otherExtensionTasks'), - administrativeAssignment= request.POST.get('administrativeAssignment'), - serviceToInstitute= request.POST.get('serviceToInstitute'), - otherContribution= request.POST.get('otherContribution'), - performanceComments = request.POST.get('performanceComments'), - submissionDate = request.POST.get('submissionDate'), - approved = request.POST.get('approved'), - approvedDate = request.POST.get('approvedDate'), - created_by=creator, - - - ) - - uploader = employee.user - uploader_designation = 'Assistant Professor' - - get_designation = get_designation_by_user_id(employee.user) - if(get_designation): - uploader_designation = get_designation - - receiver = request.POST.get('username_employee') - receiver_designation = request.POST.get('designation_employee') - src_module = "HR" - src_object_id = str(appraisal_form.id) - file_extra_JSON = {"type": "Appraisal"} - - - # Create a file representing the AppraisL form and send it to HR admin - file_id = create_file( - uploader=uploader, - uploader_designation=uploader_designation, - receiver=receiver, - receiver_designation=receiver_designation, - src_module=src_module, - src_object_id=src_object_id, - file_extra_JSON=file_extra_JSON, - attached_file=None # Attach any file if necessary - ) - - - messages.success(request, "Appraisal form filled successfully") - - return redirect(request.path_info) - - except Exception as e: - messages.warning(request, "Fill not correctly") - context = {'employee': employee} - return render(request, template, context) - - - - appraisal_requests = Appraisalform.objects.filter(employeeId=id) - - username = employee.user - uploader_designation = 'Assistant Professor' - - - - designation = get_designation_by_user_id(employee.user) - if(designation): - uploader_designation = designation - - - inbox = view_inbox(username = username, designation = uploader_designation, src_module = "HR") - - archived_files = view_archived(username = username, designation = uploader_designation, src_module = "HR") - - filtered_inbox = [] - for i in inbox: - item = i.get('file_extra_JSON', {}) - if item.get('type') == 'Appraisal': - filtered_inbox.append(i) - - filtered_archived_files = [] - for i in archived_files: - item = i.get('file_extra_JSON', {}) - if item.get('type') == 'Appraisal': - filtered_archived_files.append(i) - - - context = {'employee': employee, 'appraisal_requests': appraisal_requests, 'inbox': filtered_inbox , 'designation':designation, 'archived_files': filtered_archived_files,'user_id':user_id} - - messages.success(request, "Appraisal form filled successfully!") - return render(request, template, context) - else: - return render(request, 'hr2Module/edit.html') - - - -def form_view_appraisal(request , id): - appraisal_request = get_object_or_404(Appraisalform, id=id) - user_id = appraisal_request.created_by.id - from_user = request.GET.get('param1') - from_designation = request.GET.get('param2') - file_id = request.GET.get('param3') - - - template = 'hr2Module/view_appraisal_form.html' - appraisal_request = reverse_appraisal_pre_processing(appraisal_request) - - context = {'appraisal_request' : appraisal_request , "button" : 1 , "file_id" : file_id, "from_user" :from_user , "from_designation" : from_designation,"id":id,"user_id":user_id} - - return render(request , template , context) - - -def view_appraisal_form(request, id): - appraisal_request = get_object_or_404(Appraisalform, id=id) - - - appraisal_request = reverse_appraisal_pre_processing(appraisal_request) - - context = { - 'appraisal_request': appraisal_request - } - return render(request,'hr2Module/view_appraisal_form.html',context) - - - -def form_mangement_appraisal(request): - if(request.method == "GET"): - username = "21BCS185" - designation = "hradmin" - inbox = view_inbox(username = username, designation = designation, src_module = "HR") - - - src_object_ids = [item['src_object_id'] for item in inbox] - - appraisal_requests = [] - - for src_object_id in src_object_ids: - appraisal_request = get_object_or_404(Appraisalform, id=src_object_id) - appraisal_requests.append(appraisal_request) - - context= { - 'appraisal_requests' : appraisal_requests, - 'hr' : "1", - } - - - return render(request, 'hr2Module/appraisal_form.html',context) - - -def form_mangement_appraisal_hr(request,id): - uploader = "21BCS183" - uploader_designation = "student" - receiver = "21BCS181" - receiver_designation = "HOD" - src_module = "HR" - src_object_id = id, - file_extra_JSON = {"key": "value"} - - # Create a file representing the Appraisal form and send it to HR admin - file_id = create_file( - uploader=uploader, - uploader_designation=uploader_designation, - receiver=receiver, - receiver_designation=receiver_designation, - src_module=src_module, - src_object_id=src_object_id, - file_extra_JSON=file_extra_JSON, - attached_file=None # Attach any file if necessary - ) - - - messages.success(request, "Appraisal form filled successfully") - - return HttpResponse("Sucess") - - - -def appraisal_pre_processing(request): - data = {} - - - coursesTaught = "" - - for i in range(1,3): - for j in range(1,8): - key_is = f'info_{i}_{j}' - - if(request.POST.get(key_is) == ""): - coursesTaught = coursesTaught + 'None' + ',' - else: - coursesTaught = coursesTaught + request.POST.get(key_is) + ',' - - data['coursesTaught'] = coursesTaught.rstrip(',') - - newCoursesIntroduced = "" - - for i in range(3,5): - for j in range(1,4): - key_is = f'info_{i}_{j}' - - if(request.POST.get(key_is) == ""): - newCoursesIntroduced = newCoursesIntroduced + 'None' + ',' - else: - newCoursesIntroduced = newCoursesIntroduced + request.POST.get(key_is) + ',' - - data['newCoursesIntroduced'] = newCoursesIntroduced.rstrip(',') - - - newCoursesDeveloped = "" - - for i in range(5,7): - for j in range(1,5): - key_is = f'info_{i}_{j}' - - if(request.POST.get(key_is) == ""): - newCoursesDeveloped = newCoursesDeveloped + 'None' + ',' - else: - newCoursesDeveloped = newCoursesDeveloped + request.POST.get(key_is) + ',' - - data['newCoursesDeveloped'] = newCoursesDeveloped.rstrip(',') - - thesisSupervision = "" - - for i in range(7,9): - for j in range(1,6): - key_is = f'info_{i}_{j}' - - if(request.POST.get(key_is) == ""): - thesisSupervision = thesisSupervision + 'None' + ',' - else: - thesisSupervision = thesisSupervision + request.POST.get(key_is) + ',' - - data['thesisSupervision'] = thesisSupervision.rstrip(',') - - - - sponsoredReseachProjects = "" - - for i in range(9,10): - for j in range(1,8): - key_is = f'info_{i}_{j}' - - if(request.POST.get(key_is) == ""): - sponsoredReseachProjects = sponsoredReseachProjects + 'None' + ',' - else: - sponsoredReseachProjects = sponsoredReseachProjects + request.POST.get(key_is) + ',' - - data['sponsoredReseachProjects'] = sponsoredReseachProjects.rstrip(',') - - - return data - - - - -def reverse_appraisal_pre_processing(data): - reversed_data = {} - - # Copying over simple key-value pairs - simple_keys = [ - 'name', 'designation', 'disciplineInfo', 'specificFieldOfKnowledge', 'designation', 'currentResearchInterests', - 'otherInstructionalTasks', 'otherResearchElement', 'publication', 'referredConference', - 'conferenceOrganised', 'membership', 'honours', 'editorOfPublications', - 'expertLectureDelivered', 'membershipOfBOS', 'otherExtensionTasks', - 'administrativeAssignment', 'serviceToInstitute', 'otherContribution', 'performanceComments', - 'submissionDate' - ] - - - for key in simple_keys: - value = getattr(data, key) - reversed_data[key] = value if value != 'None' else '' - - courses_taught = getattr(data,'coursesTaught').split(',') - for index, value in enumerate(courses_taught): - courses_taught[index] = value if value != 'None' else '' - - reversed_data['info_1_1'] = courses_taught[0] - reversed_data['info_1_2'] = courses_taught[1] - reversed_data['info_1_3'] = courses_taught[2] - reversed_data['info_1_4'] = courses_taught[3] - reversed_data['info_1_5'] = courses_taught[4] - reversed_data['info_1_6'] = courses_taught[5] - reversed_data['info_1_7'] = courses_taught[6] - reversed_data['info_2_1'] = courses_taught[7] - reversed_data['info_2_2'] = courses_taught[8] - reversed_data['info_2_3'] = courses_taught[9] - reversed_data['info_2_4'] = courses_taught[10] - reversed_data['info_2_5'] = courses_taught[11] - reversed_data['info_2_6'] = courses_taught[12] - reversed_data['info_2_7'] = courses_taught[13] - - # # Reversing details_of_dependents - new_courses_introduced = getattr(data,'newCoursesIntroduced').split(',') - for i in range(3, 5): - for j in range(1, 4): - key = f'info_{i}_{j}' - value = new_courses_introduced.pop(0) - reversed_data[key] = value if value != 'None' else '' - - - - newCoursesDeveloped = getattr(data,'newCoursesDeveloped').split(',') - for i in range(5, 7): - for j in range(1, 5): - key = f'info_{i}_{j}' - value = newCoursesDeveloped.pop(0) - reversed_data[key] = value if value != 'None' else '' - - - - thesis_reasearch = getattr(data,'otherResearchElement').split(',') - for i in range(7, 9): - for j in range(1, 6): - key = f'info_{i}_{j}' - if thesis_reasearch: - value = thesis_reasearch.pop() - else: - # Handle the case where the list is empty - print("The list is empty, cannot pop from it.") - # value = thesis_reasearch.pop(0) - reversed_data[key] = value if value != 'None' else '' - - - - sponsored_research = getattr(data,'sponsoredReseachProjects').split(',') - for i in range(9, 10): - for j in range(1, 8): - key = f'info_{i}_{j}' - value = sponsored_research.pop(0) - reversed_data[key] = value if value != 'None' else '' - - return reversed_data - - - -def reverse_cpda_reimbursement_pre_processing(data): - reversed_data = {} - - simple_keys = [ - 'name', 'designation', 'pfNo', 'purpose', 'advanceTaken', 'adjustmentSubmitted', - 'submissionDate', - 'balanceAvailable', 'advanceDueAdjustment', 'amountCheckedInPDA', - ] - - - for key in simple_keys: - value = getattr(data, key) - reversed_data[key] = value if value != 'None' else '' - - return reversed_data - - -def cpda_reimbursement_form(request, id): - """ Views for edit details""" - try: - employee = ExtraInfo.objects.get(user__id=id) - except: - raise Http404("Employee does not exist! id doesnt exist") - - user_id = id - creator = User.objects.get(id = user_id) - - if(employee.user_type == 'faculty' or employee.user_type == 'staff' or employee.user_type == 'student' ): - template = 'hr2Module/cpda_reimbursement_form.html' - - if request.method == "POST": - try: - - form_2 = { - 'employeeId' : id, - 'name' : request.POST.get('name'), - 'designation' : request.POST.get('designation'), - 'pfNo' : request.POST.get('pfNo'), - 'purpose' : request.POST.get('purpose'), - 'advanceTaken' : request.POST.get('advanceTaken'), - 'advanceDueAdjustment' : request.POST.get('advanceDueAdjustment'), - 'submissionDate' : request.POST.get('submissionDate'), - 'balanceAvailable' : request.POST.get('balanceAvailable'), - 'adjustmentSubmitted' : request.POST.get('adjustmentSubmitted'), - 'amountCheckedInPDA' : request.POST.get('amountCheckedInPDA'), - 'created_by' : creator, - } - - cpda_form = CPDAReimbursementform.objects.create( - employeeId = id, - name = request.POST.get('name'), - designation = request.POST.get('designation'), - pfNo = request.POST.get('pfNo'), - purpose = request.POST.get('purpose'), - advanceTaken = request.POST.get('advanceTaken'), - advanceDueAdjustment = request.POST.get('advanceDueAdjustment'), - submissionDate = request.POST.get('submissionDate'), - balanceAvailable = request.POST.get('balanceAvailable'), - adjustmentSubmitted = request.POST.get('adjustmentSubmitted'), - amountCheckedInPDA = request.POST.get('amountCheckedInPDA'), - created_by=creator, - - ) - - - uploader = employee.user - uploader_designation = 'Assistant Professor' - - get_designation = get_designation_by_user_id(employee.user) - if(get_designation): - uploader_designation = get_designation - - receiver = request.POST.get('username_employee') - receiver_designation = request.POST.get('designation_employee') - src_module = "HR" - src_object_id = str(cpda_form.id) - file_extra_JSON = {"type": "CPDAReimbursement"} - - # Create a file representing the CPDA form - file_id = create_file( - uploader=uploader, - uploader_designation=uploader_designation, - receiver=receiver, - receiver_designation=receiver_designation, - src_module=src_module, - src_object_id=src_object_id, - file_extra_JSON=file_extra_JSON, - attached_file=None # Attach any file if necessary - ) - - messages.success(request, "cpdareimbursement form filled successfully") - - return redirect(request.path_info) - - except Exception as e: - messages.warning(request, "Fill not correctly") - context = {'employee': employee} - return render(request, template, context) - - cpda_reimbursement_requests = CPDAReimbursementform.objects.filter(employeeId=id) - - username = employee.user - uploader_designation = 'Assistant Professor' - - - designation = get_designation_by_user_id(employee.user) - if(designation): - uploader_designation = designation - - - inbox = view_inbox(username = username, designation = uploader_designation, src_module = "HR") - - archived_files = view_archived(username = username, designation = uploader_designation, src_module = "HR") - - filtered_inbox = [] - for i in inbox: - item = i.get('file_extra_JSON', {}) - if item.get('type') == 'CPDAReimbursement': - filtered_inbox.append(i) - - filtered_archived_files = [] - for i in archived_files: - item = i.get('file_extra_JSON', {}) - if item.get('type') == 'CPDAReimbursement': - filtered_archived_files.append(i) - - - context = {'employee': employee, 'cpda_reimbursement_requests': cpda_reimbursement_requests, 'inbox': filtered_inbox , 'designation':designation, 'archived_files': filtered_archived_files,'user_id':user_id} - - - messages.success(request, "cpdareimbursement form filled successfully!") - return render(request, template, context) - else: - return render(request, 'hr2Module/edit.html') - - - - -def form_view_cpda_reimbursement(request , id): - cpda_reimbursement_request = get_object_or_404(CPDAReimbursementform, id=id) - user_id = cpda_reimbursement_request.created_by.id - # isko recheck krna h - from_user = request.GET.get('param1') - from_designation = request.GET.get('param2') - file_id = request.GET.get('param3') - - template = 'hr2Module/view_cpda_reimbursement_form.html' - cpda_reimbursement_request = reverse_cpda_reimbursement_pre_processing(cpda_reimbursement_request) - - context = {'cpda_reimbursement_request' : cpda_reimbursement_request , "button" : 1 , "file_id" : file_id, "from_user" :from_user , "from_designation" : from_designation,"id":id,"user_id":user_id} - - return render(request , template , context) - - -def view_cpda_reimbursement_form(request, id): - cpda_reimbursement_request = get_object_or_404(CPDAReimbursementform, id=id) - - cpda_reimbursement_request = reverse_cpda_reimbursement_pre_processing(cpda_reimbursement_request) - - context = { - 'cpda_reimbursement_request': cpda_reimbursement_request - } - return render(request,'hr2Module/view_cpda_reimbursement_form.html',context) - - - -def form_mangement_cpda_reimbursement(request): - if(request.method == "GET"): - username = "21BCS185" - designation = "hradmin" - inbox = view_inbox(username = username, designation = designation, src_module = "HR") - - - # Extract src_object_id values - src_object_ids = [item['src_object_id'] for item in inbox] - - - cpda_reimbursement_requests = [] - - for src_object_id in src_object_ids: - cpda_reimbursement_request = get_object_or_404(CPDAReimbursementform, id=src_object_id) - cpda_reimbursement_requests.append(cpda_reimbursement_request) - - context= { - 'cpda_reimbursement_requests' : cpda_reimbursement_requests, - 'hr' : "1", - } - - - return render(request, 'hr2Module/cpda_reimbursement_form.html',context) - -def form_mangement_cpda_reimbursement_hr(request,id): - uploader = "21BCS183" - uploader_designation = "student" - receiver = "21BCS181" - receiver_designation = "HOD" - src_module = "HR" - src_object_id = id, - file_extra_JSON = {"key": "value"} - - # Create a file representing the CPDA form and send it to HR admin - file_id = create_file( - uploader=uploader, - uploader_designation=uploader_designation, - receiver=receiver, - receiver_designation=receiver_designation, - src_module=src_module, - src_object_id=src_object_id, - file_extra_JSON=file_extra_JSON, - attached_file=None # Attach any file if necessary - ) - - - messages.success(request, "CPda form filled successfully") - - - return HttpResponse("Success") - - -def form_mangement_cpda_reimbursement_hod(request): - if(request.method == "GET"): - username = "21BCS181" - designation = "HOD" - inbox = view_inbox(username = username, designation = designation, src_module = "HR") - - - # Extract src_object_id values - src_object_ids = [item['src_object_id'] for item in inbox] - - - cpda_reimbursement_requests = [] - - for src_object_id in src_object_ids: - cpda_reimbursement_request = get_object_or_404(CPDAReimbursementform, id=src_object_id) - cpda_reimbursement_requests.append(cpda_reimbursement_request) - - context= { - 'cpda_reimbursement_requests' : cpda_reimbursement_requests, - 'hr' : "1", - } - - - return render(request, 'hr2Module/cpda_reimbursement_form.html',context) - - -def getform(request): - form_type = request.GET.get("type") - id = request.GET.get("id") - - if form_type == "LTC": - try: - forms = LTCform.objects.filter(created_by=id) - - form_data = [] - for form in forms: - form_data.append({ - 'id': form.id, - 'name': form.name, - 'designation': form.designation, - 'submissionDate': form.submissionDate.strftime("%Y-%m-%d") if form.submissionDate else None, - 'is_approved' : form.approved, - - }) - - return JsonResponse(form_data, safe=False) # Return JSON response - except LTCform.DoesNotExist: - return JsonResponse({"message": "No LTC forms found."}, status=404) - -def getformcpdaAdvance(request): - form_type = request.GET.get("type") - id = request.GET.get("id") - - if form_type == "CPDAAdvance": - try: - forms = CPDAAdvanceform.objects.filter(created_by=id) - form_data = [] - for form in forms: - form_data.append({ - 'id': form.id, - 'name': form.name, - 'designation': form.designation, - 'submissionDate': form.submissionDate.strftime("%Y-%m-%d") if form.submissionDate else None, - 'is_approved' : form.approved, - }) - - return JsonResponse(form_data, safe=False) # Return JSON response - except CPDAAdvanceform.DoesNotExist: - return JsonResponse({"message": "No CPDAAdvance forms found."}, status=404) - - -def getformLeave(request): - form_type = request.GET.get("type") - id = request.GET.get("id") - - if form_type == "Leave": - try: - forms = LeaveForm.objects.filter(created_by=id) - form_data = [] - for form in forms: - form_data.append({ - 'id': form.id, - 'name': form.name, - 'designation': form.designation, - 'submissionDate': form.submissionDate.strftime("%Y-%m-%d") if form.submissionDate else None, - 'is_approved' : form.approved, - # Add other fields as needed - }) - - return JsonResponse(form_data, safe=False) # Return JSON response - except LeaveForm.DoesNotExist: - return JsonResponse({"message": "No Leave forms found."}, status=404) - - -def getformAppraisal(request): - form_type = request.GET.get("type") - id = request.GET.get("id") - - if form_type == "Appraisal": - try: - forms = Appraisalform.objects.filter(created_by=id) - form_data = [] - for form in forms: - form_data.append({ - 'id': form.id, - 'name': form.name, - 'designation': form.designation, - 'submissionDate': form.submissionDate.strftime("%Y-%m-%d") if form.submissionDate else None, - 'is_approved' : form.approved, - }) - - return JsonResponse(form_data, safe=False) # Return JSON response - except Appraisalform.DoesNotExist: - return JsonResponse({"message": "No Appraisal forms found."}, status=404) - - - -def getformcpdaReimbursement(request): - form_type = request.GET.get("type") - id = request.GET.get("id") - - if form_type == "CPDAReimbursement": - try: - forms = CPDAReimbursementform.objects.filter(created_by=id) - form_data = [] - for form in forms: - form_data.append({ - 'id': form.id, - 'name': form.name, - 'designation': form.designation, - 'submissionDate': form.submissionDate.strftime("%Y-%m-%d") if form.submissionDate else None, - 'is_approved' : form.approved, - # Add other fields as needed - }) - - return JsonResponse(form_data, safe=False) # Return JSON response - except CPDAReimbursementform.DoesNotExist: - return JsonResponse({"message": "No CPDAReimbursement forms found."}, status=404) - - - diff --git a/FusionIIIT/applications/income_expenditure/migrations/0001_initial.py b/FusionIIIT/applications/income_expenditure/migrations/0001_initial.py index 953bfcf22..1501efdb9 100644 --- a/FusionIIIT/applications/income_expenditure/migrations/0001_initial.py +++ b/FusionIIIT/applications/income_expenditure/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 3.1.5 on 2024-07-16 15:44 +# Generated by Django 3.1.5 on 2023-03-15 18:53 from django.db import migrations, models import django.db.models.deletion diff --git a/FusionIIIT/applications/iwdModuleV2/migrations/0001_initial.py b/FusionIIIT/applications/iwdModuleV2/migrations/0001_initial.py index 5e56f2f96..bbde8386c 100644 --- a/FusionIIIT/applications/iwdModuleV2/migrations/0001_initial.py +++ b/FusionIIIT/applications/iwdModuleV2/migrations/0001_initial.py @@ -1,6 +1,5 @@ -# Generated by Django 3.1.5 on 2024-07-16 15:44 +# Generated by Django 3.1.5 on 2023-03-15 18:53 -import datetime from django.db import migrations, models import django.db.models.deletion @@ -13,14 +12,6 @@ class Migration(migrations.Migration): ] operations = [ - migrations.CreateModel( - name='Budget', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=200)), - ('budgetIssued', models.IntegerField(default=0)), - ], - ), migrations.CreateModel( name='FinancialBidDetails', fields=[ @@ -35,25 +26,6 @@ class Migration(migrations.Migration): ('id', models.CharField(max_length=200, primary_key=True, serialize=False)), ], ), - migrations.CreateModel( - name='Requests', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=200)), - ('description', models.CharField(max_length=200)), - ('area', models.CharField(max_length=200)), - ('requestCreatedBy', models.CharField(max_length=200)), - ('engineerProcessed', models.IntegerField(default=0)), - ('directorApproval', models.IntegerField(default=0)), - ('deanProcessed', models.IntegerField(default=0)), - ('status', models.CharField(max_length=200)), - ('issuedWorkOrder', models.IntegerField(default=0)), - ('workCompleted', models.IntegerField(default=0)), - ('billGenerated', models.IntegerField(default=0)), - ('billProcessed', models.IntegerField(default=0)), - ('billSettled', models.IntegerField(default=0)), - ], - ), migrations.CreateModel( name='PageOneDetails', fields=[ @@ -111,21 +83,6 @@ class Migration(migrations.Migration): ('key', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='iwdModuleV2.projects', unique=True)), ], ), - migrations.CreateModel( - name='WorkOrder', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=200)), - ('date', models.DateField(default=datetime.date.today)), - ('agency', models.CharField(max_length=200)), - ('amount', models.IntegerField(default=0)), - ('deposit', models.IntegerField(default=0)), - ('alloted_time', models.CharField(max_length=200)), - ('start_date', models.DateField()), - ('completion_date', models.DateField()), - ('request_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='iwdModuleV2.requests')), - ], - ), migrations.CreateModel( name='TechnicalBidDetails', fields=[ @@ -230,14 +187,6 @@ class Migration(migrations.Migration): ('key', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='iwdModuleV2.projects', unique=True)), ], ), - migrations.CreateModel( - name='Bills', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('file', models.FileField(upload_to='')), - ('request_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='iwdModuleV2.requests')), - ], - ), migrations.CreateModel( name='Agreement', fields=[ diff --git a/FusionIIIT/applications/iwdModuleV2/migrations/0002_auto_20241020_1126.py b/FusionIIIT/applications/iwdModuleV2/migrations/0002_data_integrity_fixes.py similarity index 61% rename from FusionIIIT/applications/iwdModuleV2/migrations/0002_auto_20241020_1126.py rename to FusionIIIT/applications/iwdModuleV2/migrations/0002_data_integrity_fixes.py index cbadd4cd6..971721369 100644 --- a/FusionIIIT/applications/iwdModuleV2/migrations/0002_auto_20241020_1126.py +++ b/FusionIIIT/applications/iwdModuleV2/migrations/0002_data_integrity_fixes.py @@ -1,5 +1,3 @@ -# Generated by Django 3.1.5 on 2024-10-20 11:26 - from django.db import migrations, models import django.db.models.deletion @@ -11,21 +9,6 @@ class Migration(migrations.Migration): ] operations = [ - migrations.AddField( - model_name='pageonedetails', - name='page_id', - field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, to='iwdModuleV2.projects'), - ), - migrations.AddField( - model_name='pagethreedetails', - name='page_id', - field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, to='iwdModuleV2.projects'), - ), - migrations.AddField( - model_name='pagetwodetails', - name='page_id', - field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, to='iwdModuleV2.projects'), - ), migrations.AlterField( model_name='addendum', name='key', @@ -56,21 +39,6 @@ class Migration(migrations.Migration): name='key', field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='iwdModuleV2.projects'), ), - migrations.AlterField( - model_name='pageonedetails', - name='id', - field=models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), - ), - migrations.AlterField( - model_name='pagethreedetails', - name='id', - field=models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), - ), - migrations.AlterField( - model_name='pagetwodetails', - name='id', - field=models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), - ), migrations.AlterField( model_name='prebiddetails', name='key', diff --git a/FusionIIIT/applications/iwdModuleV2/models.py b/FusionIIIT/applications/iwdModuleV2/models.py index 9c79aa21c..140590134 100644 --- a/FusionIIIT/applications/iwdModuleV2/models.py +++ b/FusionIIIT/applications/iwdModuleV2/models.py @@ -1,5 +1,5 @@ from django.db import models -from datetime import date + # Create your models here. @@ -9,7 +9,7 @@ class Projects(models.Model): class PageOneDetails(models.Model): - page_id = models.OneToOneField(Projects, on_delete=models.CASCADE, null=True) + id = models.ForeignKey(Projects, on_delete=models.CASCADE, primary_key=True) aESFile = models.FileField(null=True) dASA = models.DateField(null=True) nitNiqNo = models.IntegerField(null=True) @@ -31,7 +31,7 @@ class AESDetails(models.Model): class PageTwoDetails(models.Model): - page_id = models.OneToOneField(Projects, on_delete=models.CASCADE, null=True) + id = models.ForeignKey(Projects, on_delete=models.CASCADE, primary_key=True) corrigendum = models.FileField(null=True) addendum = models.FileField(null=True) preBidMeetingDetails = models.FileField(null=True) @@ -143,7 +143,7 @@ class Milestones(models.Model): class PageThreeDetails(models.Model): - page_id = models.OneToOneField(Projects, on_delete=models.CASCADE, null=True) + id = models.ForeignKey(Projects, on_delete=models.CASCADE, primary_key=True) extensionOfTime = models.FileField() actualCostOfBuilding = models.IntegerField() @@ -160,38 +160,3 @@ class NoOfTechnicalBidTimes(models.Model): key = models.OneToOneField(Projects, on_delete=models.CASCADE) number = models.IntegerField() -class Requests(models.Model): - name = models.CharField(max_length=200) - description = models.CharField(max_length=200) - area = models.CharField(max_length=200) - requestCreatedBy = models.CharField(max_length=200) - engineerProcessed = models.IntegerField(default=0) - directorApproval = models.IntegerField(default=0) - deanProcessed = models.IntegerField(default=0) - status = models.CharField(max_length=200) - issuedWorkOrder = models.IntegerField(default=0) - workCompleted = models.IntegerField(default=0) - billGenerated = models.IntegerField(default=0) - billProcessed = models.IntegerField(default=0) - billSettled = models.IntegerField(default=0) - -class WorkOrder(models.Model): - # request_id = models.IntegerField() - request_id = models.ForeignKey(Requests, on_delete=models.CASCADE) - name = models.CharField(max_length=200) - date = models.DateField(default=date.today) - agency = models.CharField(max_length=200) - amount = models.IntegerField(default=0) - deposit = models.IntegerField(default=0) - alloted_time = models.CharField(max_length=200) - start_date = models.DateField() - completion_date = models.DateField() - -class Bills(models.Model): - # requestId = models.IntegerField() - request_id = models.ForeignKey(Requests, on_delete=models.CASCADE) - file = models.FileField() - -class Budget(models.Model): - name = models.CharField(max_length=200) - budgetIssued = models.IntegerField(default=0) diff --git a/FusionIIIT/applications/iwdModuleV2/urls.py b/FusionIIIT/applications/iwdModuleV2/urls.py index e976034d3..6ad401098 100644 --- a/FusionIIIT/applications/iwdModuleV2/urls.py +++ b/FusionIIIT/applications/iwdModuleV2/urls.py @@ -1,5 +1,5 @@ -from django.conf.urls import url, include -from django.urls import path +from django.conf.urls import url + from . import views app_name = 'iwdModuleV2' @@ -36,38 +36,5 @@ url(r'milestoneView/$', views.milestoneView, name='Milestones'), url(r'addendumView/$', views.addendumView, name='Addendum View'), url('agreementView/$', views.agreementView, name='Agreement VIew'), - url(r'corrigendumView/$', views.corrigendumView, name='Corrigendum View'), - url(r'^requestsView/',views.requestsView, name='Requests view'), - url(r'^createdRequestsView/',views.createdRequests, name='Created Requests view'), - url(r'^handleEngineerProcessRequests/', views.handleEngineerProcessRequests, name='Engineer-Process-Requests'), - url(r'^engineerProcessedRequestsView/',views.engineerProcessedRequests, name='Engineer-Processed-Requests view'), - url(r'^handleDeanProcessRequests/', views.handleDeanProcessRequests, name='Dean-Process-Requests'), - url(r'^deanProcessedRequestsView/',views.deanProcessedRequests, name='Dean-Processed-Requests view'), - url(r'^handleDirectorApprovalRequests/', views.handleDirectorApprovalRequests, name='Director-Approval-Requests'), - url(r'^updateRejectedRequests/', views.updateRejectedRequests, name='Update-Rejected-Requests'), - url(r'^handleUpdateRequests/', views.handleUpdateRequests, name='Handle-Update-Requests'), - # url('rejectedRequests/', views.rejectedRequests, name='Rejected-Requests'), - url(r'^rejectedRequestsView/',views.rejectedRequests, name='Rejected Requests view'), - url(r'^requestsStatus/', views.requestsStatus, name='Requests-Status'), - url(r'^fetchDesignations/', views.fetchDesignations, name='Fetch-Designations'), - url(r'^fetchRequest/', views.fetchRequest, name='Fetch-Request'), - url(r'^issueWorkOrder/', views.issueWorkOrder, name='Issue Work Order'), - url(r'^workOrder/', views.workOrder, name='Work Order'), - url(r'^requestsInProgess/', views.requestsInProgess, name='Requests In Progress'), - url(r'^workCompleted/', views.workCompleted, name='Work Completed'), - url(r'^generateFinalBill/', views.generateFinalBill, name='Generate-Final-Bill'), - url(r'^handleBillGeneratedRequests/', views.handleBillGeneratedRequests, name='Handle-Bill-Generated-Requests'), - url(r'^generatedBillsView/', views.generatedBillsView, name='Generated-Bills-View'), - url(r'^handleProcessedBills/', views.handleProcessedBills, name='Handle-Processed-Bills'), - url(r'^auditDocumentView/', views.auditDocumentView, name='Audit-Document-View'), - url(r'^auditDocument/', views.auditDocument, name='Audit-Document'), - url(r'^settleBillsView/', views.settleBillsView, name='Settle-Bills-View'), - url(r'^handleSettleBillRequests/', views.handleSettleBillRequests, name='Handle-Settle-Bill-Requests'), - url(r'^viewBudget/', views.viewBudget, name='View-Budget'), - url(r'^budget/', views.budget, name='Budget'), - url(r'^addBudget/', views.addBudget, name='Add-Budget'), - url(r'^editBudgetView/', views.editBudgetView, name='Edit-Budget-View'), - url(r'^editBudget/', views.editBudget, name='Edit-Budget'), - path('view_file//',views.view_file,name="view_file"), - + url(r'corrigendumView/$', views.corrigendumView, name='Corrigendum View') ] diff --git a/FusionIIIT/applications/iwdModuleV2/views.py b/FusionIIIT/applications/iwdModuleV2/views.py index be7042499..2b36550fc 100644 --- a/FusionIIIT/applications/iwdModuleV2/views.py +++ b/FusionIIIT/applications/iwdModuleV2/views.py @@ -1,23 +1,9 @@ from django.shortcuts import render, redirect -from django.urls import reverse -from django.db.models import Q -from django.contrib import messages + from applications.globals.models import * -from applications.ps1.models import * -from django.contrib.auth.decorators import login_required from .models import * from django.http import HttpResponseRedirect -from applications.filetracking.sdk.methods import * -from applications.globals.models import ExtraInfo, HoldsDesignation, Designation -from notification.views import iwd_notif -from datetime import datetime -from reportlab.lib.pagesizes import letter -from reportlab.pdfgen import canvas -from reportlab.platypus import Table, TableStyle -from reportlab.lib import colors -from django.http import HttpResponse -from io import BytesIO -from django.core.files.base import File as DjangoFile + # Create your views here. @@ -31,22 +17,17 @@ # owing to length and inherent extensiveness of code. Rather than, whosoever read this code is advised to do so # in conjunction with SRS. After that, everything will become easier. -# def dashboard(request): -# eligible = False -# userObj = request.user -# userDesignationObjects = HoldsDesignation.objects.filter(user=userObj) -# for p in userDesignationObjects: -# if p.designation.name == 'Admin IWD': -# eligible = True -# break -# return render(request, 'iwdModuleV2/dashboard.html', {'eligible': eligible}) - -#Junior Engineer, Electrical Engineer (Civil), Electrical_AE, Electrical_JE, EE, Civil_AE, Civil_JE - def dashboard(request): - eligible = request.session.get('currentDesignationSelected') + eligible = False + userObj = request.user + userDesignationObjects = HoldsDesignation.objects.filter(user=userObj) + for p in userDesignationObjects: + if p.designation.name == 'Admin IWD': + eligible = True + break return render(request, 'iwdModuleV2/dashboard.html', {'eligible': eligible}) + def page1_1(request): if request.method == 'POST': formObject = PageOneDetails() @@ -126,8 +107,7 @@ def page2_1(request): def corrigendumInput(request): if request.method == 'POST': - existingObject = CorrigendumTable.objects.filter( - key=Projects.objects.get(id=request.session['projectId'])) + existingObject = CorrigendumTable.objects.filter(key=Projects.objects.get(id=request.session['projectId'])) if existingObject.count() == 1: existingObject.delete() formObject = CorrigendumTable() @@ -148,8 +128,7 @@ def corrigendumInput(request): def addendumInput(request): if request.method == 'POST': - existingObject = Addendum.objects.filter( - key=Projects.objects.get(id=request.session['projectId'])) + existingObject = Addendum.objects.filter(key=Projects.objects.get(id=request.session['projectId'])) if existingObject.count() == 1: existingObject.delete() formObject = Addendum() @@ -166,8 +145,7 @@ def addendumInput(request): def PreBidForm(request): if request.method == 'POST': - existingObject = PreBidDetails.objects.filter( - key=Projects.objects.get(id=request.session['projectId'])) + existingObject = PreBidDetails.objects.filter(key=Projects.objects.get(id=request.session['projectId'])) if existingObject.count() == 1: existingObject.delete() formObject = PreBidDetails() @@ -183,11 +161,9 @@ def PreBidForm(request): def noOfEntriesTechnicalBid(request): formNoTechnicalObjects = NoOfTechnicalBidTimes() - formNoTechnicalObjects.key = Projects.objects.get( - id=request.session['projectId']) + formNoTechnicalObjects.key = Projects.objects.get(id=request.session['projectId']) if request.method == 'POST': - existingObject = NoOfTechnicalBidTimes.objects.filter( - key=Projects.objects.get(id=request.session['projectId'])) + existingObject = NoOfTechnicalBidTimes.objects.filter(key=Projects.objects.get(id=request.session['projectId'])) if existingObject.count() == 1: existingObject.delete() formNoTechnicalObjects.number = int(request.POST['number']) @@ -198,11 +174,9 @@ def noOfEntriesTechnicalBid(request): def TechnicalBidForm(request): formObject = TechnicalBidDetails() - numberOfTechnicalBidTimes = NoOfTechnicalBidTimes.objects.get( - key=Projects.objects.get(id=request.session['projectId'])).number + numberOfTechnicalBidTimes = NoOfTechnicalBidTimes.objects.get(key=Projects.objects.get(id=request.session['projectId'])).number if request.method == 'POST': - existingObject = TechnicalBidDetails.objects.filter( - key=Projects.objects.get(id=request.session['projectId'])) + existingObject = TechnicalBidDetails.objects.filter(key=Projects.objects.get(id=request.session['projectId'])) if existingObject.count() == 1: existingObject.delete() formObject = TechnicalBidDetails() @@ -210,14 +184,12 @@ def TechnicalBidForm(request): formObject.sNo = request.POST['sNo'] formObject.requirements = request.POST['requirements'] formObject.save() - TechnicalBidContractorDetails.objects.filter( - key=formObject).all().delete() + TechnicalBidContractorDetails.objects.filter(key=formObject).all().delete() for w in range(numberOfTechnicalBidTimes): formContractorObject = TechnicalBidContractorDetails() formContractorObject.key = formObject formContractorObject.name = request.POST[str(w) + 'name'] - formContractorObject.description = request.POST[str( - w) + 'Description'] + formContractorObject.description = request.POST[str(w) + 'Description'] formContractorObject.save() return redirect('iwdModuleV2/noOfEntriesFinancialBid') return render(request, 'iwdModuleV2/page2_support_4_technicalbid.html', @@ -226,15 +198,12 @@ def TechnicalBidForm(request): def noOfEntriesFinancialBid(request): listOfContractors = [] - objectTechnicalBid = TechnicalBidDetails.objects.get( - key=Projects.objects.get(id=request.session['projectId'])) - objects = TechnicalBidContractorDetails.objects.filter( - key=objectTechnicalBid) + objectTechnicalBid = TechnicalBidDetails.objects.get(key=Projects.objects.get(id=request.session['projectId'])) + objects = TechnicalBidContractorDetails.objects.filter(key=objectTechnicalBid) for t in objects: listOfContractors.append(t.name) if request.method == 'POST': - existingObject = FinancialBidDetails.objects.filter( - key=Projects.objects.get(id=request.session['projectId'])) + existingObject = FinancialBidDetails.objects.filter(key=Projects.objects.get(id=request.session['projectId'])) if existingObject.count() == 1: existingObject.delete() formObject = FinancialBidDetails() @@ -248,8 +217,7 @@ def noOfEntriesFinancialBid(request): formContractorObject.name = listOfContractors[f] formContractorObject.totalCost = request.POST[listOfContractors[f] + 'totalCost'] formContractorObject.estimatedCost = request.POST[listOfContractors[f] + 'estimatedCost'] - formContractorObject.percentageRelCost = request.POST[ - listOfContractors[f] + 'percentageRelCost'] + formContractorObject.percentageRelCost = request.POST[listOfContractors[f] + 'percentageRelCost'] formContractorObject.perFigures = request.POST[listOfContractors[f] + 'perFigures'] formContractorObject.save() return redirect('iwdModuleV2/letterOfIntent') @@ -259,8 +227,7 @@ def noOfEntriesFinancialBid(request): def letterOfIntent(request): if request.method == 'POST': - existingObject = LetterOfIntentDetails.objects.filter( - key=Projects.objects.get(id=request.session['projectId'])) + existingObject = LetterOfIntentDetails.objects.filter(key=Projects.objects.get(id=request.session['projectId'])) if existingObject.count() == 1: existingObject.delete() formObject = LetterOfIntentDetails() @@ -277,8 +244,7 @@ def letterOfIntent(request): def workOrderForm(request): if request.method == 'POST': - existingObject = WorkOrderForm.objects.filter( - key=Projects.objects.get(id=request.session['projectId'])) + existingObject = WorkOrderForm.objects.filter(key=Projects.objects.get(id=request.session['projectId'])) if existingObject.count() == 1: existingObject.delete() formObject = WorkOrderForm() @@ -301,8 +267,7 @@ def workOrderForm(request): def AgreementInput(request): if request.method == 'POST': - existingObject = Agreement.objects.filter( - key=Projects.objects.get(id=request.session['projectId'])) + existingObject = Agreement.objects.filter(key=Projects.objects.get(id=request.session['projectId'])) if existingObject.count() == 1: existingObject.delete() formObject = Agreement() @@ -326,8 +291,7 @@ def milestonesForm(request): formObject.timeAllowed = request.POST['timeAllowed'] formObject.save() return redirect('iwdModuleV2/page3_1') - Milestones.objects.filter(key=Projects.objects.get( - id=request.session['projectId'])).all().delete() + Milestones.objects.filter(key=Projects.objects.get(id=request.session['projectId'])).all().delete() return render(request, 'iwdModuleV2/page2_support_9_milestone.html', {}) @@ -357,42 +321,35 @@ def ExtensionOfTimeForm(request): def page1View(request): - if request.POST: - request.session['projectId'] = request.POST['id'] - projectPageOne = PageOneDetails.objects.get( - id=Projects.objects.get(page_id=request.session['projectId'])) + request.session['projectId'] = request.POST['id'] + projectPageOne = PageOneDetails.objects.get(id=Projects.objects.get(id=request.session['projectId'])) return render(request, 'iwdModuleV2/Page1.html', {'x': projectPageOne}) def page2View(request): - projectPageTwo = PageTwoDetails.objects.get( - id=Projects.objects.get(page_id=request.session['projectId'])) + projectPageTwo = PageTwoDetails.objects.get(id=Projects.objects.get(id=request.session['projectId'])) return render(request, 'iwdModuleV2/Page2.html', {'x': projectPageTwo}) def AESView(request): - objects = AESDetails.objects.filter( - key=Projects.objects.get(id=request.session['projectId'])) + objects = AESDetails.objects.filter(key=Projects.objects.get(id=request.session['projectId'])) return render(request, 'iwdModuleV2/AA&ES.html', {'AES': objects}) def financialBidView(request): elements = [] - objects = FinancialBidDetails.objects.filter( - key=Projects.objects.get(id=request.session['projectId'])) + objects = FinancialBidDetails.objects.filter(key=Projects.objects.get(id=request.session['projectId'])) for f in objects: contractorObjects = FinancialContractorDetails.objects.filter(key=f) for w in contractorObjects: - obj = [f.sNo, f.description, w.name, w.estimatedCost, - w.percentageRelCost, w.perFigures, w.totalCost] + obj = [f.sNo, f.description, w.name, w.estimatedCost, w.percentageRelCost, w.perFigures, w.totalCost] elements.append(obj) return render(request, 'iwdModuleV2/Page2_financialbid.html', {'financial': elements}) def technicalBidView(request): elements = [] - objects = TechnicalBidDetails.objects.filter( - key=Projects.objects.get(id=request.session['projectId'])) + objects = TechnicalBidDetails.objects.filter(key=Projects.objects.get(id=request.session['projectId'])) for f in objects: contractorObjects = TechnicalBidContractorDetails.objects.filter(key=f) for w in contractorObjects: @@ -402,877 +359,45 @@ def technicalBidView(request): def preBidDetailsView(request): - preBidObjects = PreBidDetails.objects.filter( - key=Projects.objects.get(id=request.session['projectId'])) + preBidObjects = PreBidDetails.objects.filter(key=Projects.objects.get(id=request.session['projectId'])) return render(request, 'iwdModuleV2/Page2_pre-bid.html', {'preBidDetails': preBidObjects}) def corrigendumView(request): - corrigendumObject = CorrigendumTable.objects.get( - key=Projects.objects.get(id=request.session['projectId'])) + corrigendumObject = CorrigendumTable.objects.get(key=Projects.objects.get(id=request.session['projectId'])) return render(request, 'iwdModuleV2/corrigendum.html', {'corrigendum': corrigendumObject}) def addendumView(request): - addendumObject = Addendum.objects.get( - key=Projects.objects.get(id=request.session['projectId'])) + addendumObject = Addendum.objects.get(key=Projects.objects.get(id=request.session['projectId'])) return render(request, 'iwdModuleV2/Addendum.html', {'x': addendumObject}) def letterOfIntentView(request): - letterOfIntentObject = LetterOfIntentDetails.objects.get( - key=Projects.objects.get(id=request.session['projectId'])) + letterOfIntentObject = LetterOfIntentDetails.objects.get(key=Projects.objects.get(id=request.session['projectId'])) return render(request, 'iwdModuleV2/letterOfIntent.html', {'x': letterOfIntentObject}) def workOrderFormView(request): - workOrderFormObject = WorkOrderForm.objects.get( - key=Projects.objects.get(id=request.session['projectId'])) + workOrderFormObject = WorkOrderForm.objects.get(key=Projects.objects.get(id=request.session['projectId'])) return render(request, 'iwdModuleV2/WorkOrderForm.html', {'x': workOrderFormObject}) def agreementView(request): - agreementObject = Agreement.objects.get( - key=Projects.objects.get(id=request.session['projectId'])) + agreementObject = Agreement.objects.get(key=Projects.objects.get(id=request.session['projectId'])) return render(request, 'iwdModuleV2/Agreement.html', {'agreement': agreementObject}) def milestoneView(request): - milestoneObjects = Milestones.objects.filter( - key=Projects.objects.get(id=request.session['projectId'])) + milestoneObjects = Milestones.objects.filter(key=Projects.objects.get(id=request.session['projectId'])) return render(request, 'iwdModuleV2/Page2_milestones.html', {'milestones': milestoneObjects}) def page3View(request): - pageThreeDetails = PageThreeDetails.objects.get( - id=Projects.objects.get(page_id=request.session['projectId'])) + pageThreeDetails = PageThreeDetails.objects.get(id=Projects.objects.get(id=request.session['projectId'])) return render(request, 'iwdModuleV2/Page3.html', {'x': pageThreeDetails}) def extensionFormView(request): - extensionObjects = ExtensionOfTimeDetails.objects.filter( - key=Projects.objects.get(id=request.session['projectId'])) + extensionObjects = ExtensionOfTimeDetails.objects.filter(key=Projects.objects.get(id=request.session['projectId'])) return render(request, 'iwdModuleV2/ExtensionForm.html', {'extension': extensionObjects}) - -designations_list = ["Junior Engineer", "Executive Engineer (Civil)", "Electrical_AE", "Electrical_JE", "EE", "Civil_AE", "Civil_JE", "Dean (P&D)", "Director", "Accounts Admin", "Admin IWD", "Auditor"] - -@login_required -def fetchDesignations(request): - designations = Designation.objects.filter() - - holdsDesignations = [] - - for d in designations: - for x in designations_list: - if d.name == x: - list = HoldsDesignation.objects.filter(designation=d) - holdsDesignations.append(list) - - return render(request, 'iwdModuleV2/requestsView.html', {'holdsDesignations' : holdsDesignations}) - -@login_required -def requestsView(request): - if request.method == 'POST': - formObject = Requests() - formObject.name = request.POST['name'] - formObject.description = request.POST['description'] - formObject.area = request.POST['area'] - formObject.engineerProcessed = 0 - formObject.directorApproval = 0 - formObject.deanProcessed = 0 - formObject.requestCreatedBy = request.user.username - formObject.status = "Pending" - formObject.issuedWorkOrder = 0 - formObject.workCompleted = 0 - formObject.billGenerated = 0 - formObject.billProcessed = 0 - formObject.billSettled = 0 - formObject.save() - request_object = Requests.objects.get(pk=formObject.pk) - desg = request.session.get('currentDesignationSelected') - receiver_user, receiver_desg = request.POST['designation'].split('|') - create_file(uploader=request.user.username, - uploader_designation=desg, - receiver=receiver_user, - receiver_designation=receiver_desg, - src_module="IWD", - src_object_id= str(request_object.id), - file_extra_JSON= {"value": 2}, - attached_file = None) - messages.success(request, "Request Successfully Created") - receiver_user_obj = User.objects.get(username=receiver_user) - - iwd_notif(request.user, receiver_user_obj, "Request_added") - - eligible = request.session.get('currentDesignationSelected') - return render(request, 'iwdModuleV2/dashboard.html', {'eligible' : eligible}) - -@login_required -def createdRequests(request): - obj = [] - desg = request.session.get('currentDesignationSelected') - - inbox_files = view_inbox( - username=request.user, - designation=desg, - src_module="IWD" - ) - - for result in inbox_files: - src_object_id = result['src_object_id'] - request_object = Requests.objects.filter(id=src_object_id).first() - file_obj= File.objects.get(src_object_id= request_object.id, src_module = "IWD") - if request_object: - element = [request_object.id, request_object.name, request_object.area, request_object.description, request_object.requestCreatedBy, file_obj.id] - obj.append(element) - - return render(request, 'iwdModuleV2/createdRequests.html', {'obj' : obj}) - -@login_required -def view_file(request, id, url): - file1= File.objects.get(id=id) - tracks= Tracking.objects.filter(file_id=file1) - current_user = Tracking.objects.filter(file_id=file1).order_by('-receive_date')[0].receiver_id - print(current_user) - - designations = Designation.objects.filter() - - holdsDesignations = [] - - for d in designations: - for x in designations_list: - if d.name == x: - list = HoldsDesignation.objects.filter(designation=d) - holdsDesignations.append(list) - - eligible = request.session.get('currentDesignationSelected') - - return render(request, "iwdModuleV2/view_file.html", context= {"file": file1, "tracks": tracks, "current_user": current_user, "holdsDesignations" : holdsDesignations, "url" : url, "eligible" : eligible}) - -@login_required -def handleEngineerProcessRequests(request): - if request.method == 'POST': - obj= request.POST - - fileid = obj.get('fileid') - request_id = File.objects.get(id=fileid).src_object_id - - remarks = obj.get('remarks') - attachment = request.FILES.get('attachment') - receiver_user, receiver_desg = request.POST['designation'].split('|') - - - forward_file( - file_id= fileid, - receiver= receiver_user, - receiver_designation=receiver_desg, - file_extra_JSON= { "message": "Request forwarded."}, - remarks= remarks, - file_attachment= attachment, - ) - - Requests.objects.filter(id=request_id).update(engineerProcessed=1, status="Approved by the Engineer") - - obj = [] - desg = request.session.get('currentDesignationSelected') - - inbox_files = view_inbox( - username=request.user, - designation=desg, - src_module="IWD" - ) - - for result in inbox_files: - src_object_id = result['src_object_id'] - request_object = Requests.objects.filter(id=src_object_id).first() - file_obj= File.objects.get(src_object_id= request_object.id, src_module = "IWD") - if request_object: - element = [request_object.id, request_object.name, request_object.area, request_object.description, request_object.requestCreatedBy, file_obj.id] - obj.append(element) - - messages.success(request, "File Forwarded") - receiver_user_obj = User.objects.get(username=receiver_user) - - iwd_notif(request.user, receiver_user_obj, "file_forward") - - return render(request, 'iwdModuleV2/createdRequests.html', {'obj' : obj}) - - -@login_required -def engineerProcessedRequests(request): - - obj = [] - - desg = request.session.get('currentDesignationSelected') - - inbox_files = view_inbox( - username=request.user, - designation=desg, - src_module="IWD" - ) - - for result in inbox_files: - src_object_id = result['src_object_id'] - request_object = Requests.objects.filter(id=src_object_id).first() - file_obj= File.objects.get(src_object_id= src_object_id, src_module = "IWD") - if request_object: - element = [request_object.id, request_object.name, request_object.area, request_object.description, request_object.requestCreatedBy, file_obj.id] - obj.append(element) - - return render(request, 'iwdModuleV2/engineerProcessedRequests.html', {'obj' : obj}) - -@login_required -def handleDeanProcessRequests(request): - if request.method == 'POST': - - obj= request.POST - - fileid = obj.get('fileid') - request_id = File.objects.get(id=fileid).src_object_id - - remarks = obj.get('remarks') - attachment = request.FILES.get('attachment') - receiver_user, receiver_desg = request.POST['designation'].split('|') - - - forward_file( - file_id= fileid, - receiver= receiver_user, - receiver_designation=receiver_desg, - file_extra_JSON= { "message": "Request forwarded."}, - remarks= remarks, - file_attachment= attachment, - ) - - Requests.objects.filter(id=request_id).update(deanProcessed=1, status="Approved by the dean") - desg = request.session.get('currentDesignationSelected') - - inbox_files = view_inbox( - username=request.user, - designation=desg, - src_module="IWD" - ) - - obj = [] - - for result in inbox_files: - src_object_id = result['src_object_id'] - request_object = Requests.objects.filter(id=src_object_id).first() - file_obj= File.objects.get(src_object_id = src_object_id, src_module = "IWD") - if request_object: - element = [request_object.id, request_object.name, request_object.area, request_object.description, request_object.requestCreatedBy, file_obj.id] - obj.append(element) - - messages.success(request, "File Forwarded") - receiver_user_obj = User.objects.get(username=receiver_user) - - iwd_notif(request.user, receiver_user_obj, "file_forward") - - return render(request, 'iwdModuleV2/engineerProcessedRequests.html', {'obj': obj}) - -@login_required -def deanProcessedRequests(request): - obj = [] - - desg = request.session.get('currentDesignationSelected') - - inbox_files = view_inbox( - username=request.user, - designation=desg, - src_module="IWD" - ) - - for result in inbox_files: - src_object_id = result['src_object_id'] - request_object = Requests.objects.filter(id=src_object_id).first() - file_obj= File.objects.get(src_object_id = src_object_id, src_module = "IWD") - if request_object: - element = [request_object.id, request_object.name, request_object.area, request_object.description, request_object.requestCreatedBy, file_obj.id] - obj.append(element) - - return render(request, 'iwdModuleV2/deanProcessedRequests.html', {'obj' : obj}) - -@login_required -def handleDirectorApprovalRequests(request): - if request.method == 'POST': - obj= request.POST - - fileid = obj.get('fileid') - request_id = File.objects.get(id=fileid).src_object_id - - remarks = obj.get('remarks') - attachment = request.FILES.get('attachment') - receiver_user, receiver_desg = request.POST['designation'].split('|') - - - forward_file( - file_id= fileid, - receiver= receiver_user, - receiver_designation=receiver_desg, - file_extra_JSON= { "message": "Request forwarded."}, - remarks= remarks, - file_attachment= attachment, - ) - - message = "" - - if (obj.get('action') == 'approve'): - message = "Request_approved" - Requests.objects.filter(id=request_id).update(directorApproval=1, status="Approved by the director") - else: - message = "Request_rejected" - Requests.objects.filter(id=request_id).update(directorApproval=-1, status="Rejected by the director") - - desg = request.session.get('currentDesignationSelected') - - inbox_files = view_inbox( - username=request.user, - designation=desg, - src_module="IWD" - ) - - obj = [] - - for result in inbox_files: - src_object_id = result['src_object_id'] - request_object = Requests.objects.filter(id=src_object_id).first() - file_obj= File.objects.get(src_object_id = src_object_id, src_module = "IWD") - if request_object: - element = [request_object.id, request_object.name, request_object.area, request_object.description, request_object.requestCreatedBy, file_obj.id] - obj.append(element) - - messages.success(request, "File forwarded") - receiver_user_obj = User.objects.get(username=receiver_user) - - iwd_notif(request.user, receiver_user_obj, message) - - return render(request, 'iwdModuleV2/deanProcessedRequests.html', {'obj': obj}) - -@login_required -def rejectedRequests(request): - obj = [] - - desg = request.session.get('currentDesignationSelected') - - inbox_files = view_inbox( - username=request.user, - designation=desg, - src_module="IWD" - ) - - for result in inbox_files: - src_object_id = result['src_object_id'] - request_object = Requests.objects.filter(id=src_object_id).first() - if request_object.directorApproval == -1: - element = [request_object.id, request_object.name, request_object.area, request_object.description, request_object.requestCreatedBy] - obj.append(element) - - designations = Designation.objects.filter() - - holdsDesignations = [] - - for d in designations: - for x in designations_list: - if d.name == x: - list = HoldsDesignation.objects.filter(designation=d) - holdsDesignations.append(list) - - return render(request, 'iwdModuleV2/rejectedRequests.html', {'obj' : obj, 'holdsDesignations' : holdsDesignations}) - -@login_required -def updateRejectedRequests(request): - request_id = request.POST.get("id", 0) - - desg = request.session.get('currentDesignationSelected') - - inbox_files = view_inbox( - username=request.user, - designation=desg, - src_module="IWD" - ) - - for p in inbox_files: - if p['src_object_id'] == request_id: - delete_file(file_id = p['id']) - break - - designations = Designation.objects.filter() - - holdsDesignations = [] - obj = [] - - request_object = Requests.objects.get(id=request_id) - - obj = [request_object.id, request_object.name, request_object.description, request_object.area] - - for d in designations: - for x in designations_list: - if d.name == x: - list = HoldsDesignation.objects.filter(designation=d) - holdsDesignations.append(list) - - return render(request, 'iwdModuleV2/updateRequests.html', {'obj' : obj, 'holdsDesignations' : holdsDesignations}) - -@login_required -def handleUpdateRequests(request): - if request.method == 'POST': - request_id = request.POST.get("id", 0) - desg = request.session.get('currentDesignationSelected') - receiver_user, receiver_desg = request.POST['designation'].split('|') - Requests.objects.filter(id=request_id).update(name=request.POST['name'], - description=request.POST['description'], - area=request.POST['area'], - engineerProcessed=0, - directorApproval=0, - deanProcessed=0, - requestCreatedBy=request.user.username, - status="Pending", - issuedWorkOrder=0, - workCompleted=0, - billGenerated=0, - billProcessed=0, - billSettled=0) - create_file(uploader=request.user.username, - uploader_designation=desg, - receiver=receiver_user, - receiver_designation=receiver_desg, - src_module="IWD", - src_object_id= str(request_id), - file_extra_JSON= {"value": 2}, - attached_file = None) - messages.success(request, "Request updated") - receiver_user_obj = User.objects.get(username=receiver_user) - - iwd_notif(request.user, receiver_user_obj, "Request_added") - eligible = request.session.get('currentDesignationSelected') - return render(request, 'iwdModuleV2/dashboard.html', {'eligible' : eligible}) - -@login_required -def issueWorkOrder(request): - obj = [] - - desg = request.session.get('currentDesignationSelected') - - inbox_files = view_inbox( - username=request.user, - designation=desg, - src_module="IWD" - ) - - for result in inbox_files: - src_object_id = result['src_object_id'] - request_object = Requests.objects.filter(id=src_object_id, directorApproval=1, issuedWorkOrder=0).first() - if request_object: - element = [request_object.id, request_object.name, request_object.area, request_object.description, request_object.requestCreatedBy] - obj.append(element) - return render(request, 'iwdModuleV2/issueWorkOrder.html', {'obj' : obj}) - -@login_required -def fetchRequest(request): - request_id = request.POST.get("id", 0) - req_request = Requests.objects.get(id=request_id) - return render(request, 'iwdModuleV2/workOrder.html', {'req' : req_request}) - -@login_required -def workOrder(request): - if request.method == 'POST': - request_instance = Requests.objects.get(pk=request.POST['id']) - formObject = WorkOrder() - formObject.request_id = request_instance - formObject.name = request.POST['name'] - formObject.date = request.POST['date'] - formObject.agency = request.POST['agency'] - formObject.amount = request.POST['amount'] - formObject.deposit = request.POST['deposit'] - formObject.alloted_time = request.POST['time'] - formObject.start_date = request.POST['startDate'] - formObject.completion_date = request.POST['completionDate'] - formObject.save() - - Requests.objects.filter(id=request.POST['id']).update(status="Work Order issued", issuedWorkOrder=1) - - desg = request.session.get('currentDesignationSelected') - - inbox_files = view_inbox( - username=request.user, - designation=desg, - src_module="IWD" - ) - - obj = [] - - for result in inbox_files: - src_object_id = result['src_object_id'] - request_object = Requests.objects.filter(id=src_object_id).first() - if request_object.issuedWorkOrder==0: - element = [request_object.id, request_object.name, request_object.area, request_object.description, request_object.requestCreatedBy] - obj.append(element) - messages.success(request, "Work Order Issued") - return render(request, 'iwdModuleV2/issueWorkOrder.html', {'obj' : obj}) - -@login_required -def requestsStatus(request): - obj = [] - requestsObject = Requests.objects.all() - for x in requestsObject: - element = [x.id, x.name, x.area, x.description, x.requestCreatedBy, x.status] - obj.append(element) - return render(request, 'iwdModuleV2/requestsStatus.html', {'obj' : obj}) - -@login_required -def requestsInProgess(request): - obj = [] - requestsObject = Requests.objects.filter(issuedWorkOrder=1, billGenerated=0) - for x in requestsObject: - element = [x.id, x.name, x.area, x.description, x.requestCreatedBy, x.workCompleted] - obj.append(element) - return render(request, 'iwdModuleV2/requestsInProgress.html', {'obj' : obj}) - -@login_required -def workCompleted(request): - if request.method == 'POST': - Requests.objects.filter(id=request.POST['id']).update(workCompleted=1, status="Work Completed") - obj = [] - requestsObject = Requests.objects.filter(issuedWorkOrder=1, billGenerated=0) - for x in requestsObject: - element = [x.id, x.name, x.area, x.description, x.requestCreatedBy, x.workCompleted] - obj.append(element) - messages.success(request, "Work Completed") - return render(request, 'iwdModuleV2/requestsInProgress.html', {'obj' : obj}) - -@login_required -def generateFinalBill(request): - if request.method == 'POST': - requestId = request.POST.get("id", 0) - - # usedItems = UsedItems.objects.filter(request_id=requestId) - workOrder = WorkOrder.objects.get(request_id=requestId) - - iwd_items = StockItem.objects.filter(department = 34) - - itemsList = [] - - for x in iwd_items: - stock_entry_id = x.StockEntryId.item_id.file_info - indent_file_object = IndentFile.objects.filter(file_info = stock_entry_id) - for items in indent_file_object: - if items.purpose == requestId: - element = [items.item_name, items.quantity, items.estimated_cost, items.file_info.upload_date] - itemsList.append(element) - - - # for used in usedItems: - # element = [used.itemName, used.quantity, used.cost, used.date] - # itemsList.append(element) - - filename = f"Request_id_{requestId}_final_bill.pdf" - - buffer = BytesIO() - - c = canvas.Canvas(buffer, pagesize=letter) - - c.setFont("Helvetica", 12) - - y_position = 750 - - rid = f"Request Id : {requestId}" - agency = f"Agency : {workOrder.agency}" - - c.drawString(100, y_position, rid) - y_position -= 20 - - c.drawString(100, y_position, agency) - y_position -= 20 - - c.drawString(100, y_position - 40, "Items:") - - data = [["Item Name", "Quantity", "Cost (in Rupees)", "Date of Purchase", "Total Amount"]] - for item in itemsList: - data.append([item[0], str(item[1]), "{:.2f}".format(item[2]), item[3], "{:.2f}".format(item[1] * item[2])]) - - total_amount_to_be_paid = sum(item[1] * item[2] for item in itemsList) - - c.drawString(100, y_position - 80, f"Total Amount (in Rupees): {total_amount_to_be_paid:.2f}") - - table = Table(data) - table.setStyle(TableStyle([('BACKGROUND', (0, 0), (-1, 0), colors.grey), - ('TEXTCOLOR', (0, 0), (-1, 0), colors.whitesmoke), - ('ALIGN', (0, 0), (-1, -1), 'CENTER'), - ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'), - ('BOTTOMPADDING', (0, 0), (-1, 0), 12), - ('BACKGROUND', (0, 1), (-1, -1), colors.beige), - ('GRID', (0, 0), (-1, -1), 1, colors.black)])) - - table.wrapOn(c, 400, 600) - table.drawOn(c, 100, y_position - 60) - - c.save() - - buffer.seek(0) - - response = HttpResponse(content_type='application/pdf') - response['Content-Disposition'] = f'attachment; filename="{filename}"' - response.write(buffer.getvalue()) - - # messages.success(request, "Bill generated") - - return response - -@login_required -def handleBillGeneratedRequests(request): - if request.method == 'POST': - requestId = request.POST.get("id", 0) - Requests.objects.filter(id=requestId).update(status="Bill Generated", billGenerated=1) - obj = [] - requestsObject = Requests.objects.filter(issuedWorkOrder=1, billGenerated=0) - for x in requestsObject: - element = [x.id, x.name, x.area, x.description, x.requestCreatedBy, x.workCompleted] - obj.append(element) - return render(request, 'iwdModuleV2/requestsInProgress.html', {'obj' : obj}) - -@login_required -def generatedBillsView(request): - request_object = Requests.objects.filter(billGenerated=1) - obj = [] - for x in request_object: - file_obj= File.objects.get(src_object_id= x.id, src_module = "IWD") - if request_object: - element = [x.id, x.name, x.description, x.area, x.requestCreatedBy, file_obj.id] - obj.append(element) - - return render(request, 'iwdModuleV2/generatedBillsRequestsView.html', {'obj' : obj}) - -@login_required -def handleProcessedBills(request): - if request.method == 'POST': - obj= request.POST - - fileid = obj.get('fileid') - # filez= File.objects.get(id=fileid) - request_id = File.objects.get(id=fileid).src_object_id - - remarks = obj.get('remarks') - attachment = request.FILES.get('attachment') - receiver_user, receiver_desg = request.POST['designation'].split('|') - - - forward_file( - file_id= fileid, - receiver= receiver_user, - receiver_designation=receiver_desg, - file_extra_JSON= { "message": "Request forwarded."}, - remarks= remarks, - file_attachment= attachment, - ) - - Requests.objects.filter(id=request_id).update(billProcessed=1, status="Final Bill Processed") - - - request_instance = Requests.objects.get(pk=request_id) - - formObject = Bills() - formObject.request_id = request_instance - formObject.file = attachment - formObject.save() - - req_object = Requests.objects.filter(billGenerated=1) - - obj = [] - - for result in req_object: - request_object = Requests.objects.filter(id=result.id).first() - file_obj= File.objects.get(src_object_id = result.id, src_module = "IWD") - if request_object: - element = [request_object.id, request_object.name, request_object.area, request_object.description, request_object.requestCreatedBy, file_obj.id] - obj.append(element) - - messages.success(request, "Bill processed") - - receiver_user_obj = User.objects.get(username=receiver_user) - - iwd_notif(request.user, receiver_user_obj, "file_forward") - - return render(request, 'iwdModuleV2/generatedBillsRequestsView.html', {'obj' : obj}) - -@login_required -def auditDocumentView(request): - desg = request.session.get('currentDesignationSelected') - - inbox_files = view_inbox( - username=request.user, - designation=desg, - src_module="IWD" - ) - - obj = [] - - for x in inbox_files: - requestId = x['src_object_id'] - files = Bills.objects.get(request_id=requestId) - file_obj= File.objects.get(src_object_id = requestId, src_module = "IWD") - element = [files.request_id.id, files.file, files.file.url, file_obj.id, file_obj.id, file_obj.id] - obj.append(element) - - return render(request, 'iwdModuleV2/auditDocumentView.html', {'obj' : obj}) - -@login_required -def auditDocument(request): - if request.method == 'POST': - obj= request.POST - - fileid = obj.get('fileid') - request_id = File.objects.get(id=fileid).src_object_id - - remarks = obj.get('remarks') - attachment = request.FILES.get('attachment') - receiver_user, receiver_desg = request.POST['designation'].split('|') - - - forward_file( - file_id= fileid, - receiver= receiver_user, - receiver_designation=receiver_desg, - file_extra_JSON= { "message": "Request forwarded."}, - remarks= remarks, - file_attachment= attachment, - ) - - Requests.objects.filter(id=request_id).update(status="Bill Audited") - - desg = request.session.get('currentDesignationSelected') - - inbox_files = view_inbox( - username=request.user, - designation=desg, - src_module="IWD" - ) - - obj = [] - - for x in inbox_files: - requestId = x['src_object_id'] - files = Bills.objects.get(request_id=requestId) - file_obj= File.objects.get(src_object_id = requestId, src_module = "IWD") - element = [files.request_id.id, files.file, files.file.url, file_obj.id, file_obj.id, file_obj.id] - obj.append(element) - - messages.success(request, "File Audit done") - - receiver_user_obj = User.objects.get(username=receiver_user) - - iwd_notif(request.user, receiver_user_obj, "file_forward") - - return render(request, 'iwdModuleV2/auditDocumentView.html', {'obj' : obj}) - -@login_required -def settleBillsView(request): - desg = request.session.get('currentDesignationSelected') - - inbox_files = view_inbox( - username=request.user, - designation=desg, - src_module="IWD" - ) - - obj = [] - - for x in inbox_files: - requestId = x['src_object_id'] - bills_object = Bills.objects.filter(request_id=requestId).first() - file_obj= File.objects.get(src_object_id = requestId, src_module = "IWD") - request_object = Requests.objects.get(id = requestId) - element = [bills_object.request_id.id, bills_object.file, bills_object.file.url, request_object.billSettled, file_obj.id, file_obj.id] - obj.append(element) - - return render(request, 'iwdModuleV2/settleBillsView.html', {'obj' : obj}) - -@login_required -def handleSettleBillRequests(request): - if request.method == 'POST': - request_id = request.POST.get("id", 0) - - desg = request.session.get('currentDesignationSelected') - - inbox_files = view_inbox( - username=request.user, - designation=desg, - src_module="IWD" - ) - - Requests.objects.filter(id=request_id).update(status="Final Bill Settled", billSettled=1) - - obj = [] - - for x in inbox_files: - requestId = x['src_object_id'] - bills_object = Bills.objects.filter(request_id=requestId).first() - file_obj= File.objects.get(src_object_id = requestId, src_module = "IWD") - request_object = Requests.objects.get(id = requestId) - element = [bills_object.request_id.id, bills_object.file, bills_object.file.url, request_object.billSettled, file_obj.id, file_obj.id] - obj.append(element) - - messages.success(request, "Final Bill settled") - - return render(request, 'iwdModuleV2/settleBillsView.html', {'obj' : obj}) - -@login_required -def viewBudget(request): - - budget_object = Budget.objects.filter() - - obj = [] - - for x in budget_object: - element = [x.id, x.name, x.budgetIssued] - obj.append(element) - - return render(request, 'iwdModuleV2/viewBudget.html', {'obj' : obj}) - -@login_required -def budget(request): - budget_object = Budget.objects.filter() - - obj = [] - - for x in budget_object: - element = [x.id, x.name, x.budgetIssued] - obj.append(element) - - return render(request, 'iwdModuleV2/budget.html', {'obj' : obj}) - -@login_required -def addBudget(request): - if request.method == 'POST': - formObject = Budget() - formObject.name = request.POST['name'] - formObject.budgetIssued = request.POST['budget'] - formObject.save() - return render(request, 'iwdModuleV2/addBudget.html', {}) - -@login_required -def editBudgetView(request): - budget_object = Budget.objects.filter() - - obj = [] - - for x in budget_object: - element = [x.id, x.name, x.budgetIssued] - obj.append(element) - - return render(request, 'iwdModuleV2/editBudget.html', {'obj' : obj}) - -@login_required -def editBudget(request): - if request.method == "POST": - budgetId = request.POST['id'] - budgetName = request.POST['name'] - budgetIssued = request.POST['budget'] - Budget.objects.filter(id=budgetId).update(name=budgetName, budgetIssued=budgetIssued) - items = Budget.objects.filter() - obj = [] - for i in items: - element = [i.id, i.name, i.budgetIssued] - obj.append(element) - return render(request, 'iwdModuleV2/editBudget.html', {'obj' : obj}) - - diff --git a/FusionIIIT/applications/leave/migrations/0001_initial.py b/FusionIIIT/applications/leave/migrations/0001_initial.py index 3ffab190b..d9ce4e044 100644 --- a/FusionIIIT/applications/leave/migrations/0001_initial.py +++ b/FusionIIIT/applications/leave/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 3.1.5 on 2024-07-16 15:44 +# Generated by Django 3.1.5 on 2023-03-15 18:53 from django.conf import settings from django.db import migrations, models @@ -10,8 +10,8 @@ class Migration(migrations.Migration): initial = True dependencies = [ - ('globals', '0001_initial'), migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('globals', '0001_initial'), ] operations = [ diff --git a/FusionIIIT/applications/notifications_extension/api/serializers.py b/FusionIIIT/applications/notifications_extension/api/serializers.py deleted file mode 100644 index 899c50e57..000000000 --- a/FusionIIIT/applications/notifications_extension/api/serializers.py +++ /dev/null @@ -1,6 +0,0 @@ -from rest_framework import serializers -from notifications.models import Notification -class NotificationSerializer(serializers.ModelSerializer): - class Meta: - model = Notification - fields = '__all__' \ No newline at end of file diff --git a/FusionIIIT/applications/notifications_extension/api/urls.py b/FusionIIIT/applications/notifications_extension/api/urls.py deleted file mode 100644 index 62238c007..000000000 --- a/FusionIIIT/applications/notifications_extension/api/urls.py +++ /dev/null @@ -1,53 +0,0 @@ -# urls.py -from django.urls import path -from .views import ( - LeaveModuleNotificationAPIView, - PlacementCellNotificationAPIView, - AcademicsModuleNotificationAPIView, - OfficeModuleNotificationAPIView, - CentralMessNotificationAPIView, - VisitorsHostelNotificationAPIView, - HealthcareCenterNotificationAPIView, - FileTrackingNotificationAPIView, - ScholarshipPortalNotificationAPIView, - ComplaintSystemNotificationAPIView, - OfficeDeanPnDNotificationAPIView, - OfficeDeanSNotificationAPIView, - GymkhanaVotingNotificationAPIView, - GymkhanaSessionNotificationAPIView, - GymkhanaEventNotificationAPIView, - AssistantshipClaimNotificationAPIView, - DepartmentNotificationAPIView, - OfficeDeanRSPCNotificationAPIView, - ResearchProceduresNotificationAPIView, - HostelModuleNotificationAPIView, - MarkAsRead, - Delete, - NotificationsList, -) - -urlpatterns = [ - path('notifications/', NotificationsList.as_view(), name='notifications' ), - path('delete/', Delete.as_view(),name='delete'), - path('mark_as_read/', MarkAsRead.as_view(),name='mark_as_read'), - path('leave_module_notification/', LeaveModuleNotificationAPIView.as_view(), name='leave_module_notification'), - path('placement_cell_notification/', PlacementCellNotificationAPIView.as_view(), name='placement_cell_notification'), - path('academics_module_notification/', AcademicsModuleNotificationAPIView.as_view(), name='academics_module_notification'), - path('office_module_notification/', OfficeModuleNotificationAPIView.as_view(), name='office_module_notification'), - path('central_mess_notification/', CentralMessNotificationAPIView.as_view(), name='central_mess_notification'), - path('visitors_hostel_notification/', VisitorsHostelNotificationAPIView.as_view(), name='visitors_hostel_notification'), - path('healthcare_center_notification/', HealthcareCenterNotificationAPIView.as_view(), name='healthcare_center_notification'), - path('file_tracking_notification/', FileTrackingNotificationAPIView.as_view(), name='file_tracking_notification'), - path('scholarship_portal_notification/', ScholarshipPortalNotificationAPIView.as_view(), name='scholarship_portal_notification'), - path('complaint_system_notification/', ComplaintSystemNotificationAPIView.as_view(), name='complaint_system_notification'), - path('office_dean_PnD_notification/', OfficeDeanPnDNotificationAPIView.as_view(), name='office_dean_PnD_notification'), - path('office_dean_S_notification/', OfficeDeanSNotificationAPIView.as_view(), name='office_dean_S_notification'), - path('gymkhana_voting/', GymkhanaVotingNotificationAPIView.as_view(), name='gymkhana_voting'), - path('gymkhana_session/', GymkhanaSessionNotificationAPIView.as_view(), name='gymkhana_session'), - path('gymkhana_event/', GymkhanaEventNotificationAPIView.as_view(), name='gymkhana_event'), - path('assistantship_claim/', AssistantshipClaimNotificationAPIView.as_view(), name='assistantship_claim'), - path('department_notification/', DepartmentNotificationAPIView.as_view(), name='department_notification'), - path('office_dean_RSPC_notification/', OfficeDeanRSPCNotificationAPIView.as_view(), name='office_dean_RSPC_notification'), - path('research_procedures_notification/', ResearchProceduresNotificationAPIView.as_view(), name='research_procedures_notification'), - path('hostel_notifications/', HostelModuleNotificationAPIView.as_view(), name='hostel_notifications'), -] diff --git a/FusionIIIT/applications/notifications_extension/api/views.py b/FusionIIIT/applications/notifications_extension/api/views.py deleted file mode 100644 index c5017f79f..000000000 --- a/FusionIIIT/applications/notifications_extension/api/views.py +++ /dev/null @@ -1,371 +0,0 @@ -# views.py -from rest_framework.views import APIView -from django.contrib.auth import get_user_model -from rest_framework.response import Response -from notifications.utils import slug2id -from django.shortcuts import get_object_or_404 -from rest_framework.generics import ListAPIView -from notifications.models import Notification -from rest_framework import status -from .serializers import NotificationSerializer -from notification.views import (leave_module_notif, - placement_cell_notif, - academics_module_notif, - office_module_notif, - central_mess_notif, - visitors_hostel_notif, - healthcare_center_notif, - file_tracking_notif, - scholarship_portal_notif, - complaint_system_notif, - office_dean_PnD_notif, - office_module_DeanS_notif, - gymkhana_voting, - gymkhana_session, - gymkhana_event, - AssistantshipClaim_notify, - AssistantshipClaim_faculty_notify, - AssistantshipClaim_acad_notify, - AssistantshipClaim_account_notify, - department_notif, - office_module_DeanRSPC_notif, - research_procedures_notif, - hostel_notifications) - - - -class LeaveModuleNotificationAPIView(APIView): - def post(self, request, *args, **kwargs): - # Extract data from the request, you can customize this based on your needs - sender = request.user - recipient_id = request.data.get('recipient') - type = request.data.get('type') - date = request.data.get('date') - User = get_user_model() - recipient = User.objects.get(pk=recipient_id) - # Trigger the notification function - leave_module_notif(sender, recipient, type, date) - - return Response({'message': 'Notification sent successfully'}, status=status.HTTP_201_CREATED) - -class PlacementCellNotificationAPIView(APIView): - def post(self, request, *args, **kwargs): - # Extract data from the request, you can customize this based on your needs - sender = request.user - recipient_id = request.data.get('recipient') - type = request.data.get('type') - User = get_user_model() - recipient = User.objects.get(pk=recipient_id) - # Trigger the notification function - placement_cell_notif(sender, recipient, type) - - return Response({'message': 'Notification sent successfully'}, status=status.HTTP_201_CREATED) - -class AcademicsModuleNotificationAPIView(APIView): - def post(self, request, *args, **kwargs): - # Extract data from the request, you can customize this based on your needs - sender = request.user - recipient_id = request.data.get('recipient') - type = request.data.get('type') - User = get_user_model() - recipient = User.objects.get(pk=recipient_id) - # Trigger the notification function - academics_module_notif(sender, recipient, type) - - return Response({'message': 'Notification sent successfully'}, status=status.HTTP_201_CREATED) - -class OfficeModuleNotificationAPIView(APIView): - def post(self, request, *args, **kwargs): - # Extract data from the request, you can customize this based on your needs - sender = request.user - recipient_id = request.data.get('recipient') - User = get_user_model() - recipient = User.objects.get(pk=recipient_id) - # Trigger the notification function - office_module_notif(sender, recipient) - - return Response({'message': 'Notification sent successfully'}, status=status.HTTP_201_CREATED) -class CentralMessNotificationAPIView(APIView): - def post(self, request, *args, **kwargs): - # Extract data from the request, you can customize this based on your needs - sender = request.user - recipient_id = request.data.get('recipient') - type = request.data.get('type') - message = request.data.get('message') - - User = get_user_model() - recipient = User.objects.get(pk=recipient_id) - - # Trigger the notification function - central_mess_notif(sender, recipient, type, message) - - return Response({'message': 'Notification sent successfully'}, status=status.HTTP_201_CREATED) - -class VisitorsHostelNotificationAPIView(APIView): - def post(self, request, *args, **kwargs): - # Extract data from the request, you can customize this based on your needs - sender = request.user - recipient_id = request.data.get('recipient') - type = request.data.get('type') - User = get_user_model() - recipient = User.objects.get(pk=recipient_id) - # Trigger the notification function - visitors_hostel_notif(sender, recipient, type) - - return Response({'message': 'Notification sent successfully'}, status=status.HTTP_201_CREATED) - -class HealthcareCenterNotificationAPIView(APIView): - def post(self, request, *args, **kwargs): - # Extract data from the request, you can customize this based on your needs - sender = request.user - recipient_id = request.data.get('recipient') - type = request.data.get('type') - - # Trigger the notification function - healthcare_center_notif(sender, recipient, type) - - return Response({'message': 'Notification sent successfully'}, status=status.HTTP_201_CREATED) - -class FileTrackingNotificationAPIView(APIView): - def post(self, request, *args, **kwargs): - # Extract data from the request, you can customize this based on your needs - sender = request.user - recipient_id = request.data.get('recipient') - title = request.data.get('title') - User = get_user_model() - recipient = User.objects.get(pk=recipient_id) - # Trigger the notification function - file_tracking_notif(sender, recipient, title) - - return Response({'message': 'Notification sent successfully'}, status=status.HTTP_201_CREATED) -class ScholarshipPortalNotificationAPIView(APIView): - def post(self, request, *args, **kwargs): - # Extract data from the request, you can customize this based on your needs - sender = request.user - recipient_id = request.data.get('recipient') - type = request.data.get('type') - User = get_user_model() - recipient = User.objects.get(pk=recipient_id) - # Trigger the notification function - scholarship_portal_notif(sender, recipient, type) - - return Response({'message': 'Notification sent successfully'}, status=status.HTTP_201_CREATED) - -class ComplaintSystemNotificationAPIView(APIView): - def post(self, request, *args, **kwargs): - # Extract data from the request, you can customize this based on your needs - sender = request.user - recipient_id = request.data.get('recipient') - type = request.data.get('type') - complaint_id = request.data.get('complaint_id') - student = request.data.get('student') - message = request.data.get('message') - User = get_user_model() - recipient = User.objects.get(pk=recipient_id) - # Trigger the notification function - complaint_system_notif(sender, recipient, type, complaint_id, student, message) - - return Response({'message': 'Notification sent successfully'}, status=status.HTTP_201_CREATED) - -class OfficeDeanPnDNotificationAPIView(APIView): - def post(self, request, *args, **kwargs): - # Extract data from the request, you can customize this based on your needs - sender = request.user - recipient_id = request.data.get('recipient') - type = request.data.get('type') - User = get_user_model() - recipient = User.objects.get(pk=recipient_id) - # Trigger the notification function - office_dean_PnD_notif(sender, recipient, type) - - return Response({'message': 'Notification sent successfully'}, status=status.HTTP_201_CREATED) - -class OfficeDeanSNotificationAPIView(APIView): - def post(self, request, *args, **kwargs): - # Extract data from the request, you can customize this based on your needs - sender = request.user - recipient_id = request.data.get('recipient') - type = request.data.get('type') - User = get_user_model() - recipient = User.objects.get(pk=recipient_id) - # Trigger the notification function - office_module_DeanS_notif(sender, recipient, type) - - return Response({'message': 'Notification sent successfully'}, status=status.HTTP_201_CREATED) - -class GymkhanaVotingNotificationAPIView(APIView): - def post(self, request, *args, **kwargs): - # Extract data from the request, you can customize this based on your needs - sender = request.user - recipient_id = request.data.get('recipient') - type = request.data.get('type') - title = request.data.get('title') - desc = request.data.get('desc') - User = get_user_model() - recipient = User.objects.get(pk=recipient_id) - # Trigger the notification function - gymkhana_voting(sender, recipient, type, title, desc) - - return Response({'message': 'Notification sent successfully'}, status=status.HTTP_201_CREATED) - -class GymkhanaSessionNotificationAPIView(APIView): - def post(self, request, *args, **kwargs): - # Extract data from the request, you can customize this based on your needs - sender = request.user - recipient_id = request.data.get('recipient') - type = request.data.get('type') - club = request.data.get('club') - desc = request.data.get('desc') - venue = request.data.get('venue') - User = get_user_model() - recipient = User.objects.get(pk=recipient_id) - # Trigger the notification function - gymkhana_session(sender, recipient, type, club, desc, venue) - - return Response({'message': 'Notification sent successfully'}, status=status.HTTP_201_CREATED) - -class GymkhanaEventNotificationAPIView(APIView): - def post(self, request, *args, **kwargs): - # Extract data from the request, you can customize this based on your needs - sender = request.user - recipient_id = request.data.get('recipient') - type = request.data.get('type') - club = request.data.get('club') - event_name = request.data.get('event_name') - desc = request.data.get('desc') - venue = request.data.get('venue') - User = get_user_model() - recipient = User.objects.get(pk=recipient_id) - # Trigger the notification function - gymkhana_event(sender, recipient, type, club, event_name, desc, venue) - - return Response({'message': 'Notification sent successfully'}, status=status.HTTP_201_CREATED) - -class AssistantshipClaimNotificationAPIView(APIView): - def post(self, request, *args, **kwargs): - # Extract data from the request, you can customize this based on your needs - sender = request.user - recipient_id = request.data.get('recipient') - month = request.data.get('month') - year = request.data.get('year') - User = get_user_model() - recipient = User.objects.get(pk=recipient_id) - # Trigger the notification function - AssistantshipClaim_notify(sender, recipient, month, year) - - return Response({'message': 'Notification sent successfully'}, status=status.HTTP_201_CREATED) -class AssistantshipClaimFacultyNotificationAPIView(APIView): - def post(self, request, *args, **kwargs): - # Extract data from the request, you can customize this based on your needs - sender = request.user - recipient_id = request.data.get('recipient') - User = get_user_model() - recipient = User.objects.get(pk=recipient_id) - # Trigger the notification function - AssistantshipClaim_faculty_notify(sender, recipient) - - return Response({'message': 'Notification sent successfully'}, status=status.HTTP_201_CREATED) - -class AssistantshipClaimAcadNotificationAPIView(APIView): - def post(self, request, *args, **kwargs): - # Extract data from the request, you can customize this based on your needs - sender = request.user - recipient_id = request.data.get('recipient') - User = get_user_model() - recipient = User.objects.get(pk=recipient_id) - # Trigger the notification function - AssistantshipClaim_acad_notify(sender, recipient) - - return Response({'message': 'Notification sent successfully'}, status=status.HTTP_201_CREATED) - -class AssistantshipClaimAccountNotificationAPIView(APIView): - def post(self, request, *args, **kwargs): - # Extract data from the request, you can customize this based on your needs - sender = request.user - stu = request.data.get('stu') - recipient_id = request.data.get('recipient') - User = get_user_model() - recipient = User.objects.get(pk=recipient_id) - # Trigger the notification function - AssistantshipClaim_account_notify(sender, stu, recipient) - - return Response({'message': 'Notification sent successfully'}, status=status.HTTP_201_CREATED) - -class DepartmentNotificationAPIView(APIView): - def post(self, request, *args, **kwargs): - # Extract data from the request, you can customize this based on your needs - sender = request.user - recipient_id = request.data.get('recipient') - type = request.data.get('type') - User = get_user_model() - recipient = User.objects.get(pk=recipient_id) - # Trigger the notification function - department_notif(sender, recipient, type) - - return Response({'message': 'Notification sent successfully'}, status=status.HTTP_201_CREATED) -class OfficeDeanRSPCNotificationAPIView(APIView): - def post(self, request, *args, **kwargs): - # Extract data from the request, you can customize this based on your needs - sender = request.user - recipient_id = request.data.get('recipient') - type = request.data.get('type') - User = get_user_model() - recipient = User.objects.get(pk=recipient_id) - # Trigger the notification function - office_module_DeanRSPC_notif(sender, recipient, type) - - return Response({'message': 'Notification sent successfully'}, status=status.HTTP_201_CREATED) - -class ResearchProceduresNotificationAPIView(APIView): - def post(self, request, *args, **kwargs): - # Extract data from the request, you can customize this based on your needs - sender = request.user - recipient_id = request.data.get('recipient') - type = request.data.get('type') - User = get_user_model() - recipient = User.objects.get(pk=recipient_id) - # Trigger the notification function - research_procedures_notif(sender, recipient, type) - - return Response({'message': 'Notification sent successfully'}, status=status.HTTP_201_CREATED) - -class HostelModuleNotificationAPIView(APIView): - def post(self, request, *args, **kwargs): - # Extract data from the request, you can customize this based on your needs - sender = request.user - recipient_id = request.data.get('recipient') - type = request.data.get('type') - User = get_user_model() - recipient = User.objects.get(pk=recipient_id) - # Trigger the notification function - hostel_notifications(sender, recipient, type) - - return Response({'message': 'Notification sent successfully'}, status=status.HTTP_201_CREATED) - -class MarkAsRead(APIView): - - def put(self,request,**args): - notification_id = self.request.query_params.get('id') - notification = get_object_or_404( - Notification, recipient=request.user, id=notification_id) - - notification.mark_as_read() - - return Response({'message': "Successfully marked as read"}, status=status.HTTP_200_OK) - -class Delete(APIView): - - def delete(self,request, **args): - notification_id = self.request.query_params.get('id') - notification = get_object_or_404( - Notification, recipient=request.user, id=notification_id) - - notification.delete() - - return Response({'message': "Notification deleted succesfully"}, status=status.HTTP_200_OK) - -class NotificationsList(ListAPIView): - # queryset = Notification.objects.all(actor_object_id=) - serializer_class = NotificationSerializer - def get_queryset(self): - return Notification.objects.all().filter(recipient_id=self.request.user.id) \ No newline at end of file diff --git a/FusionIIIT/applications/notifications_extension/urls.py b/FusionIIIT/applications/notifications_extension/urls.py index b5690994b..5a6caaa41 100644 --- a/FusionIIIT/applications/notifications_extension/urls.py +++ b/FusionIIIT/applications/notifications_extension/urls.py @@ -4,12 +4,9 @@ from django.conf.urls import include, url from . import views -# from .api import urls - app_name = 'notifications' urlpatterns = [ pattern(r'^mark-as-read-and-redirect/(?P\d+)/$', views.mark_as_read_and_redirect, name='mark_as_read_and_redirect'), - pattern(r'^delete/(?P\d+)/$', views.delete, name='delete'), - url(r'^api/',include('applications.notifications_extension.api.urls')), + pattern(r'^api/placement_cell_notification/$', views.placement_cell_notification, name='placement_cell_notification'), ] + urlpatterns diff --git a/FusionIIIT/applications/notifications_extension/views.py b/FusionIIIT/applications/notifications_extension/views.py index a95e7a798..31efcd81b 100644 --- a/FusionIIIT/applications/notifications_extension/views.py +++ b/FusionIIIT/applications/notifications_extension/views.py @@ -3,16 +3,16 @@ from notifications.utils import id2slug, slug2id from django.shortcuts import get_object_or_404, redirect from notifications.models import Notification -import Fusion.settings as FusionIIIT_settings -def delete(request, slug=None): - notification_id = slug2id(slug) - notification = get_object_or_404( - Notification, recipient=request.user, id=notification_id) - notification.delete() - previous_page = request.META.get('HTTP_REFERER', '/') - # Redirect to the previous page or the home page if the referrer is not available - return HttpResponseRedirect(previous_page) - # return HttpResponseRedirect('dashboard/') +from notifications.signals import notify +from rest_framework import status +from rest_framework.authentication import TokenAuthentication +from rest_framework.decorators import api_view, authentication_classes, permission_classes +from rest_framework.permissions import IsAuthenticated +from rest_framework.response import Response +from django.contrib.auth.models import User + + + def mark_as_read_and_redirect(request, slug=None): notification_id = slug2id(slug) notification = get_object_or_404( @@ -21,14 +21,35 @@ def mark_as_read_and_redirect(request, slug=None): # This conditional statement is True only in # case of complaint_module. - # return redirect('notifications:all') - if(notification.data['module'] == 'Complaint System'): + if(notification.data['module'] == 'Complaint System'): complaint_id=notification.description return HttpResponseRedirect(reverse(notification.data['url'],kwargs={'detailcomp_id1':complaint_id})) - elif(notification.data['module'] == 'Course Management'): - course_code = notification.data['course_code'] - print(course_code) - return HttpResponseRedirect(reverse(notification.data['url'],kwargs={'course_code': course_code})) else: return HttpResponseRedirect(reverse(notification.data['url'])) + + +@api_view(['POST']) +@permission_classes([IsAuthenticated]) +@authentication_classes([TokenAuthentication]) +def placement_cell_notification(request): + send_to = request.data.get('sendTo') + recipient = request.data.get('recipient') + description = request.data.get('description') or request.data.get('type') or 'Placement Cell notification' + + if send_to == 'All': + recipients = User.objects.filter(extrainfo__user_type='student') + else: + target_user = get_object_or_404(User, username=recipient) + recipients = [target_user] + + for target in recipients: + notify.send( + sender=request.user, + recipient=target, + verb=description, + url='placement:placement', + module='Placement Cell', + ) + + return Response({'message': 'Notification sent successfully.'}, status=status.HTTP_200_OK) diff --git a/FusionIIIT/applications/office_module/migrations/0001_initial.py b/FusionIIIT/applications/office_module/migrations/0001_initial.py index 2190b792d..521c8e886 100644 --- a/FusionIIIT/applications/office_module/migrations/0001_initial.py +++ b/FusionIIIT/applications/office_module/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 3.1.5 on 2024-07-16 15:44 +# Generated by Django 3.1.5 on 2023-03-15 18:53 import datetime from django.db import migrations, models @@ -10,10 +10,10 @@ class Migration(migrations.Migration): initial = True dependencies = [ - ('globals', '0001_initial'), ('academic_information', '0001_initial'), - ('leave', '0001_initial'), ('filetracking', '0001_initial'), + ('leave', '0001_initial'), + ('globals', '0001_initial'), ] operations = [ diff --git a/FusionIIIT/applications/online_cms/admin.py b/FusionIIIT/applications/online_cms/admin.py index 8b8cab7ab..ac9eb780a 100644 --- a/FusionIIIT/applications/online_cms/admin.py +++ b/FusionIIIT/applications/online_cms/admin.py @@ -2,13 +2,14 @@ from .models import (Assignment, CourseDocuments, CourseVideo, Forum, ForumReply, Quiz, QuizQuestion, QuizResult, StudentAnswer, - StudentAssignment, Topics,Student_grades) + StudentAssignment, Topics) class QuizResultAdmin(admin.ModelAdmin): model = QuizResult raw_id_fields = ("student_id",) admin.site.register(CourseDocuments) + admin.site.register(CourseVideo) admin.site.register(Quiz) @@ -28,5 +29,3 @@ class QuizResultAdmin(admin.ModelAdmin): admin.site.register(Forum) admin.site.register(ForumReply) - -admin.site.register(Student_grades) diff --git a/FusionIIIT/applications/online_cms/api/serializers.py b/FusionIIIT/applications/online_cms/api/serializers.py deleted file mode 100644 index 93f79f445..000000000 --- a/FusionIIIT/applications/online_cms/api/serializers.py +++ /dev/null @@ -1,89 +0,0 @@ -# from rest_framework.authtoken.models import Token -# from rest_framework import serializers - -# from applications.academic_procedures.models import course_registration - - -from rest_framework import serializers -from applications.academic_information.models import (Student,Student_attendance,Calendar, Timetable) -from applications.programme_curriculum.models import Course as Courses -from applications.programme_curriculum.models import CourseInstructor -from applications.academic_procedures.models import course_registration -from applications.globals.models import ExtraInfo -from applications.online_cms.models import * - -class ExtraInfoSerializer(serializers.ModelSerializer): - class Meta: - model = ExtraInfo - fields = '__all__' # Include all fields - -class StudentSerializer(serializers.ModelSerializer): - class Meta: - model = Student - fields = '__all__' - -class CourseInstructorSerializer(serializers.ModelSerializer): - class Meta: - model = CourseInstructor - fields = '__all__' - -class CoursesSerializer(serializers.ModelSerializer): - class Meta: - model = Courses - fields = '__all__' - - def create(self,validated_data): - return Courses.objects.create(**validated_data) -class RegisteredCoursesSerializer(serializers.ModelSerializer): - - class Meta: - model = course_registration - fields = ('__all__') -class CourseRegistrationSerializer(serializers.ModelSerializer): - class Meta: - model = Courses - fields = ('__all__') - -class ModulesSerializer(serializers.ModelSerializer): - class Meta: - model = Modules - fields = '__all__' - -class CourseDocumentsSerializer(serializers.ModelSerializer): - class Meta: - model = CourseDocuments - fields = '__all__' - -class AssignmentSerializer(serializers.ModelSerializer): - class Meta: - model = Assignment - fields = '__all__' # Include all fields for now (adjust as needed) - -class AttendanceSerializer(serializers.ModelSerializer): - class Meta: - model = Attendance - fields = '__all__' # Include all fields for now (adjust as needed) - -class ForumSerializer(serializers.ModelSerializer): - class Meta: - model = Forum - fields = '__all__' # Include all fields for now (adjust as needed) - -class ForumReplySerializer(serializers.ModelSerializer): - class Meta: - model = ForumReply - fields = '__all__' # Include all fields for now (adjust as needed) - -class GradingSchemeSerializer(serializers.ModelSerializer): - class Meta: - model = GradingScheme - fields = '__all__' # Include all fields for now (adjust as needed) - -class GradingScheme_gradesSerializer(serializers.ModelSerializer): - class Meta: - model = GradingScheme_grades - fields = '__all__' # Include all fields for now (adjust as needed) -class TopicsSerializer(serializers.ModelSerializer): - class Meta: - model = Topics, - fields = '__all__' diff --git a/FusionIIIT/applications/online_cms/api/urls.py b/FusionIIIT/applications/online_cms/api/urls.py deleted file mode 100644 index 87919db4d..000000000 --- a/FusionIIIT/applications/online_cms/api/urls.py +++ /dev/null @@ -1,15 +0,0 @@ -# from django.conf.urls import url - -# from . import views - -# urlpatterns = [ -# url(r'^courses', views.viewcourses_serialized, name="registered_courses") -# ] -from django.conf.urls import url -# from .views import course -from . import views -urlpatterns = [ - # url(r'^courses', CourseListView.as_view(), name='courses'), - url(r'^(?P[A-Za-z0-9]+)/(?P[\d.]+)/$', views.course, name='course'), - url(r'^courses', views.courseview, name='courseview'), -] diff --git a/FusionIIIT/applications/online_cms/api/views.py b/FusionIIIT/applications/online_cms/api/views.py deleted file mode 100644 index 23df6aaf3..000000000 --- a/FusionIIIT/applications/online_cms/api/views.py +++ /dev/null @@ -1,540 +0,0 @@ -from __future__ import unicode_literals -from django.views.decorators.csrf import csrf_protect -from django.core import serializers -import collections -import json -import os -import random -import subprocess -import datetime -import requests -from django.conf import settings -from django.contrib.auth.decorators import login_required -from django.core.files.storage import FileSystemStorage -from django.http import HttpResponse, HttpResponseBadRequest, HttpResponseRedirect -from django.shortcuts import redirect, render -from django.utils import timezone -from django.core.serializers import serialize -from django.http import JsonResponse -from decimal import Decimal -from applications.academic_information.models import (Student,Student_attendance,Calendar, Timetable) -from applications.programme_curriculum.models import Course as Courses -from applications.programme_curriculum.models import CourseInstructor -from applications.academic_procedures.models import course_registration -from applications.globals.models import ExtraInfo -# from applications.globals.models import * - -# from .forms import * -# from .helpers import create_thumbnail, semester -# from .models import * -# from .helpers import create_thumbnail, semester -# from notification.views import course_management_notif -# def viewcourses_serialized(request): -# user = request.user -# extrainfo = ExtraInfo.objects.select_related().get(user=user) - -# # If the user is a student -# if extrainfo.user_type == 'student': -# student = Student.objects.select_related('id').get(id=extrainfo) -# register = course_registration.objects.select_related().filter(student_id=student) - -# # Serialize registered courses -# registered_courses_data = serializers.serialize('json', register) - -# return JsonResponse({ -# 'user_type': 'student', -# 'registered_courses': registered_courses_data, -# }) - -# # If the user is faculty -# elif extrainfo.user_type == 'faculty': -# instructor = CourseInstructor.objects.select_related('curriculum_id').filter(instructor_id=extrainfo) -# curriculum_list = [Courses.objects.select_related().get(pk=x.course_id) for x in instructor] - -# # Serialize curriculum list -# curriculum_data = serializers.serialize('json', curriculum_list) - -# return JsonResponse({ -# 'user_type': 'faculty', -# 'curriculum_list': curriculum_data, -# }) - -# # If the user is an admin -# elif extrainfo.id == 'id_admin': -# # if request.session.get('currentDesignationSelected') != 'acadadmin': -# # return HttpResponseRedirect('/dashboard/') - -# calendar = Calendar.objects.all() -# timetable = Timetable.objects.all() - -# # Serialize calendar and timetable data -# calendar_data = serializers.serialize('json', calendar) -# timetable_data = serializers.serialize('json', timetable) - -# return JsonResponse({ -# 'user_type': 'admin', -# 'academic_calendar': calendar_data, -# 'timetable': timetable_data, -# }) - -# return JsonResponse({ -# 'error': 'Unknown user type' -# }) - -from rest_framework.views import APIView -from rest_framework.response import Response -from rest_framework.decorators import api_view -from rest_framework import status # Import for custom status codes -from .serializers import * -# class CourseListView(APIView): -# def get(self, request): -# user = request.user - -# try: -# extrainfo = ExtraInfo.objects.select_related().get(user=user) -# except ExtraInfo.DoesNotExist: -# return Response({'message': 'ExtraInfo object not found for this user'}, status=status.HTTP_404_NOT_FOUND) - -# if extrainfo.user_type == 'student': -# try: -# student = Student.objects.select_related('id').get(id=extrainfo) -# register = course_registration.objects.select_related().filter(student_id=student) -# except (Student.DoesNotExist, course_registration.DoesNotExist): -# return Response({'message': 'No courses found for this student'}, status=status.HTTP_404_NOT_FOUND) - -# courses = collections.OrderedDict() -# for reg in register: -# # instructor = CourseInstructor.objects.select_related().get(course_id=reg.course_id).first() -# instructors = CourseInstructor.objects.select_related().filter(course_id=reg.course_id) -# instructor = instructors.first() # Get the first instructor - -# courses[reg] = instructor - -# courses_serializer = CoursesSerializer(courses, many=True) # Assuming CourseRegistrationSerializer exists -# return Response(courses_serializer.data) -# elif extrainfo.user_type == 'faculty': -# # ... similar logic for faculty courses ... - -# return Response(faculty_courses_serializer.data) # Assuming faculty_courses_serializer exists -# elif extrainfo.user_type == 'id_admin': -# # ... similar logic for admin courses ... - -# return Response(admin_courses_serializer.data) # Assuming admin_courses_serializer exists -# else: -# return Response({'message': 'Invalid user type'}, status=status.HTTP_400_BAD_REQUEST) - -@api_view(['GET']) -def courseview(request): - user = request.user - extrainfo = ExtraInfo.objects.select_related().get(user=user) - if extrainfo.user_type == 'student': - student = Student.objects.select_related('id').get(id=extrainfo) - # register = course_registration.objects.select_related().filter(student_id=student) - # courses_info = Courses.objects.select_related().filter(id=register) - - # course_serializer = CourseRegistrationSerializer(course_infos, many=True) - register = course_registration.objects.select_related().filter(student_id=student) - - courses_info = [] - for reg in register: - course = Courses.objects.select_related().get(pk=reg.course_id.id) # Optimized retrieval using primary key - courses_info.append(course) - - course_serializer = CourseRegistrationSerializer(courses_info, many=True) - print(course_serializer) - data ={ - "courses":course_serializer.data - } - return Response(data,status=status.HTTP_200_OK) - - elif extrainfo.user_type == 'faculty': - instructor = CourseInstructor.objects.select_related('course_id').filter(instructor_id=extrainfo) - courses_info=[] - for ins in instructor: - course = Courses.objects.select_related().get(pk=ins.course_id.id) - courses_info.append(course) - - course_serializer = CourseRegistrationSerializer(courses_info, many=True) - data = { - "courses":course_serializer.data - } - return Response(data, status=status.HTTP_200_OK) - else: - return Response({"error":"error occured"}) - -@api_view(['GET']) -def course(request, course_code, version): - ''' - desc: Home page for each courses for Student/Faculty - ''' - user = request.user - extrainfo = ExtraInfo.objects.select_related().get(user=user) - notifs = request.user.notifications.all() - if extrainfo.user_type == 'student': #if the user is student .. funtionality used by him/her - # if request.session.get('currentDesignationSelected') != 'student': - # return HttpResponseRedirect('/dashboard/') - student = Student.objects.select_related('id').get(id=extrainfo) - #info about courses he is registered in - course = Courses.objects.select_related().get(code=course_code, version = version) - #instructor of the course - instructor = CourseInstructor.objects.select_related().get(course_id = course, batch_id = student.batch_id) - - # Serialize course and instructor - course_serializer = CoursesSerializer(course) - instructor_serializer = CourseInstructorSerializer(instructor) - #course material uploaded by the instructor - # videos = CourseVideo.objects.filter(course_id=course) - videos = [] - if request.method == 'POST': - search_url = "https://www.googleapis.com/youtube/v3/search" - video_url = "https://www.googleapis.com/youtube/v3/videos" - search_params = { - 'part': 'snippet', - 'q': request.POST['search'], - 'key': settings.YOUTUBE_DATA_API_KEY, - 'type': 'video', - 'channelId': 'channel_id' - } - videos_ids = [] - r = requests.get(search_url, params=search_params) - # print(r) - results = r.json()['items'] - for result in results: - videos_ids.append(result['id']['videoId']) - - video_params = { - 'key': settings.YOUTUBE_DATA_API_KEY, - 'part': 'snippet,contentDetails', - 'id': ','.join(videos_ids), - 'maxResults': 9 - } - - p = requests.get(video_url, params=video_params) - results1 = p.json()['items'] - - for result in results1: - video_data = { - 'id': result['id'], - # 'url': f'https://www.youtube.com/watch?v={result["id"]}', - 'title': result['snippet']['title'], - # 'duration': int(parse_duration(result['contentDetails']['duration']).total_seconds() // 60), - # 'thumbnails': result['snippet']['thumbnails']['high']['url'] - } - - videos.append(video_data) - else: - x = 0 - # channel_url = "https://www.googleapis.com/youtube/v3/channels" - # playlist_url = "https://www.googleapis.com/youtube/v3/playlistItems" - # videos_url = "https://www.googleapis.com/youtube/v3/videos" - - # videos_list = [] - # channel_params = { - # 'part': 'contentDetails', - # 'id': 'channel_id', - # 'key': settings.YOUTUBE_DATA_API_KEY, - # } - # r = requests.get(channel_url, params=channel_params) - # results = r.json()['items'][0]['contentDetails']['relatedPlaylists']['uploads'] - - # playlist_params = { - # 'key': settings.YOUTUBE_DATA_API_KEY, - # 'part': 'snippet', - # 'playlistId': results, - # 'maxResults': 5, - # } - # p = requests.get(playlist_url, params=playlist_params) - # results1 = p.json()['items'] - - # for result in results1: - # # print(results) - # videos_list.append(result['snippet']['resourceId']['videoId']) - - # videos_params = { - # 'key': settings.YOUTUBE_DATA_API_KEY, - # 'part': 'snippet', - # 'id': ','.join(videos_list) - # } - - # v = requests.get(videos_url, params=videos_params) - # results2 = v.json()['items'] - # videos = [] - # for res in results2: - # video_data = { - # 'id': res['id'], - # 'title': res['snippet']['title'], - # } - - # videos.append(video_data) - # print(videos) - - modules = Modules.objects.select_related().filter(course_id=course) - slides = CourseDocuments.objects.select_related().filter(course_id=course) - - modules_with_slides = collections.OrderedDict() - for m in modules: - sl = [] - for slide in slides: - if slide.module_id.id == m.id: - sl.append(slide) - if len(sl) == 0: - modules_with_slides[m] = 0 - else: - modules_with_slides[m] = sl - # Serialize modules - modules_serializer = ModulesSerializer(modules, many=True) - slides_serializer = CourseDocumentsSerializer(slides, many=True) - - quiz = Quiz.objects.select_related().filter(course_id=course) - assignment = Assignment.objects.select_related().filter(course_id=course) - submitable_assignments = [] - for assi in assignment: - if assi.submit_date.date() >= datetime.date.today(): - submitable_assignments.append(assi) - - student_assignment = [] - for assi in assignment: - sa = StudentAssignment.objects.select_related().filter(assignment_id=assi, student_id=student) - student_assignment.append(sa) - - assignment_serializer = AssignmentSerializer(assignment, many=True) - - ''' - marks to store the marks of quizes of student - marks_pk to store the quizs taken by student - quizs=>quizs that are not over - ''' - # marks = [] - # quizs = [] - # marks_pk = [] - # #quizzes details - # for q in quiz: - # qs = QuizResult.objects.select_related().filter(quiz_id=q, student_id=student) - # qs_pk = qs.values_list('quiz_id', flat=True) - # if q.end_time > timezone.now(): - # quizs.append(q) - # if qs: - # marks.append(qs[0]) - # marks_pk.append(qs_pk[0]) - - present_attendance = {} - total_attendance=None - a = Attendance.objects.select_related().filter(student_id=student , instructor_id = instructor) - total_attendance = len(a) - count = 0 - for row in a: - if(row.present): - count+=1 - present_attendance[row.date] = 1 - else: - present_attendance[row.date] = 0 - attendance_percent = 0 - if(total_attendance): - attendance_percent = count/total_attendance*100 - attendance_percent = round(attendance_percent,2) - - attendance_file = {} - try: - attendance_file = AttendanceFiles.objects.select_related().filter(course_id=course) - except AttendanceFiles.DoesNotExist: - attendance_file = {} - - attendance_serializer = AttendanceSerializer(a, many=True) - - lec = 0 - comments = Forum.objects.select_related().filter(course_id=course).order_by('comment_time') - answers = collections.OrderedDict() - for comment in comments: - fr = ForumReply.objects.select_related().filter(forum_reply=comment) - fr1 = ForumReply.objects.select_related().filter(forum_ques=comment) - if not fr: - answers[comment] = fr1 - # serialise forum and reply - forum_serializer = ForumSerializer(comments,many=True) - # forum_reply_serializer = ForumReplySerializer(fr,many=True) - - data = { - 'course': course_serializer.data, - 'instructor': instructor_serializer.data, - 'modules': modules_serializer.data, - 'slides': slides_serializer.data, - 'assignments': assignment_serializer.data, - 'student_assignment': student_assignment, # Include if needed - 'attendance': attendance_serializer.data, #try sending "total attendance" - 'present_attendance': present_attendance, - 'attendance_percent': attendance_percent, - 'attendance_file': attendance_file, - # 'comments': nested_comments, # Use nested comments for better structure - # 'forum_reply': forum_reply_serializer.data, # Include if not nested # Include if needed - # 'notifications': notifs.values('id', 'title', 'message', 'is_seen'), - } - print(course_serializer.data) - return Response(data,status=status.HTTP_200_OK) - - else: - # if request.session.get('currentDesignationSelected') != "faculty" and request.session.get('currentDesignationSelected') != "Associate Professor" and request.session.get('currentDesignationSelected') != "Professor" and request.session.get('currentDesignationSelected') != "Assistant Professor": - # return HttpResponseRedirect('/dashboard/') - instructor = CourseInstructor.objects.select_related('course_id').filter(instructor_id=extrainfo) - for ins in instructor: - if ins.course_id.code == course_code and ins.course_id.version == Decimal(version): - registered_students = course_registration.objects.select_related('student_id').filter(course_id = ins.course_id) - students = {} - test_marks = {} - for x in registered_students: - students[x.student_id.id.id] = (x.student_id.id.user.first_name + " " + x.student_id.id.user.last_name) - # stored_marks = StoreMarks.objects.filter(mid = x.r_id) - # for x in stored_marks: - # test_marks[x.id] = (x.mid.r_id,x.exam_type,x.marks) - #marks_id.append(x.curr_id) - #print(stored_marks) - #for x in stored_marks: - # print(x) - students_info = [] - for stu in registered_students: - s = Student.objects.select_related().get(pk=stu.student_id.id) - students_info.append(s) - - - # registered student serializer - registered_students_serialiser = StudentSerializer(students_info,many=True) - course = ins.course_id - result_topics = Topics.objects.select_related().filter(course_id = course) - - if (len(list(result_topics))!=0): - topics = result_topics - else: - topics = None - # serializer - topics_serialiser = TopicsSerializer(topics,many=True) - present_attendance = {} - total_attendance=None - for x in registered_students: - a = Attendance.objects.select_related().filter(student_id=x.student_id , instructor_id = ins) - total_attendance = len(a) - count =0 - for row in a: - if(row.present): - count += 1 - - attendance_percent = 0 - if(total_attendance): - attendance_percent = count/total_attendance*100 - attendance_percent = round(attendance_percent,2) - present_attendance[x.student_id.id.id] = { - 'count': count, - 'attendance_percent': attendance_percent - } - - lec = 1 - videos = [] - - # videos = CourseVideo.objects.filter(course_id=course) - # channel_url = "https://www.googleapis.com/youtube/v3/channels" - # playlist_url = "https://www.googleapis.com/youtube/v3/playlistItems" - # videos_url = "https://www.googleapis.com/youtube/v3/videos" - - # videos_list = [] - # channel_params = { - # 'part': 'contentDetails', - # # 'forUsername': 'TechGuyWeb', - # 'id': 'UCdGQeihs84hyCssI2KuAPmA', - # 'key': settings.YOUTUBE_DATA_API_KEY, - # } - # r = requests.get(channel_url, params=channel_params) - # results = r.json()['items'][0]['contentDetails']['relatedPlaylists']['uploads'] - - # playlist_params = { - # 'key': settings.YOUTUBE_DATA_API_KEY, - # 'part': 'snippet', - # 'playlistId': results, - # 'maxResults': 5, - # } - # p = requests.get(playlist_url, params=playlist_params) - # results1 = p.json()['items'] - - # for result in results1: - # videos_list.append(result['snippet']['resourceId']['videoId']) - - # videos_params = { - # 'key': settings.YOUTUBE_DATA_API_KEY, - # 'part': 'snippet', - # 'id': ','.join(videos_list) - # } - - # v = requests.get(videos_url, params=videos_params) - # results2 = v.json()['items'] - # videos = [] - # for res in results2: - # video_data = { - # 'id': res['id'], - # 'title': res['snippet']['title'], - # } - - # videos.append(video_data) - modules = Modules.objects.select_related().filter(course_id=course) - slides = CourseDocuments.objects.select_related().filter(course_id=course) - modules_with_slides = collections.OrderedDict() - for m in modules: - sl = [] - for slide in slides: - if slide.module_id.id == m.id: - sl.append(slide) - if len(sl) == 0: - modules_with_slides[m] = 0 - else: - modules_with_slides[m] = sl - # quiz = Quiz.objects.select_related().filter(course_id=course) - # marks = [] - # quizs = [] - modules_serializer = ModulesSerializer(modules, many=True) - slides_serializer = CourseDocumentsSerializer(slides, many=True) - - assignment = Assignment.objects.select_related().filter(course_id=course) - student_assignment = [] - for assi in assignment: - sa = StudentAssignment.objects.select_related().filter(assignment_id=assi) - student_assignment.append(sa) - # for q in quiz: - # qs = QuizResult.objects.select_related().filter(quiz_id=q) - # if q.end_time > timezone.now(): - # quizs.append(q) - # if len(qs) != 0: - # marks.append(qs) - - assignment_serializer = AssignmentSerializer(assignment, many=True) - - comments = Forum.objects.select_related().filter(course_id=course).order_by('comment_time') - answers = collections.OrderedDict() - for comment in comments: - fr = ForumReply.objects.select_related().filter(forum_reply=comment) - fr1 = ForumReply.objects.select_related().filter(forum_ques=comment) - if not fr: - answers[comment] = fr1 - # qb = QuestionBank.objects.select_related().filter(instructor_id=extrainfo, course_id=course) - forum_serializer = ForumSerializer(comments,many=True) - - gradingscheme = GradingScheme.objects.select_related().filter(course_id=course) - try: - gradingscheme_grades = GradingScheme_grades.objects.select_related().get(course_id=course) - except GradingScheme_grades.DoesNotExist: - gradingscheme_grades = {} - - try: - student_grades = Student_grades.objects.select_related().filter(course_id=course) - except Student_grades.DoesNotExist: - student_grades = {} - - gradingscheme_serialiser = GradingSchemeSerializer(gradingscheme,many=True) - data = { - "students":registered_students_serialiser.data, - "topics":topics_serialiser.data, - "present_att":present_attendance, - "total_att":total_attendance, - "modules":modules_serializer.data, - "slides":slides_serializer.data, - "assignment":assignment_serializer.data, - "forum":forum_serializer.data, - "grading_scheme":gradingscheme_serialiser.data, - } - return Response(data,status=status.HTTP_200_OK) - \ No newline at end of file diff --git a/FusionIIIT/applications/online_cms/forms.py b/FusionIIIT/applications/online_cms/forms.py index c03a8e160..e656a838f 100644 --- a/FusionIIIT/applications/online_cms/forms.py +++ b/FusionIIIT/applications/online_cms/forms.py @@ -1,13 +1,10 @@ -# from django import forms import datetime from datetime import time, timedelta #import information from the models from django import forms # from .models import StoreMarks -from applications.academic_information.models import (Student_attendance,Timetable) +from applications.academic_information.models import Student_attendance - -from .models import * #the types of exam whose marks can be stored from edit marks in assessment, related to StoreMarks table in models EXAM_TYPES= [ ('quiz1', 'Quiz 1'), @@ -115,27 +112,4 @@ def clean(self): examtype = self.cleaned_data.get("exam_type") enteredmarks = self.cleaned_data.get("entered_marks") - return self.cleaned_data - -class GradingSchemeForm(forms.Form): - quiz_weightage = forms.DecimalField(label='Quiz', max_digits=10, decimal_places=2) - midsem_weightage = forms.DecimalField(label='Mid Semester', max_digits=10, decimal_places=2) - assignment_weightage = forms.DecimalField(label='Assignment', max_digits=10, decimal_places=2) - endsem_weightage = forms.DecimalField(label='End Semester', max_digits=10, decimal_places=2) - project_weightage = forms.DecimalField(label='Project', max_digits=10, decimal_places=2) - - -class AcademicTimetableForm(forms.ModelForm): - """ - the form to add a new academic timetable to the database. - It consist of year, programme and the timetable file upload - - @attrubutes: - model - the model used is the Timetable class - fields - the fields shown in the form for the user to fill up is year, programme and timetable file upload - widgets - defining the id, required and placeholder of the filed in the form - - """ - class Meta: - model = Timetable - fields = ('programme', 'batch', 'branch', 'time_table') + return self.cleaned_data \ No newline at end of file diff --git a/FusionIIIT/applications/online_cms/helpers.py b/FusionIIIT/applications/online_cms/helpers.py index 22bd1659a..8fb9f530e 100644 --- a/FusionIIIT/applications/online_cms/helpers.py +++ b/FusionIIIT/applications/online_cms/helpers.py @@ -5,25 +5,12 @@ def semester(roll): - if not roll.isnumeric(): - s='' - s+='2' - s+='0' - s+=roll[0] - s+=roll[1] - month = datetime.now().month - sem = 0 - if month >= 8 and month <= 12: - sem = 1 - semester = (datetime.now().year-int(s))*2+sem - return semester - else: - month = datetime.now().month - sem = 0 - if month >= 8 and month <= 12: - sem = 1 - semester = (datetime.now().year-int(roll))*2+sem - return semester + month = datetime.now().month + sem = 0 + if month >= 8 and month <= 12: + sem = 1 + semester = (datetime.now().year-int(roll))*2+sem + return semester #storing media files like images, videos and assignments def create_thumbnail(course_code,course, row, name, ext, attach_str, thumb_time, thumb_size): @@ -32,4 +19,4 @@ def create_thumbnail(course_code,course, row, name, ext, attach_str, thumb_time, filename = filename + name.replace(' ', '-') + '-' + attach_str + '.png' process = 'ffmpeg -y -i ' + filepath + ' -vframes ' + str(1) + ' -an -s ' process = process + thumb_size + ' -ss ' + str(thumb_time) + ' ' + filename - subprocess.call(process, shell=True) \ No newline at end of file + subprocess.call(process, shell=True) diff --git a/FusionIIIT/applications/online_cms/migrations/0001_initial.py b/FusionIIIT/applications/online_cms/migrations/0001_initial.py index 23293a797..4e3377ab7 100644 --- a/FusionIIIT/applications/online_cms/migrations/0001_initial.py +++ b/FusionIIIT/applications/online_cms/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 3.1.5 on 2024-07-16 15:44 +# Generated by Django 3.1.5 on 2023-03-15 18:53 from django.db import migrations, models import django.db.models.deletion @@ -9,9 +9,8 @@ class Migration(migrations.Migration): initial = True dependencies = [ - ('programme_curriculum', '0001_initial'), - ('globals', '0001_initial'), ('academic_information', '0001_initial'), + ('globals', '0001_initial'), ] operations = [ @@ -23,7 +22,7 @@ class Migration(migrations.Migration): ('submit_date', models.DateTimeField()), ('assignment_name', models.CharField(max_length=100)), ('assignment_url', models.CharField(max_length=100, null=True)), - ('course_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='programme_curriculum.course')), + ('course_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='academic_information.course')), ], ), migrations.CreateModel( @@ -33,7 +32,7 @@ class Migration(migrations.Migration): ('comment_time', models.DateTimeField(auto_now=True)), ('comment', models.TextField(max_length=2000)), ('commenter_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='globals.extrainfo')), - ('course_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='programme_curriculum.course')), + ('course_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='academic_information.course')), ], ), migrations.CreateModel( @@ -45,7 +44,7 @@ class Migration(migrations.Migration): ('number_of_question', models.IntegerField(default=0)), ('description', models.TextField(max_length=1000)), ('total_score', models.IntegerField(default=0)), - ('course_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='programme_curriculum.course')), + ('course_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='academic_information.course')), ], ), migrations.CreateModel( @@ -78,7 +77,7 @@ class Migration(migrations.Migration): ('description', models.TextField(max_length=1000)), ('rules', models.TextField(max_length=2000)), ('total_score', models.IntegerField(default=0)), - ('course_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='programme_curriculum.course')), + ('course_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='academic_information.course')), ], ), migrations.CreateModel( @@ -94,7 +93,7 @@ class Migration(migrations.Migration): fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('topic_name', models.TextField(max_length=200)), - ('course_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='programme_curriculum.course')), + ('course_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='academic_information.course')), ], ), migrations.CreateModel( @@ -120,19 +119,6 @@ class Migration(migrations.Migration): ('student_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='academic_information.student')), ], ), - migrations.CreateModel( - name='Student_grades', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('semester', models.IntegerField(default=1)), - ('year', models.IntegerField(default=2016)), - ('roll_no', models.TextField(max_length=2000)), - ('total_marks', models.DecimalField(decimal_places=2, default=0, max_digits=10)), - ('grade', models.TextField(max_length=2000)), - ('batch', models.IntegerField(default=2021)), - ('course_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='programme_curriculum.course')), - ], - ), migrations.CreateModel( name='QuizResult', fields=[ @@ -148,7 +134,7 @@ class Migration(migrations.Migration): fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('name', models.CharField(max_length=100)), - ('course_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='programme_curriculum.course')), + ('course_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='academic_information.course')), ('instructor_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='globals.extrainfo')), ], ), @@ -177,50 +163,6 @@ class Migration(migrations.Migration): ('prac_quiz_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='online_cms.practice')), ], ), - migrations.CreateModel( - name='Modules', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('module_name', models.CharField(max_length=50)), - ('course_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='programme_curriculum.course')), - ], - ), - migrations.CreateModel( - name='GradingScheme_grades', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('O_Lower', models.DecimalField(decimal_places=2, default=0, max_digits=10)), - ('O_Upper', models.DecimalField(decimal_places=2, default=0, max_digits=10)), - ('A_plus_Lower', models.DecimalField(decimal_places=2, default=0, max_digits=10)), - ('A_plus_Upper', models.DecimalField(decimal_places=2, default=0, max_digits=10)), - ('A_Lower', models.DecimalField(decimal_places=2, default=0, max_digits=10)), - ('A_Upper', models.DecimalField(decimal_places=2, default=0, max_digits=10)), - ('B_plus_Lower', models.DecimalField(decimal_places=2, default=0, max_digits=10)), - ('B_plus_Upper', models.DecimalField(decimal_places=2, default=0, max_digits=10)), - ('B_Lower', models.DecimalField(decimal_places=2, default=0, max_digits=10)), - ('B_Upper', models.DecimalField(decimal_places=2, default=0, max_digits=10)), - ('C_plus_Lower', models.DecimalField(decimal_places=2, default=0, max_digits=10)), - ('C_plus_Upper', models.DecimalField(decimal_places=2, default=0, max_digits=10)), - ('C_Lower', models.DecimalField(decimal_places=2, default=0, max_digits=10)), - ('C_Upper', models.DecimalField(decimal_places=2, default=0, max_digits=10)), - ('D_plus_Lower', models.DecimalField(decimal_places=2, default=0, max_digits=10)), - ('D_plus_Upper', models.DecimalField(decimal_places=2, default=0, max_digits=10)), - ('D_Lower', models.DecimalField(decimal_places=2, default=0, max_digits=10)), - ('D_Upper', models.DecimalField(decimal_places=2, default=0, max_digits=10)), - ('F_Lower', models.DecimalField(decimal_places=2, default=0, max_digits=10)), - ('F_Upper', models.DecimalField(decimal_places=2, default=0, max_digits=10)), - ('course_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='programme_curriculum.course')), - ], - ), - migrations.CreateModel( - name='GradingScheme', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('type_of_evaluation', models.TextField(default=None, max_length=255)), - ('weightage', models.DecimalField(decimal_places=2, default=0, max_digits=10)), - ('course_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='programme_curriculum.course')), - ], - ), migrations.CreateModel( name='ForumReply', fields=[ @@ -237,7 +179,7 @@ class Migration(migrations.Migration): ('description', models.CharField(max_length=100)), ('video_name', models.CharField(max_length=40)), ('video_url', models.CharField(max_length=100, null=True)), - ('course_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='programme_curriculum.course')), + ('course_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='academic_information.course')), ], ), migrations.CreateModel( @@ -248,28 +190,7 @@ class Migration(migrations.Migration): ('description', models.CharField(max_length=100)), ('document_name', models.CharField(max_length=40)), ('document_url', models.CharField(max_length=100, null=True)), - ('course_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='programme_curriculum.course')), - ('module_id', models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='online_cms.modules')), - ], - ), - migrations.CreateModel( - name='AttendanceFiles', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('upload_time', models.DateTimeField(auto_now=True)), - ('file_name', models.CharField(max_length=40)), - ('file_url', models.CharField(max_length=100, null=True)), - ('course_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='programme_curriculum.course')), - ], - ), - migrations.CreateModel( - name='Attendance', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('date', models.DateField()), - ('present', models.BooleanField(default=False)), - ('instructor_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='programme_curriculum.courseinstructor')), - ('student_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='academic_information.student')), + ('course_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='academic_information.course')), ], ), ] diff --git a/FusionIIIT/applications/online_cms/migrations/0002_auto_20241023_1002.py b/FusionIIIT/applications/online_cms/migrations/0002_auto_20241023_1002.py deleted file mode 100644 index 659201064..000000000 --- a/FusionIIIT/applications/online_cms/migrations/0002_auto_20241023_1002.py +++ /dev/null @@ -1,59 +0,0 @@ -# Generated by Django 3.1.5 on 2024-10-23 10:02 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('academic_information', '0001_initial'), - ('online_cms', '0001_initial'), - ] - - operations = [ - migrations.RemoveField( - model_name='student_grades', - name='total_marks', - ), - migrations.AddField( - model_name='attendance', - name='no_of_attendance', - field=models.IntegerField(default=1), - ), - migrations.AddField( - model_name='student_grades', - name='reSubmit', - field=models.BooleanField(default=True), - ), - migrations.AddField( - model_name='student_grades', - name='remarks', - field=models.CharField(max_length=500, null=True), - ), - migrations.AddField( - model_name='student_grades', - name='verified', - field=models.BooleanField(default=False), - ), - migrations.AlterField( - model_name='attendance', - name='present', - field=models.IntegerField(default=0), - ), - migrations.AlterField( - model_name='gradingscheme', - name='type_of_evaluation', - field=models.CharField(max_length=100), - ), - migrations.CreateModel( - name='StudentEvaluation', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('marks', models.DecimalField(decimal_places=2, max_digits=10, null=True)), - ('total_marks', models.DecimalField(decimal_places=2, default=0, max_digits=10)), - ('evaluation_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='online_cms.gradingscheme')), - ('student_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='academic_information.student')), - ], - ), - ] diff --git a/FusionIIIT/applications/online_cms/migrations/0003_auto_20250409_2146.py b/FusionIIIT/applications/online_cms/migrations/0003_auto_20250409_2146.py deleted file mode 100644 index 1ac9f5cb0..000000000 --- a/FusionIIIT/applications/online_cms/migrations/0003_auto_20250409_2146.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 3.1.5 on 2025-04-09 21:46 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('online_cms', '0002_auto_20241023_1002'), - ] - - operations = [ - migrations.AddField( - model_name='student_grades', - name='academic_year', - field=models.CharField(max_length=9, null=True), - ), - migrations.AddField( - model_name='student_grades', - name='semester_type', - field=models.CharField(choices=[('Odd Semester', 'Odd Semester'), ('Even Semester', 'Even Semester'), ('Summer Semester', 'Summer Semester')], max_length=20, null=True), - ), - ] diff --git a/FusionIIIT/applications/online_cms/migrations/0004_populate_academic_year_and_type.py b/FusionIIIT/applications/online_cms/migrations/0004_populate_academic_year_and_type.py deleted file mode 100644 index e0131b162..000000000 --- a/FusionIIIT/applications/online_cms/migrations/0004_populate_academic_year_and_type.py +++ /dev/null @@ -1,38 +0,0 @@ -# Generated by Django 3.1.5 on 2025-04-09 21:47 - -from django.db import migrations - -def populate_academic_year_and_semester_type(apps, schema_editor): - Student_grades = apps.get_model('online_cms', 'Student_grades') - - for obj in Student_grades.objects.filter(): - sem_no = obj.semester - working_year = obj.year - - # session logic - if sem_no % 2 == 1: - academic_year = f"{working_year}-{str(working_year + 1)[-2:]}" - else: - academic_year = f"{working_year - 1}-{str(working_year)[-2:]}" - - # semester_type logic - if sem_no == 0: - semester_type = "Summer Semester" - elif sem_no % 2 == 0: - semester_type = "Even Semester" - else: - semester_type = "Odd Semester" - - obj.academic_year = academic_year - obj.semester_type = semester_type - obj.save(update_fields=['academic_year', 'semester_type']) - -class Migration(migrations.Migration): - - dependencies = [ - ('online_cms', '0003_auto_20250409_2146'), - ] - - operations = [ - migrations.RunPython(populate_academic_year_and_semester_type), - ] diff --git a/FusionIIIT/applications/online_cms/models.py b/FusionIIIT/applications/online_cms/models.py index aa3d5c6ec..a34e5c3ee 100644 --- a/FusionIIIT/applications/online_cms/models.py +++ b/FusionIIIT/applications/online_cms/models.py @@ -1,21 +1,12 @@ from django.db import models #import models used from academic procedure and academic information modules and globals -from applications.academic_information.models import Student, Curriculum -from applications.programme_curriculum.models import Course as Courses, CourseInstructor +from applications.academic_information.models import Course, Student, Curriculum +from applications.academic_procedures.models import Register from applications.globals.models import ExtraInfo -#the modules for containing course content -class Modules(models.Model): - module_name = models.CharField(max_length=50) - course_id = models.ForeignKey(Courses, on_delete=models.CASCADE) - - def __str__(self): - return self.module_name - #the documents in the course (slides , ppt) added by the faculty and can be downloaded by the students class CourseDocuments(models.Model): - course_id = models.ForeignKey(Courses, on_delete=models.CASCADE) - module_id = models.ForeignKey(Modules, on_delete=models.CASCADE, default = 1) + course_id = models.ForeignKey(Course, on_delete=models.CASCADE) upload_time = models.DateTimeField(auto_now=True) description = models.CharField(max_length=100) document_name = models.CharField(max_length=40) @@ -23,20 +14,10 @@ class CourseDocuments(models.Model): def __str__(self): return '{} - {}'.format(self.course_id, self.document_name) - -#the attendance files added by the faculty and can be downloaded by the students -class AttendanceFiles(models.Model): - course_id = models.ForeignKey(Courses, on_delete=models.CASCADE) - upload_time = models.DateTimeField(auto_now=True) - file_name = models.CharField(max_length=40) - file_url = models.CharField(max_length=100, null=True) - - def __str__(self): - return '{} - {}'.format(self.course_id, self.file_name) #videos added by the faculty and can be downloaded by students class CourseVideo(models.Model): - course_id = models.ForeignKey(Courses, on_delete=models.CASCADE) + course_id = models.ForeignKey(Course, on_delete=models.CASCADE) upload_time = models.DateTimeField(auto_now=True) description = models.CharField(max_length=100) video_name = models.CharField(max_length=40) @@ -47,7 +28,7 @@ def __str__(self): #For storing the questions topic wise class Topics(models.Model): - course_id = models.ForeignKey(Courses, on_delete=models.CASCADE) + course_id = models.ForeignKey(Course, on_delete=models.CASCADE) topic_name = models.TextField(max_length=200) def __str__(self): @@ -56,7 +37,7 @@ def __str__(self): #details of a question bank of which course it is class QuestionBank(models.Model): instructor_id = models.ForeignKey(ExtraInfo, on_delete=models.CASCADE) - course_id = models.ForeignKey(Courses, on_delete=models.CASCADE) + course_id = models.ForeignKey(Course, on_delete=models.CASCADE) name = models.CharField(max_length=100) #name of question bank def __str__(self): @@ -84,7 +65,7 @@ class Question(models.Model): #details of quiz are stored class Quiz(models.Model): - course_id = models.ForeignKey(Courses, on_delete=models.CASCADE) + course_id = models.ForeignKey(Course, on_delete=models.CASCADE) quiz_name = models.CharField(max_length=20) end_time = models.DateTimeField() start_time = models.DateTimeField() @@ -114,7 +95,7 @@ def __str__(self): #the details of practice quiz (objective assignment)---- under development class Practice(models.Model): - course_id = models.ForeignKey(Courses, on_delete=models.CASCADE) + course_id = models.ForeignKey(Course, on_delete=models.CASCADE) prac_quiz_name = models.CharField(max_length=20) negative_marks = models.FloatField(default=0) number_of_question = models.IntegerField(default=0) @@ -159,7 +140,7 @@ def __str__(self): #details of the assignment uploaded by the faculty class Assignment(models.Model): - course_id = models.ForeignKey(Courses, on_delete=models.CASCADE) + course_id = models.ForeignKey(Course, on_delete=models.CASCADE) upload_time = models.DateTimeField(auto_now=True) submit_date = models.DateTimeField() assignment_name = models.CharField(max_length=100) @@ -199,7 +180,7 @@ def __str__(self): #to store the comment of student and lecturer class Forum(models.Model): - course_id = models.ForeignKey(Courses, on_delete=models.CASCADE) + course_id = models.ForeignKey(Course, on_delete=models.CASCADE) commenter_id = models.ForeignKey(ExtraInfo, on_delete=models.CASCADE) comment_time = models.DateTimeField(auto_now=True) comment = models.TextField(max_length=2000) @@ -221,92 +202,4 @@ class ForumReply(models.Model): def __str__(self): return '{} - {} - {}'.format(self.pk, self.forum_ques, self.forum_reply) - -class GradingScheme(models.Model): - course_id = models.ForeignKey(Courses, on_delete=models.CASCADE) - # quiz = models.DecimalField(max_digits=10, decimal_places=2,default=0) - # assignment = models.DecimalField(max_digits=10, decimal_places=2,default=0) - # midsem = models.DecimalField(max_digits=10, decimal_places=2,default=0) - # endsem = models.DecimalField(max_digits=10, decimal_places=2,default=0) - # projects = models.DecimalField(max_digits=10, decimal_places=2,default=0) - type_of_evaluation = models.CharField(max_length=100) - weightage = models.DecimalField(max_digits=10, decimal_places=2,default=0) - def __str__(self): - return '{} - {}'.format( - self.pk, self.course_id) - -class GradingScheme_grades(models.Model): - course_id = models.ForeignKey(Courses, on_delete=models.CASCADE) - O_Lower = models.DecimalField(max_digits=10, decimal_places=2, default=0) - O_Upper = models.DecimalField(max_digits=10, decimal_places=2, default=0) - A_plus_Lower = models.DecimalField(max_digits=10, decimal_places=2, default=0) - A_plus_Upper = models.DecimalField(max_digits=10, decimal_places=2, default=0) - A_Lower = models.DecimalField(max_digits=10, decimal_places=2, default=0) - A_Upper = models.DecimalField(max_digits=10, decimal_places=2, default=0) - B_plus_Lower = models.DecimalField(max_digits=10, decimal_places=2, default=0) - B_plus_Upper = models.DecimalField(max_digits=10, decimal_places=2, default=0) - B_Lower = models.DecimalField(max_digits=10, decimal_places=2, default=0) - B_Upper = models.DecimalField(max_digits=10, decimal_places=2, default=0) - C_plus_Lower = models.DecimalField(max_digits=10, decimal_places=2, default=0) - C_plus_Upper = models.DecimalField(max_digits=10, decimal_places=2, default=0) - C_Lower = models.DecimalField(max_digits=10, decimal_places=2, default=0) - C_Upper = models.DecimalField(max_digits=10, decimal_places=2, default=0) - D_plus_Lower = models.DecimalField(max_digits=10, decimal_places=2, default=0) - D_plus_Upper = models.DecimalField(max_digits=10, decimal_places=2, default=0) - D_Lower = models.DecimalField(max_digits=10, decimal_places=2, default=0) - D_Upper = models.DecimalField(max_digits=10, decimal_places=2, default=0) - F_Lower = models.DecimalField(max_digits=10, decimal_places=2, default=0) - F_Upper = models.DecimalField(max_digits=10, decimal_places=2, default=0) - - def __str__(self): - return '{} - {}'.format(self.pk, self.course_id) - -class Student_grades(models.Model): - course_id = models.ForeignKey(Courses, on_delete=models.CASCADE) - semester = models.IntegerField(default=1) - year = models.IntegerField(default=2016) - roll_no = models.TextField(max_length=2000) - grade = models.TextField(max_length=2000) - batch = models.IntegerField(default=2021) - remarks = models.CharField(max_length=500,null=True) - verified = models.BooleanField(default=False) - reSubmit = models.BooleanField(default=True) - academic_year = models.CharField(max_length=9, null=True) - - SEMESTER_TYPE_CHOICES = [ - ("Odd Semester", "Odd Semester"), - ("Even Semester", "Even Semester"), - ("Summer Semester", "Summer Semester"), - ] - semester_type = models.CharField( - max_length=20, - choices=SEMESTER_TYPE_CHOICES, - null=True - ) - def __str__(self): - return '{} - {}'.format(self.pk, self.course_id) - -class Attendance(models.Model): - student_id = models.ForeignKey(Student,on_delete=models.CASCADE) -# course_id = models.ForeignKey(Course) -# attend = models.CharField(max_length=6, choices=Constants.ATTEND_CHOICES) - instructor_id = models.ForeignKey(CourseInstructor, on_delete=models.CASCADE) -# curriculum_id = models.ForeignKey(Curriculum, on_delete=models.CASCADE) - date = models.DateField() - present = models.IntegerField(default=0) - no_of_attendance = models.IntegerField(default=1) - - def __str__(self): - return self.course_id - -class StudentEvaluation(models.Model): - student_id = models.ForeignKey(Student, on_delete=models.CASCADE) - evaluation_id = models.ForeignKey(GradingScheme, on_delete=models.CASCADE) - marks = models.DecimalField(max_digits=10, decimal_places=2,null=True) - total_marks = models.DecimalField(max_digits=10, decimal_places=2, default=0) - - def __str__(self): - return '{} - {} - {} - {}'.format( - self.pk, self.student_id, - self.evaluation_id, self.marks) \ No newline at end of file diff --git a/FusionIIIT/applications/online_cms/templates/Sample_Attendance_Sheet.xlsx b/FusionIIIT/applications/online_cms/templates/Sample_Attendance_Sheet.xlsx deleted file mode 100644 index 2f2e9616b..000000000 Binary files a/FusionIIIT/applications/online_cms/templates/Sample_Attendance_Sheet.xlsx and /dev/null differ diff --git a/FusionIIIT/applications/online_cms/urls.py b/FusionIIIT/applications/online_cms/urls.py index c81142539..c24c69cc2 100644 --- a/FusionIIIT/applications/online_cms/urls.py +++ b/FusionIIIT/applications/online_cms/urls.py @@ -1,87 +1,72 @@ -from django.conf import settings -from django.conf.urls.static import static -from django.conf.urls import url, include +from django.conf.urls import url + from . import views -app_name = 'online_cms' +app_name = 'online_cms' urlpatterns = [ - + url(r'^$', views.viewcourses, name='viewcourses'), - url(r'^api/', include('applications.online_cms.api.urls'), name='api'), - url(r'^(?P[A-Za-z0-9]+)/(?P[\d.]+)/$', views.course, name='course'), - url(r'^(?P[A-Za-z0-9]+)/(?P[\d.]+)/edit_marks$', views.edit_marks, name='edit_marks'), - url(r'^(?P[A-Za-z0-9]+)/(?P[\d.]+)/get_exam_data$', views.get_exam_data, name='get_exam_data'), - url(r'^(?P[A-Za-z0-9]+)/(?P[\d.]+)/forum$', views.forum, + url(r'^(?P[A-z]+[0-9]+[A-z]?)/$', views.course, name='course'), + # url(r'^(?P[A-z]+[0-9]+[A-z]?)/edit_marks$', views.edit_marks, name='edit_marks'), + url(r'^(?P[A-z]+[0-9]+[A-z]?)/get_exam_data$', views.get_exam_data, name='get_exam_data'), + url(r'^(?P[A-z]+[0-9]+[A-z]?)/forum$', views.forum, name='forum'), - url(r'^(?P[A-Za-z0-9]+)/(?P[\d.]+)/ajax_reply$', views.ajax_reply, + url(r'^(?P[A-z]+[0-9]+[A-z]?)/ajax_reply$', views.ajax_reply, name='ajax_reply'), - url(r'^(?P[A-Za-z0-9]+)/(?P[\d.]+)/ajax_new$', views.ajax_new, + url(r'^(?P[A-z]+[0-9]+[A-z]?)/ajax_new$', views.ajax_new, name='ajax_new'), - url(r'^(?P[A-Za-z0-9]+)/(?P[\d.]+)/ajax_remove$', views.ajax_remove, + url(r'^(?P[A-z]+[0-9]+[A-z]?)/ajax_remove$', views.ajax_remove, name='ajax_remove'), - url(r'^(?P[A-Za-z0-9]+)/(?P[\d.]+)/upload_assignment$', views.upload_assignment, + url(r'^(?P[A-z]+[0-9]+[A-z]?)/upload_assignment$', views.upload_assignment, name='upload_assignment'), - url(r'^(?P[A-Za-z0-9]+)/(?P[\d.]+)/add_modules$', views.add_modules, - name='add_modules'), - url(r'^(?P[A-Za-z0-9]+)/(?P[\d.]+)/add_documents$', views.add_document, + url(r'^(?P[A-z]+[0-9]+[A-z]?)/add_documents$', views.add_document, name='add_document'), - url(r'^(?P[A-Za-z0-9]+)/(?P[\d.]+)/add_assignment$', + url(r'^(?P[A-z]+[0-9]+[A-z]?)/add_assignment$', views.add_assignment, name='add_assignment'), - # url(r'^(?P[A-Za-z0-9]+)/add_video$', views.add_videos, + # url(r'^(?P[A-z]+[0-9]+[A-z]?)/add_video$', views.add_videos, # name='add_videos'), - url(r'^(?P[A-Za-z0-9]+)/(?P[\d.]+)/delete/$', views.delete, + url(r'^(?P[A-z]+[0-9]+[A-z]?)/delete/$', views.delete, name='delete'), - url(r'^(?P[A-Za-z0-9]+)/(?P[\d.]+)/ajax_assess$', views.ajax_assess, + url(r'^(?P[A-z]+[0-9]+[A-z]?)/ajax_assess$', views.ajax_assess, name='ajax_assess'), - url(r'^(?P[A-Za-z0-9]+)/(?P[\d.]+)/ajax_feedback$', views.ajax_feedback, + url(r'^(?P[A-z]+[0-9]+[A-z]?)/ajax_feedback$', views.ajax_feedback, name='ajax_feedback'), url(r'^quiz/(?P[0-9]+)/$', views.quiz, name='quiz'), - url(r'^(?P[A-Za-z0-9]+)/create_quiz/$', views.create_quiz, name='create_quiz'), - url(r'^(?P[A-Za-z0-9]+)/edit_quiz/(?P[0-9]+)/$', + url(r'^(?P[A-z]+[0-9]+[A-z]?)/create_quiz/$', views.create_quiz, name='create_quiz'), + url(r'^(?P[A-z]+[0-9]+[A-z]?)/edit_quiz/(?P[0-9]+)/$', views.edit_quiz, name='edit_quiz'), - url(r'^(?P[A-Za-z0-9]+)/edit_quiz/(?P[0-9]+)/(?P[0-9]+)$', + url(r'^(?P[A-z]+[0-9]+[A-z]?)/edit_quiz/(?P[0-9]+)/(?P[0-9]+)$', views.edit_quiz_topic, name='edit_quiz_topic'), - url(r'^(?P[A-Za-z0-9]+)/(?P[0-9]+)/add_question_topic$', + url(r'^(?P[A-z]+[0-9]+[A-z]?)/(?P[0-9]+)/add_question_topic$', views.add_question_topicwise, name='add_question_topicwise'), url(r'^(?P[0-9]+?)/(?P[0-9]+)/add_questions_to_quiz$', views.add_questions_to_quiz, name='add_questions_to_quiz'), url( - r'^(?P[A-Za-z0-9]+)/(?P[0-9]+)/(?P[0-9]+)/remove_quiz_question$', + r'^(?P[A-z]+[0-9]+[A-z]?)/(?P[0-9]+)/(?P[0-9]+)/remove_quiz_question$', views.remove_quiz_question, name='remove_quiz_question'), - url(r'^(?P[A-Za-z0-9]+)/preview_quiz/(?P[0-9]+)/$', views.preview_quiz, + url(r'^(?P[A-z]+[0-9]+[A-z]?)/preview_quiz/(?P[0-9]+)/$', views.preview_quiz, name='preview_quiz'), - url(r'^(?P[A-Za-z0-9]+)/edit_quiz_details/(?P[0-9]+)/$', + url(r'^(?P[A-z]+[0-9]+[A-z]?)/edit_quiz_details/(?P[0-9]+)/$', views.edit_quiz_details, name='edit_quiz_details'), url(r'^(?P[0-9]+)/ajax$', views.ajax_q, name='ajax_q'), url(r'^(?P[0-9]+)/submit$', views.submit, name='submit'), - url(r'^(?P[A-Za-z0-9]+)/remove_quiz$', views.remove_quiz, + url(r'^(?P[A-z]+[0-9]+[A-z]?)/remove_quiz$', views.remove_quiz, name='remove_quiz'), - url(r'^(?P[A-Za-z0-9]+)/remove_bank$', views.remove_bank, + url(r'^(?P[A-z]+[0-9]+[A-z]?)/remove_bank$', views.remove_bank, name='remove_bank'), - url(r'^(?P[A-Za-z0-9]+)/remove_topic$', views.remove_topic, + url(r'^(?P[A-z]+[0-9]+[A-z]?)/remove_topic$', views.remove_topic, name='remove_topic'), - url(r'^(?P[A-Za-z0-9]+)/create_bank$', views.create_bank, + url(r'^(?P[A-z]+[0-9]+[A-z]?)/create_bank$', views.create_bank, name='create_bank'), - url(r'^(?P[A-Za-z0-9]+)/create_topic$', views.create_topic, + url(r'^(?P[A-z]+[0-9]+[A-z]?)/create_topic$', views.create_topic, name='create_topic'), - url(r'^(?P[A-Za-z0-9]+)/(?P[0-9]+)/(?P[0-9]+)$', + url(r'^(?P[A-z]+[0-9]+[A-z]?)/(?P[0-9]+)/(?P[0-9]+)$', views.edit_qb_topics, name='edit_qb_topics'), url(r'^(?P[0-9]+?)/(?P[0-9]+)/(?P[0-9]+)/add_question$', views.add_question, name='add_question'), - url(r'^(?P[A-Za-z0-9]+)/(?P[0-9]+)/(?P[0-9]+)/remove_question$', + url(r'^(?P[A-z]+[0-9]+[A-z]?)/(?P[0-9]+)/(?P[0-9]+)/remove_question$', views.remove_question, name='remove_question'), - url(r'^(?P[A-Za-z0-9]+)/edit_bank/(?P[0-9]+)$', + url(r'^(?P[A-z]+[0-9]+[A-z]?)/edit_bank/(?P[0-9]+)$', views.edit_bank, name='edit_bank'), - url(r'^(?P[A-Za-z0-9]+)/(?P[\d.]+)/attendance$', views.submit_attendance, - name='submit_attendance'), - url(r'^(?P[A-Za-z0-9]+)/(?P[\d.]+)/add_attendance$', views.add_attendance, - name='add_attendance'), - url(r'^(?P[A-Za-z0-9]+)/(?P[\d.]+)/create_grading_scheme$', - views.create_grading_scheme, name='Create_grading_scheme'), - url(r'^admin/add_academic_calendar', views.add_academic_calendar, name='Add Calendar'), - url(r'^admin/update_calendar', views.update_calendar, name='Add Calendar'), - url(r'^admin/add_timetable', views.add_timetable, name="add_timetable"), - url(r'^admin/delete_timetable', views.delete_timetable, name='delete_timetable'), - url(r'^(?P[A-z0-9]+)/(?P[\d.]+)/submit_marks$',views.submit_marks, name='submit_marks'), - ] - + url(r'^(?P[A-z]+[0-9]+[A-z]?)/attendance$', views.submit_attendance, + name='submit_attendance'),] diff --git a/FusionIIIT/applications/online_cms/views.py b/FusionIIIT/applications/online_cms/views.py index 27acb4498..cc74626f3 100644 --- a/FusionIIIT/applications/online_cms/views.py +++ b/FusionIIIT/applications/online_cms/views.py @@ -11,16 +11,13 @@ from django.conf import settings from django.contrib.auth.decorators import login_required from django.core.files.storage import FileSystemStorage -from django.http import HttpResponse, HttpResponseBadRequest, HttpResponseRedirect -from django.http import JsonResponse +from django.http import HttpResponse, HttpResponseBadRequest from django.shortcuts import redirect, render from django.utils import timezone -from django.core.serializers import serialize -from decimal import Decimal -from applications.academic_information.models import (Student,Student_attendance,Calendar, Timetable) -from applications.programme_curriculum.models import Course as Courses -from applications.programme_curriculum.models import CourseInstructor -from applications.academic_procedures.models import course_registration + +from applications.academic_information.models import (Course, Curriculum_Instructor,Curriculum, + Student,Student_attendance) +from applications.academic_procedures.models import Register from applications.globals.models import ExtraInfo from applications.globals.models import * @@ -28,7 +25,6 @@ # from .helpers import create_thumbnail, semester from .models import * from .helpers import create_thumbnail, semester -from notification.views import course_management_notif @login_required @@ -41,63 +37,47 @@ def viewcourses(request): extrainfo = ExtraInfo.objects.select_related().get(user=user) #get the type of user if extrainfo.user_type == 'student': #if student is using student = Student.objects.select_related('id').get(id=extrainfo) - roll = student.id.id[:2] #get the roll no. of the student - register = course_registration.objects.select_related().filter(student_id=student) - #info of registered student + roll = student.id.id[:4] #get the roll no. of the student + register = Register.objects.select_related().filter(student_id=student, semester=semester(roll)) #info of registered student courses = collections.OrderedDict() #courses in which student is registerd for reg in register: #info of the courses - instructor = CourseInstructor.objects.select_related().get(course_id = reg.course_id) + instructor = Curriculum_Instructor.objects.select_related().get(course_id=reg.course_id) courses[reg] = instructor - return render(request, 'coursemanagement/coursemanagement1.html', {'courses': courses, 'extrainfo': extrainfo}) - elif extrainfo.user_type == 'faculty': #if the user is lecturer - instructor = CourseInstructor.objects.select_related('curriculum_id').filter(instructor_id=extrainfo) #get info of the instructor + else: #if the user is lecturer + instructor = Curriculum_Instructor.objects.select_related('curriculum_id').filter(instructor_id=extrainfo) #get info of the instructor curriculum_list = [] for x in instructor: - c = Courses.objects.select_related().get(pk = x.course_id) + c = Curriculum.objects.select_related().get(curriculum_id = x.curriculum_id.curriculum_id) curriculum_list.append(c) + return render(request, 'coursemanagement/coursemanagement1.html', {'instructor': instructor, 'extrainfo': extrainfo, 'curriculum_list': curriculum_list}) - - elif request.session.get('currentDesignationSelected') == 'acadadmin': - acadTtForm = AcademicTimetableForm() - calendar = Calendar.objects.all() - timetable = Timetable.objects.all() - return render(request, 'coursemanagement/academicinfo.html', - {'acadTtForm': acadTtForm, - 'academic_calendar':calendar, - 'timetable':timetable}) - else: - return HttpResponseRedirect('/dashboard/') - @login_required -def course(request, course_code, version): +def course(request, course_code): ''' desc: Home page for each courses for Student/Faculty ''' user = request.user extrainfo = ExtraInfo.objects.select_related().get(user=user) - notifs = request.user.notifications.all() if extrainfo.user_type == 'student': #if the user is student .. funtionality used by him/her - if request.session.get('currentDesignationSelected') != 'student': - return HttpResponseRedirect('/dashboard/') student = Student.objects.select_related('id').get(id=extrainfo) + roll = student.id.id[:4] + #info about courses he is registered in - course = Courses.objects.select_related().get(code=course_code, version = version) + curriculum = Curriculum.objects.select_related('course_id').get(course_code=course_code) + course = curriculum.course_id #instructor of the course - try: - instructor = CourseInstructor.objects.select_related().get(course_id = course, batch_id = student.batch_id) - except: - return HttpResponseRedirect('/academic-procedures/stu/') + instructor = Curriculum_Instructor.objects.select_related().get(curriculum_id=curriculum) #course material uploaded by the instructor # videos = CourseVideo.objects.filter(course_id=course) videos = [] @@ -139,72 +119,52 @@ def course(request, course_code, version): videos.append(video_data) else: - x = 0 - # channel_url = "https://www.googleapis.com/youtube/v3/channels" - # playlist_url = "https://www.googleapis.com/youtube/v3/playlistItems" - # videos_url = "https://www.googleapis.com/youtube/v3/videos" - - # videos_list = [] - # channel_params = { - # 'part': 'contentDetails', - # 'id': 'channel_id', - # 'key': settings.YOUTUBE_DATA_API_KEY, - # } - # r = requests.get(channel_url, params=channel_params) - # results = r.json()['items'][0]['contentDetails']['relatedPlaylists']['uploads'] - - # playlist_params = { - # 'key': settings.YOUTUBE_DATA_API_KEY, - # 'part': 'snippet', - # 'playlistId': results, - # 'maxResults': 5, - # } - # p = requests.get(playlist_url, params=playlist_params) - # results1 = p.json()['items'] - - # for result in results1: - # # print(results) - # videos_list.append(result['snippet']['resourceId']['videoId']) - - # videos_params = { - # 'key': settings.YOUTUBE_DATA_API_KEY, - # 'part': 'snippet', - # 'id': ','.join(videos_list) - # } - - # v = requests.get(videos_url, params=videos_params) - # results2 = v.json()['items'] - # videos = [] - # for res in results2: - # video_data = { - # 'id': res['id'], - # 'title': res['snippet']['title'], - # } - - # videos.append(video_data) - # print(videos) + channel_url = "https://www.googleapis.com/youtube/v3/channels" + playlist_url = "https://www.googleapis.com/youtube/v3/playlistItems" + videos_url = "https://www.googleapis.com/youtube/v3/videos" + + videos_list = [] + channel_params = { + 'part': 'contentDetails', + 'id': 'channel_id', + 'key': settings.YOUTUBE_DATA_API_KEY, + } + r = requests.get(channel_url, params=channel_params) + results = r.json()['items'][0]['contentDetails']['relatedPlaylists']['uploads'] - modules = Modules.objects.select_related().filter(course_id=course) - slides = CourseDocuments.objects.select_related().filter(course_id=course) + playlist_params = { + 'key': settings.YOUTUBE_DATA_API_KEY, + 'part': 'snippet', + 'playlistId': results, + 'maxResults': 5, + } + p = requests.get(playlist_url, params=playlist_params) + results1 = p.json()['items'] + + for result in results1: + # print(results) + videos_list.append(result['snippet']['resourceId']['videoId']) - modules_with_slides = collections.OrderedDict() - for m in modules: - sl = [] - for slide in slides: - if slide.module_id.id == m.id: - sl.append(slide) - if len(sl) == 0: - modules_with_slides[m] = 0 - else: - modules_with_slides[m] = sl + videos_params = { + 'key': settings.YOUTUBE_DATA_API_KEY, + 'part': 'snippet', + 'id': ','.join(videos_list) + } + v = requests.get(videos_url, params=videos_params) + results2 = v.json()['items'] + videos = [] + for res in results2: + video_data = { + 'id': res['id'], + 'title': res['snippet']['title'], + } + + videos.append(video_data) + # print(videos) + slides = CourseDocuments.objects.select_related().filter(course_id=course) quiz = Quiz.objects.select_related().filter(course_id=course) assignment = Assignment.objects.select_related().filter(course_id=course) - submitable_assignments = [] - for assi in assignment: - if assi.submit_date.date() >= datetime.date.today(): - submitable_assignments.append(assi) - student_assignment = [] for assi in assignment: sa = StudentAssignment.objects.select_related().filter(assignment_id=assi, student_id=student) @@ -214,52 +174,18 @@ def course(request, course_code, version): marks_pk to store the quizs taken by student quizs=>quizs that are not over ''' - # marks = [] - # quizs = [] - # marks_pk = [] - # #quizzes details - # for q in quiz: - # qs = QuizResult.objects.select_related().filter(quiz_id=q, student_id=student) - # qs_pk = qs.values_list('quiz_id', flat=True) - # if q.end_time > timezone.now(): - # quizs.append(q) - # if qs: - # marks.append(qs[0]) - # marks_pk.append(qs_pk[0]) - - present_attendance = {} - total_attendance=0 - a = Attendance.objects.select_related().filter(student_id=student , instructor_id = instructor) - count = 0 - for row in a: - total_attendance+=row.no_of_attendance - count+=row.present - present_attendance[row.date] = row.present - - attendance_percent = 0 - if(total_attendance): - attendance_percent = count/total_attendance*100 - attendance_percent = round(attendance_percent,2) - - attendance_file = {} - try: - attendance_file = AttendanceFiles.objects.select_related().filter(course_id=course) - except AttendanceFiles.DoesNotExist: - attendance_file = {} - - quiz_marks = 0 - quiz = {} - try: - quiz = GradingScheme.objects.select_related().filter(course_id = course) - for q in quiz: - if q.type_of_evaluation == "Quiz": - m = StudentEvaluation.objects.select_related().filter(evaluation_id = q, student_id = student) - for mr in m: - quiz_marks+=mr.marks - except GradingScheme.DoesNotExist: - quiz_marks = 0 - - + marks = [] + quizs = [] + marks_pk = [] + #quizzes details + for q in quiz: + qs = QuizResult.objects.select_related().filter(quiz_id=q, student_id=student) + qs_pk = qs.values_list('quiz_id', flat=True) + if q.end_time > timezone.now(): + quizs.append(q) + if qs: + marks.append(qs[0]) + marks_pk.append(qs_pk[0]) lec = 0 comments = Forum.objects.select_related().filter(course_id=course).order_by('comment_time') answers = collections.OrderedDict() @@ -270,39 +196,29 @@ def course(request, course_code, version): answers[comment] = fr1 return render(request, 'coursemanagement/viewcourse.html', {'course': course, - 'modules': modules_with_slides, + 'quizs': marks, + 'quizs_pk': marks_pk, + 'fut_quiz': quizs, + 'videos': videos, 'instructor': instructor, 'slides': slides, 'extrainfo': extrainfo, 'answers': answers, 'assignment': assignment, - 'submitable_assignment':submitable_assignments, 'student_assignment': student_assignment, - 'total_attendance' : total_attendance, - 'present_attendance':present_attendance, - 'attendance_percent': attendance_percent, 'Lecturer': lec, - 'quiz_marks': quiz_marks, - 'attendance_file':attendance_file, - 'notifications': notifs}) + 'curriculum': curriculum}) else: - if request.session.get('currentDesignationSelected') != "faculty" and request.session.get('currentDesignationSelected') != "Associate Professor" and request.session.get('currentDesignationSelected') != "Professor" and request.session.get('currentDesignationSelected') != "Assistant Professor": - return HttpResponseRedirect('/dashboard/') - instructor = {} - try: - instructor = CourseInstructor.objects.select_related('course_id').filter(instructor_id=extrainfo) - except: - return HttpResponseRedirect('/academic-procedures/fac/') - + instructor = Curriculum_Instructor.objects.select_related('curriculum_id').filter(instructor_id=extrainfo) for ins in instructor: - if ins.course_id.code == course_code and ins.course_id.version == Decimal(version): - registered_students = course_registration.objects.select_related('student_id').filter(course_id = ins.course_id) + if ins.curriculum_id.course_code == course_code: + registered_students = Register.objects.select_related('student_id').filter(curr_id = ins.curriculum_id.curriculum_id) students = {} test_marks = {} for x in registered_students: - students[x.student_id.id.id] = (x.student_id.id.user.first_name + " " + x.student_id.id.user.last_name) - # stored_marks = StudentEvaluation.objects.filter(mid = x.r_id) + students[x.student_id.id.id] = (x.student_id.id.user.first_name + " " + x.student_id.id.user.last_name, x.id) + # stored_marks = StoreMarks.objects.filter(mid = x.r_id) # for x in stored_marks: # test_marks[x.id] = (x.mid.r_id,x.exam_type,x.marks) #marks_id.append(x.curr_id) @@ -310,103 +226,84 @@ def course(request, course_code, version): #for x in stored_marks: # print(x) - course = ins.course_id + curriculum = ins.curriculum_id + course = ins.curriculum_id.course_id result_topics = Topics.objects.select_related().filter(course_id = course) if (len(list(result_topics))!=0): topics = result_topics else: topics = None present_attendance = {} - total_attendance=None - for x in registered_students: - a = Attendance.objects.select_related().filter(student_id=x.student_id , instructor_id = ins) - total_attendance = 0 - count =0 - for row in a: - total_attendance+=row.no_of_attendance - count+=row.present - - attendance_percent = 0 - if(total_attendance): - attendance_percent = count/total_attendance*100 - attendance_percent = round(attendance_percent,2) - present_attendance[x.student_id.id.id] = { - 'count': count, - 'attendance_percent': attendance_percent - } + total_attendance=None + for x in registered_students: + a = Student_attendance.objects.select_related().filter(student_id=x.student_id , instructor_id = ins) + total_attendance = len(a) + count =0 + for row in a: + if(row.present): + count += 1 + present_attendance[x.student_id.id.id] = count lec = 1 - videos = [] - + # videos = CourseVideo.objects.filter(course_id=course) - # channel_url = "https://www.googleapis.com/youtube/v3/channels" - # playlist_url = "https://www.googleapis.com/youtube/v3/playlistItems" - # videos_url = "https://www.googleapis.com/youtube/v3/videos" - - # videos_list = [] - # channel_params = { - # 'part': 'contentDetails', - # # 'forUsername': 'TechGuyWeb', - # 'id': 'UCdGQeihs84hyCssI2KuAPmA', - # 'key': settings.YOUTUBE_DATA_API_KEY, - # } - # r = requests.get(channel_url, params=channel_params) - # results = r.json()['items'][0]['contentDetails']['relatedPlaylists']['uploads'] - - # playlist_params = { - # 'key': settings.YOUTUBE_DATA_API_KEY, - # 'part': 'snippet', - # 'playlistId': results, - # 'maxResults': 5, - # } - # p = requests.get(playlist_url, params=playlist_params) - # results1 = p.json()['items'] - - # for result in results1: - # videos_list.append(result['snippet']['resourceId']['videoId']) - - # videos_params = { - # 'key': settings.YOUTUBE_DATA_API_KEY, - # 'part': 'snippet', - # 'id': ','.join(videos_list) - # } - - # v = requests.get(videos_url, params=videos_params) - # results2 = v.json()['items'] - # videos = [] - # for res in results2: - # video_data = { - # 'id': res['id'], - # 'title': res['snippet']['title'], - # } - - # videos.append(video_data) - modules = Modules.objects.select_related().filter(course_id=course) + channel_url = "https://www.googleapis.com/youtube/v3/channels" + playlist_url = "https://www.googleapis.com/youtube/v3/playlistItems" + videos_url = "https://www.googleapis.com/youtube/v3/videos" + + videos_list = [] + channel_params = { + 'part': 'contentDetails', + # 'forUsername': 'TechGuyWeb', + 'id': 'UCdGQeihs84hyCssI2KuAPmA', + 'key': settings.YOUTUBE_DATA_API_KEY, + } + r = requests.get(channel_url, params=channel_params) + results = r.json()['items'][0]['contentDetails']['relatedPlaylists']['uploads'] + + playlist_params = { + 'key': settings.YOUTUBE_DATA_API_KEY, + 'part': 'snippet', + 'playlistId': results, + 'maxResults': 5, + } + p = requests.get(playlist_url, params=playlist_params) + results1 = p.json()['items'] + + for result in results1: + videos_list.append(result['snippet']['resourceId']['videoId']) + + videos_params = { + 'key': settings.YOUTUBE_DATA_API_KEY, + 'part': 'snippet', + 'id': ','.join(videos_list) + } + + v = requests.get(videos_url, params=videos_params) + results2 = v.json()['items'] + videos = [] + for res in results2: + video_data = { + 'id': res['id'], + 'title': res['snippet']['title'], + } + + videos.append(video_data) slides = CourseDocuments.objects.select_related().filter(course_id=course) - modules_with_slides = collections.OrderedDict() - for m in modules: - sl = [] - for slide in slides: - if slide.module_id.id == m.id: - sl.append(slide) - if len(sl) == 0: - modules_with_slides[m] = 0 - else: - modules_with_slides[m] = sl - # quiz = Quiz.objects.select_related().filter(course_id=course) - # marks = [] - # quizs = [] + quiz = Quiz.objects.select_related().filter(course_id=course) + marks = [] + quizs = [] assignment = Assignment.objects.select_related().filter(course_id=course) student_assignment = [] for assi in assignment: sa = StudentAssignment.objects.select_related().filter(assignment_id=assi) student_assignment.append(sa) - # for q in quiz: - # qs = QuizResult.objects.select_related().filter(quiz_id=q) - # if q.end_time > timezone.now(): - # quizs.append(q) - # if len(qs) != 0: - # marks.append(qs) + for q in quiz: + qs = QuizResult.objects.select_related().filter(quiz_id=q) + if q.end_time > timezone.now(): + quizs.append(q) + if len(qs) != 0: + marks.append(qs) comments = Forum.objects.select_related().filter(course_id=course).order_by('comment_time') answers = collections.OrderedDict() for comment in comments: @@ -414,40 +311,16 @@ def course(request, course_code, version): fr1 = ForumReply.objects.select_related().filter(forum_ques=comment) if not fr: answers[comment] = fr1 - # qb = QuestionBank.objects.select_related().filter(instructor_id=extrainfo, course_id=course) - - gradingscheme = GradingScheme.objects.select_related().filter(course_id=course) - try: - gradingscheme_grades = GradingScheme_grades.objects.select_related().get(course_id=course) - except GradingScheme_grades.DoesNotExist: - gradingscheme_grades = {} - - try: - student_grades = Student_grades.objects.select_related().filter(course_id=course).order_by('roll_no') - except Student_grades.DoesNotExist: - student_grades = {} - - marks_with_roll = collections.OrderedDict() - for eval in gradingscheme: - try: - rollist = StudentEvaluation.objects.filter(evaluation_id=eval) - except StudentEvaluation.DoesNotExist: - rollist = {} - for roll in rollist: - sid = roll.student_id.id.id - scaling_factor = eval.weightage/roll.total_marks - scaled_marks = scaling_factor*roll.marks - if sid in marks_with_roll: - marks_with_roll[sid] += round(scaled_marks,2) - else: - marks_with_roll[sid] = round(scaled_marks,2) - + qb = QuestionBank.objects.select_related().filter(instructor_id=extrainfo, course_id=course) return render(request, 'coursemanagement/viewcourse.html', {'instructor': instructor, 'extrainfo': extrainfo, + 'curriculum': curriculum, 'students' : students, 'registered_students': registered_students, - 'modules': modules_with_slides, + 'fut_quiz': quizs, + 'quizs': marks, + 'videos': videos, 'slides': slides, 'topics':topics, 'course': course, @@ -455,21 +328,16 @@ def course(request, course_code, version): 'assignment': assignment, 'student_assignment': student_assignment, 'Lecturer': lec, - 'notifications': notifs, + 'questionbank': qb, 'students': students, 'total_attendance' : total_attendance, 'present_attendance':present_attendance, - 'test_marks': test_marks, - 'gradingscheme':gradingscheme, - 'gradingscheme_grades': gradingscheme_grades, - 'student_grades' : student_grades, - 'marks_with_roll': marks_with_roll + 'test_marks': test_marks }) - #when student uploads the assignment's solution @login_required -def upload_assignment(request, course_code, version): +def upload_assignment(request, course_code): extrainfo = ExtraInfo.objects.select_related().get(user=request.user) if extrainfo.user_type == "student": student = Student.objects.select_related('id').get(id=extrainfo) @@ -481,9 +349,9 @@ def upload_assignment(request, course_code, version): assign = Assignment.objects.get(pk=assi_name) filename, file_extenstion = os.path.splitext(request.FILES.get('img').name) except: - return HttpResponse("Please fill each and every field correctly!", status=422) + return HttpResponse("Please fill each and every field correctly!") filename = name - full_path = settings.MEDIA_ROOT + "/online_cms/" + course_code + "/" + version + "/assi/" #storing the media files + full_path = settings.MEDIA_ROOT + "/online_cms/" + course_code + "/assi/" #storing the media files full_path = full_path + assign.assignment_name + "/" + student.id.id + "/" url = settings.MEDIA_URL + filename if not os.path.isdir(full_path): @@ -491,8 +359,7 @@ def upload_assignment(request, course_code, version): subprocess.call(cmd, shell=True) fs = FileSystemStorage(full_path, url) fs.save(name + file_extenstion, doc) #saving the media files - # uploaded_file_url = full_path+ "/" + name + file_extenstion - uploaded_file_url = "/media/online_cms/" + course_code + "/" + version + "/assi/" + assign.assignment_name + "/" + student.id.id + "/" + name + file_extenstion + uploaded_file_url = full_path+ "/" + name + file_extenstion # to save the solution of assignment the database sa = StudentAssignment( student_id=student, @@ -505,141 +372,71 @@ def upload_assignment(request, course_code, version): else: return HttpResponse("not found") -# when faculty creates modules -@login_required -def add_modules(request, course_code, version): - extrainfo = ExtraInfo.objects.select_related().get(user=request.user) - if extrainfo.user_type == "faculty": #user should be faculty only - instructor = CourseInstructor.objects.select_related('course_id').filter(instructor_id=extrainfo) #get the course information - - for ins in instructor: - if ins.course_id.code == course_code and ins.course_id.version == Decimal(version): - course = ins.course_id - - try: - module_name = request.POST.get('module_name') - except: - return HttpResponse("Please fill each and every field correctly!",status=422) - - Modules.objects.create( - course_id=course, - module_name=module_name - ) - return HttpResponse("Module creation successful") - else: - return HttpResponse("Not found", status=400) - # when faculty uploads the slides, ppt @login_required -def add_document(request, course_code, version): +def add_document(request, course_code): extrainfo = ExtraInfo.objects.select_related().get(user=request.user) if extrainfo.user_type == "faculty": #user should be faculty only - instructor = CourseInstructor.objects.select_related('course_id').filter(instructor_id=extrainfo) #get the course information + instructor = Curriculum_Instructor.objects.select_related('curriculum_id').filter(instructor_id=extrainfo) #get the course information for ins in instructor: - if ins.course_id.code == course_code and ins.course_id.version == Decimal(version): - course = ins.course_id + if ins.curriculum_id.course_code == course_code: + course = ins.curriculum_id.course_id try: description = request.POST.get('description') doc = request.FILES.get('img') name = request.POST.get('name') - module_id = request.POST.get('module_id') filename, file_extenstion = os.path.splitext(request.FILES.get('img').name) except: - return HttpResponse("Please fill each and every field correctly!",status=422) + return HttpResponse("Please fill each and every field correctly!") #for storing the media files properly filename = name - full_path = settings.MEDIA_ROOT + "/online_cms/" + course_code + "/" + version + "/doc/" + full_path = settings.MEDIA_ROOT + "/online_cms/" + course_code + "/doc/" url = settings.MEDIA_URL + filename + file_extenstion if not os.path.isdir(full_path): cmd = "mkdir " + full_path subprocess.call(cmd, shell=True) fs = FileSystemStorage(full_path, url) fs.save(filename + file_extenstion, doc) - # uploaded_file_url = full_path + filename + file_extenstion - uploaded_file_url = "/media/online_cms/" + course_code + "/" + version + "/doc/" + filename + file_extenstion + uploaded_file_url = full_path + filename + file_extenstion #save the info/details in the database CourseDocuments.objects.create( course_id=course, upload_time=datetime.datetime.now(), description=description, document_url=uploaded_file_url, - document_name=name+file_extenstion, - module_id_id=module_id - ) - return HttpResponse("Upload successful.") - else: - return HttpResponse("Not found") - -# if faculty uploads the attendance file -@login_required -def add_attendance(request, course_code, version): - extrainfo = ExtraInfo.objects.select_related().get(user=request.user) - if extrainfo.user_type == "faculty": #user should be faculty only - instructor = CourseInstructor.objects.select_related('course_id').filter(instructor_id=extrainfo) #get the course information - - for ins in instructor: - if ins.course_id.code == course_code and ins.course_id.version == Decimal(version): - course = ins.course_id - - try: - doc = request.FILES.get('img') - name = request.POST.get('name') - filename, file_extenstion = os.path.splitext(request.FILES.get('img').name) - except: - return HttpResponse("Please fill each and every field correctly!",status=422) - #for storing the media files properly - filename = name - full_path = settings.MEDIA_ROOT + "/online_cms/" + course_code + "/" + version + "/attendance/" - url = settings.MEDIA_URL + filename + file_extenstion - if not os.path.isdir(full_path): - cmd = "mkdir " + full_path - subprocess.call(cmd, shell=True) - fs = FileSystemStorage(full_path, url) - fs.save(filename + file_extenstion, doc) - # uploaded_file_url = full_path + filename + file_extenstion - uploaded_file_url = "/media/online_cms/" + course_code + "/" + version + "/attendance/" + filename + file_extenstion - #save the info/details in the database - AttendanceFiles.objects.create( - course_id=course, - upload_time=datetime.datetime.now(), - file_url=uploaded_file_url, - file_name=name+file_extenstion + document_name=name+file_extenstion ) return HttpResponse("Upload successful.") else: - return HttpResponse("Not found") + return HttpResponse("not found") #it is to delete things(assignment, slides, videos, ) from the dustin icon or delete buttons @login_required -def delete(request, course_code, version): +def delete(request, course_code): data_type = request.POST.get('type') user = request.user extrainfo = ExtraInfo.objects.select_related().get(user=user) #get the course and user information first if extrainfo.user_type == "faculty": - instructor = CourseInstructor.objects.select_related('course_id').filter(instructor_id=extrainfo) #get the course information - + instructor = Curriculum_Instructor.objects.select_related('curriculum_id').filter(instructor_id=extrainfo) for ins in instructor: - if ins.course_id.code == course_code and ins.course_id.version == Decimal(version): - course = ins.course_id + if ins.curriculum_id.course_code == course_code: + course = ins.curriculum_id.course_id if extrainfo.user_type == 'student': - curriculum_details = Courses.objects.select_related().filter(code=course_code, version=version) + curriculum_details = Curriculum.objects.select_related('course_id').filter(course_code=course_code) course = curriculum_details - + course1 = curriculum_details[0].course_id + curriculum1 = course[0] pk = request.POST.get('pk') - path = "" #to delete videos if data_type == 'video': video = CourseVideo.objects.get(pk=pk, course_id=course) path = video.video_url video.delete() - elif data_type == 'module': - module = Modules.objects.select_related().get(pk=pk, course_id=course) - module.delete() #to delete slides/documents elif data_type == 'slide': slide = CourseDocuments.objects.select_related().get(pk=pk, course_id=course) @@ -655,10 +452,8 @@ def delete(request, course_code, version): lec_assi = Assignment.objects.select_related().get(pk=pk) path = lec_assi.assignment_url lec_assi.delete() - if path: - cmd = "rm "+path - subprocess.call(cmd, shell=True) - + cmd = "rm "+path + subprocess.call(cmd, shell=True) data = { 'msg': 'Data Deleted successfully'} return HttpResponse(json.dumps(data), content_type='application/json') @@ -668,11 +463,10 @@ def add_videos(request, course_code): extrainfo = ExtraInfo.objects.select_related().get(user=request.user) #only faculty can add the videos if extrainfo.user_type == "faculty": - instructor = CourseInstructor.objects.select_related('course_id').filter(instructor_id=extrainfo) - + instructor = Curriculum_Instructor.objects.select_related('curriculum_id').filter(instructor_id=extrainfo) for ins in instructor: - if ins.course_id.code == course_code: - course = ins.course_id + if ins.curriculum_id.course_code == course_code: + course = ins.curriculum_id.course_id try: description = request.POST.get('description') #the media files required vid = request.FILES.get('img') @@ -702,24 +496,24 @@ def add_videos(request, course_code): create_thumbnail(course_code,course, video, name, file_extenstion, 'Small', 1, '170:127') return HttpResponse("Upload successful.") else: - return HttpResponse("Not found") + return HttpResponse("not found") @login_required -def forum(request, course_code, version): +def forum(request, course_code): # take care of sem extrainfo = ExtraInfo.objects.select_related().get(user=request.user) if extrainfo.user_type == "student": student = Student.objects.select_related('id').get(id=extrainfo) roll = student.id.id[:4] - course = Courses.objects.select_related().get(course=course_code, version=version) + course = Course.objects.select_related().get(course_id=course_code, sem=semester(roll)) else: - instructor = CourseInstructor.objects.select_related('course_id').filter(instructor_id=extrainfo) + instructor = Curriculum_Instructor.objects.select_related().filter(instructor_id=extrainfo) for ins in instructor: - if ins.course_id.code == course_code and ins.course_id.version == Decimal(version): - course = ins.course_id.pk + if ins.course_id.course_id == course_code: + course = ins.course_id comments = Forum.objects.select_related().filter(course_id=course).order_by('comment_time') - instructor = CourseInstructor.objects.get(course_id=course) + instructor = Curriculum_Instructor.objects.get(course_id=course) if instructor.instructor_id.user.pk == request.user.pk: lec = 1 else: @@ -735,21 +529,22 @@ def forum(request, course_code, version): @login_required -def ajax_reply(request, course_code, version): +def ajax_reply(request, course_code): extrainfo = ExtraInfo.objects.select_related().get(user=request.user) if extrainfo.user_type == "student": student = Student.objects.select_related('id').get(id=extrainfo) roll = student.id.id[:4] + curriculum_details = Curriculum.objects.select_related('course_id').filter(course_code=course_code) #curriculum id #print(curriculum_details[0].course_id) #print(Curriculum.objects.values_list('curriculum_id')) - course = Courses.objects.select_related().filter(code=course_code, version=version) + course = curriculum_details[0].course_id # course = Course.objects.get(course_id=course_code, sem=semester(roll)) else: - instructor = CourseInstructor.objects.select_related('course_id').filter(instructor_id=extrainfo) + instructor = Curriculum_Instructor.objects.select_related('curriculum_id').filter(instructor_id=extrainfo) for ins in instructor: - if ins.course_id.code == course_code and ins.course_id.version == Decimal(version): - course = ins.course_id.pk + if ins.curriculum_id.course_code == course_code: + course = ins.curriculum_id.course_id ex = ExtraInfo.objects.select_related().get(user=request.user) f = Forum( course_id=course, @@ -770,64 +565,34 @@ def ajax_reply(request, course_code, version): @login_required -def ajax_new(request, course_code, version): - # extrainfo = ExtraInfo.objects.select_related().get(user=request.user) - # if extrainfo.user_type == "student": - # student = Student.objects.select_related('id').get(id=extrainfo) - # roll = student.id.id[:4] - # #course = Course.objects.get(course_id=course_code, sem=semester(roll)) - # curriculum_details = Curriculum.objects.select_related('course_id').filter(course_code=course_code) #curriculum id - # #print(curriculum_details[0].course_id) - # #print(Curriculum.objects.values_list('curriculum_id')) - # course = curriculum_details[0].course_id - # else: - - # instructor = Curriculum_Instructor.objects.select_related('curriculum_id').filter(instructor_id=extrainfo) - # for ins in instructor: - # if ins.curriculum_id.course_code == course_code: - # course = ins.curriculum_id.course_id - # ex = ExtraInfo.objects.select_related().get(user=request.user) - # f = Forum( - # course_id=course, - # commenter_id=ex, - # comment=request.POST.get('question') - # ) - # f.save() - # name = request.user.first_name + " " + request.user.last_name - # time = f.comment_time.strftime('%b. %d, %Y, %I:%M %p') - - # data = {'pk': f.pk, 'question': f.comment, 'replier': f.commenter_id.user.username, - # 'time': time, 'name': name} - ex = ExtraInfo.objects.select_related().get(user=request.user) - if ex.user_type == "student": - print() +def ajax_new(request, course_code): + extrainfo = ExtraInfo.objects.select_related().get(user=request.user) + if extrainfo.user_type == "student": + student = Student.objects.select_related('id').get(id=extrainfo) + roll = student.id.id[:4] + #course = Course.objects.get(course_id=course_code, sem=semester(roll)) + curriculum_details = Curriculum.objects.select_related('course_id').filter(course_code=course_code) #curriculum id + #print(curriculum_details[0].course_id) + #print(Curriculum.objects.values_list('curriculum_id')) + course = curriculum_details[0].course_id else: - HttpResponse('Announcement in Progress') - instructor = CourseInstructor.objects.select_related('course_id').filter(instructor_id=ex) + + instructor = Curriculum_Instructor.objects.select_related('curriculum_id').filter(instructor_id=extrainfo) for ins in instructor: - if ins.course_id.code == course_code and ins.course_id.version == Decimal(version): - course = ins.course_id - registered_students = course_registration.objects.filter(course_id = ins.course_id) - - f = Forum( - course_id=course, - commenter_id=ex, - comment=request.POST.get('question') - ) - f.save() - name = request.user.first_name + " " + request.user.last_name - time = f.comment_time.strftime('%b. %d, %Y, %I:%M %p') - data = {'pk': f.pk, 'question': f.comment, 'replier': f.commenter_id.user.username, - 'time': time, 'name': name} - # course_json = serialize('json', [course]) - - student = [] - for reg_student in registered_students: - student.append(Student.objects.get(id = str(reg_student.student_id)).id.user) - # student = Student.objects.get(id = str(reg_student.student_id)) - - course_management_notif(request.user, student, request.POST.get('question'), course.pk, str(course.name),course_code, "announcement") + if ins.curriculum_id.course_code == course_code: + course = ins.curriculum_id.course_id + ex = ExtraInfo.objects.select_related().get(user=request.user) + f = Forum( + course_id=course, + commenter_id=ex, + comment=request.POST.get('question') + ) + f.save() + name = request.user.first_name + " " + request.user.last_name + time = f.comment_time.strftime('%b. %d, %Y, %I:%M %p') + data = {'pk': f.pk, 'question': f.comment, 'replier': f.commenter_id.user.username, + 'time': time, 'name': name} return HttpResponse(json.dumps(data), content_type='application/json') @@ -856,30 +621,28 @@ def ajax_remove(request, course_code): @login_required -def add_assignment(request, course_code, version): #from faculty side +def add_assignment(request, course_code): #from faculty side extrainfo = ExtraInfo.objects.select_related().get(user=request.user) if extrainfo.user_type == "faculty": - instructor = CourseInstructor.objects.select_related('course_id').filter(instructor_id=extrainfo) + instructor = Curriculum_Instructor.objects.select_related('curriculum_id').filter(instructor_id=extrainfo) for ins in instructor: - if ins.course_id.code == course_code and ins.course_id.version == Decimal(version): - course = ins.course_id - registered_students = course_registration.objects.filter(course_id = ins.course_id) + if ins.curriculum_id.course_code == course_code: + course = ins.curriculum_id.course_id try: assi = request.FILES.get('img') name = request.POST.get('name') filename, file_extenstion = os.path.splitext(request.FILES.get('img').name) except: - return HttpResponse("Please Enter The Form Properly",status=422) + return HttpResponse("Please Enter The Form Properly") filename = name - full_path = settings.MEDIA_ROOT + "/online_cms/" + course_code + "/" + version + "/assi/" + name + "/" + full_path = settings.MEDIA_ROOT + "/online_cms/" + course_code + "/assi/" + name + "/" url = settings.MEDIA_URL + filename if not os.path.isdir(full_path): cmd = "mkdir " + full_path subprocess.call(cmd, shell=True) fs = FileSystemStorage(full_path, url) fs.save(filename+file_extenstion, assi) - uploaded_file_url = "/media/online_cms/"+ course_code + "/" + version + "/assi/" + name + "/" + filename + file_extenstion - # uploaded_file_url = full_path + filename + file_extenstion + uploaded_file_url = full_path + filename + file_extenstion assign = Assignment( course_id=course, submit_date=request.POST.get('myDate'), @@ -887,12 +650,7 @@ def add_assignment(request, course_code, version): #from faculty assignment_name=name ) assign.save() - student = [] - for reg_student in registered_students: - student.append(Student.objects.get(id = str(reg_student.student_id)).id.user) - print(student) - course_management_notif(request.user, student, "New Assignment Created!", course.pk, str(course.name),course_code,0) - return HttpResponse("Assignment created successfully.") + return HttpResponse("Upload successful.") else: return HttpResponse("not found") @@ -903,10 +661,10 @@ def edit_bank(request, course_code, qb_code): extrainfo = ExtraInfo.objects.select_related().get(user=user) lec = 1 if extrainfo.user_type == "faculty": - instructor = CourseInstructor.objects.select_related('course_id').filter(instructor_id=extrainfo) + instructor = Curriculum_Instructor.objects.select_related('curriculum_id').filter(instructor_id=extrainfo) for ins in instructor: - if ins.course_id.code == course_code: - course = ins.course_id + if ins.curriculum_id.course_code == course_code: + course = ins.curriculum_id.course_id qb = QuestionBank.objects.select_related().filter(id=qb_code) topics = Topics.objects.select_related().filter(course_id=course) Topic = {} @@ -934,10 +692,10 @@ def create_bank(request, course_code): user = request.user extrainfo = ExtraInfo.objects.select_related().get(user=user) if extrainfo.user_type == "faculty": - instructor = CourseInstructor.objects.select_related('course_id').filter(instructor_id=extrainfo) + instructor = Curriculum_Instructor.objects.select_related('curriculum_id').filter(instructor_id=extrainfo) for ins in instructor: - if ins.course_id.code == course_code: - course = ins.course_id + if ins.curriculum_id.course_code == course_code: + course = ins.curriculum_id.course_id qb = QuestionBank.objects.create(instructor_id=extrainfo, course_id=course, name=request.POST.get('qbname')) return redirect('/ocms/' + course_code + '/edit_bank/'+str(qb.id)) @@ -947,10 +705,10 @@ def create_topic(request, course_code): user = request.user extrainfo = ExtraInfo.objects.select_related().get(user=user) if extrainfo.user_type == "faculty": - instructor = CourseInstructor.objects.select_related('course_id').filter(instructor_id=extrainfo) + instructor = Curriculum_Instructor.objects.select_related('curriculum_id').filter(instructor_id=extrainfo) for ins in instructor: - if ins.course_id.code == course_code: - course = ins.course_id + if ins.curriculum_id.course_code == course_code: + course = ins.curriculum_id.course_id topic = Topics.objects.create(course_id=course, topic_name=request.POST.get('topic_name')) return redirect('/ocms/' + course_code) @@ -961,10 +719,10 @@ def remove_bank(request, course_code): user = request.user extrainfo = ExtraInfo.objects.select_related().get(user=user) if extrainfo.user_type == "faculty": - instructor = CourseInstructor.objects.select_related('course_id').filter(instructor_id=extrainfo) + instructor = Curriculum_Instructor.objects.select_related('curriculum_id').filter(instructor_id=extrainfo) for ins in instructor: - if ins.course_id.code == course_code: - course = ins.course_id + if ins.curriculum_id.course_code == course_code: + course = ins.curriculum_id.course_id qb = QuestionBank.objects.select_related().get(id=request.POST.get('pk')) qb.delete() qb = QuestionBank.objects.select_related().filter(instructor_id=extrainfo, course_id=course) @@ -976,10 +734,10 @@ def remove_topic(request, course_code): user = request.user extrainfo = ExtraInfo.objects.select_related().get(user=user) if extrainfo.user_type == "faculty": - instructor = CourseInstructor.objects.select_related('course_id').filter(instructor_id=extrainfo) + instructor = Curriculum_Instructor.objects.select_related('curriculum_id').filter(instructor_id=extrainfo) for ins in instructor: - if ins.course_id.code == course_code: - course = ins.course_id + if ins.curriculum_id.course_code == course_code: + course = ins.curriculum_id.course_id topic = Topics.objects.select_related().get(id=request.POST.get('pk')) topic.delete() n_topics = Topics.objects.select_related().filter(course_id=course) @@ -990,7 +748,7 @@ def remove_topic(request, course_code): @login_required def add_question(request, course_id, qb_code, topic_id): user = request.user - course = Courses.objects.select_related().get(pk=course_id) + course = Course.objects.select_related().get(pk=course_id) curriculum = Curriculum.objects.select_related().get(course_id=course) course_code = curriculum.course_code extrainfo = ExtraInfo.objects.select_related().get(user=user) @@ -1048,9 +806,9 @@ def edit_qb_topics(request, course_code, qb_code, topic_id): extrainfo = ExtraInfo.objects.select_related().get(user=user) lec = 1 if extrainfo.user_type == "faculty": - instructor = CourseInstructor.objects.select_related('course_id').filter(instructor_id=extrainfo) + instructor = Curriculum_Instructor.objects.select_related().filter(instructor_id=extrainfo) for ins in instructor: - if ins.course_id.code == course_code: + if ins.course_id.course_id == course_code: course = ins.course_id qb = QuestionBank.objects.select_related().filter(pk=qb_code) topic = Topics.objects.select_related().get(id=topic_id) @@ -1149,10 +907,11 @@ def create_quiz(request, course_code): extrainfo = ExtraInfo.objects.select_related().get(user=request.user) if extrainfo.user_type == 'faculty': - instructor = CourseInstructor.objects.select_related('course_id').filter(instructor_id=extrainfo) + instructor = Curriculum_Instructor.objects.select_related('curriculum_id').filter(instructor_id=extrainfo) for ins in instructor: - if ins.course_id.code == course_code: - course = ins.course_id + if ins.curriculum_id.course_code == course_code: + curriculum = ins.curriculum_id + course = ins.curriculum_id.course_id for ins in instructor: if ins.curriculum_id.course_code == course_code: @@ -1249,11 +1008,11 @@ def edit_quiz(request, course_code, quiz_code): extrainfo = ExtraInfo.objects.select_related().get(user=request.user) if extrainfo.user_type == 'faculty': lec = 1 - instructor = CourseInstructor.objects.select_related('course_id').filter(instructor_id=extrainfo) + instructor = Curriculum_Instructor.objects.select_related('curriculum_id').filter(instructor_id=extrainfo) for ins in instructor: - if ins.course_id.code == course_code: - course = ins.course_id - registered_students = course_registration.objects.filter(course_id = ins.course_id) + if ins.curriculum_id.course_code == course_code: + curriculum = ins.curriculum_id + course = ins.curriculum_id.course_id # errors = None quiz = Quiz.objects.select_related().get(pk=quiz_code) questions = QuizQuestion.objects.select_related('question').filter(quiz_id=quiz) @@ -1284,11 +1043,10 @@ def edit_quiz_topic(request, course_code, quiz_code, topic_id): extrainfo = ExtraInfo.objects.select_related().get(user=user) lec = 1 if extrainfo.user_type == "faculty": - instructor = CourseInstructor.objects.select_related('course_id').filter(instructor_id=extrainfo) + instructor = Curriculum_Instructor.objects.select_related().filter(instructor_id=extrainfo) for ins in instructor: - if ins.course_id.code == course_code: + if ins.course_id.course_id == course_code: course = ins.course_id - registered_students = course_registration.objects.filter(course_id = ins.course_id) quiz_question = QuizQuestion.objects.select_related('question').filter(quiz_id=quiz_code) quest = [] quiz = Quiz.objects.select_related().get(pk=quiz_code) @@ -1318,10 +1076,10 @@ def remove_quiz_question(request, course_code, quiz_code, topic_id): def add_question_topicwise(request, course_code, quiz_id): extrainfo = ExtraInfo.objects.select_related().get(user=request.user) if extrainfo.user_type == 'faculty': - instructor = CourseInstructor.objects.select_related('course_id').filter(instructor_id=extrainfo) + instructor = Curriculum_Instructor.objects.select_related('curriculum_id').filter(instructor_id=extrainfo) for ins in instructor: - if ins.course_id.code == course_code: - course = ins.course_id + if ins.curriculum_id.course_code == course_code: + course = ins.curriculum_id.course_id ques_bank = request.POST.get('qbank') quiz = Quiz.objects.select_related().get(pk=quiz_id) topic = request.POST.get('topic') @@ -1346,7 +1104,7 @@ def add_question_topicwise(request, course_code, quiz_id): @login_required def add_questions_to_quiz(request, course_id, quiz_id): - course = Courses.objects.select_related().get(pk=course_id) + course = Course.objects.select_related().get(pk=course_id) curriculum = Curriculum.objects.select_related().get(course_id = course) course_code = curriculum.course_code extrainfo = ExtraInfo.objects.select_related().get(user=request.user) @@ -1366,10 +1124,10 @@ def add_questions_to_quiz(request, course_id, quiz_id): def preview_quiz(request, course_code, quiz_code): extrainfo = ExtraInfo.objects.select_related().get(user=request.user) if extrainfo.user_type == 'faculty': - instructor = CourseInstructor.objects.select_related('course_id').filter(instructor_id=extrainfo) + instructor = Curriculum_Instructor.objects.select_related('curriculum_id').filter(instructor_id=extrainfo) for ins in instructor: - if ins.course_id.code == course_code: - course = ins.course_id + if ins.curriculum_id.course_code == course_code: + course = ins.curriculum_id.course_id quiz = Quiz.objects.select_related().get(pk=quiz_code) questions = QuizQuestion.objects.select_related().filter(quiz_id=quiz) @@ -1400,27 +1158,15 @@ def remove_quiz(request, course_code): @login_required -def ajax_assess(request, course_code, version): - extrainfo = ExtraInfo.objects.select_related().get(user=request.user) - if extrainfo.user_type == "faculty": - instructor = CourseInstructor.objects.select_related('course_id').filter(instructor_id=extrainfo) - for ins in instructor: - if ins.course_id.code == course_code and ins.course_id.version == Decimal(version): - course = ins.course_id - # registered_students = Register.objects.filter(curr_id = ins.curriculum_id.curriculum_id) - sa = StudentAssignment.objects.select_related().get(pk=request.POST.get('pk')) - sa.score = request.POST.get('marks') - sa.save() - student = [] - student.append(sa.student_id.id.user) - # for reg_student in registered_students: - # # student.append(Student.objects.get(id = str(reg_student.student_id)).id.user) - course_management_notif(request.user, student, "An Assignment has been graded!", course.pk, str(course.name),course_code, 0) - return HttpResponse("Marks uploaded") +def ajax_assess(request, course_code): + sa = StudentAssignment.objects.select_related().get(pk=request.POST.get('pk')) + sa.score = request.POST.get('marks') + sa.save() + return HttpResponse("Marks uploaded") @login_required -def ajax_feedback(request, course_code, version): +def ajax_feedback(request, course_code): sa = StudentAssignment.objects.select_related().get(pk=request.POST.get('pk')) sa.feedback = request.POST.get('feedback') sa.save() @@ -1564,70 +1310,68 @@ def add_practice_question(request, course_code, practice_contest_code): ) return redirect('/ocms/' + course_code + '/edit_practice_contest/'+str(pq[0].id)) -@csrf_protect -@login_required -def edit_marks(request, course_code,version): - user = request.user - extrainfo = ExtraInfo.objects.get(user=user) +# @csrf_protect +# @login_required +# def edit_marks(request, course_code): +# user = request.user +# extrainfo = ExtraInfo.objects.get(user=user) - if extrainfo.user_type == 'faculty': - instructor = CourseInstructor.objects.select_related('course_id').filter(instructor_id=extrainfo) - for ins in instructor: - if ins.course_id.code == course_code and ins.course_id.version == Decimal(version): - registered_students = course_registration.objects.select_related('student_id').filter(course_id = ins.course_id) +# if extrainfo.user_type == 'faculty': +# instructor = Curriculum_Instructor.objects.filter(instructor_id=extrainfo) +# for ins in instructor: +# if ins.curriculum_id.course_code == course_code: +# registered_students = Register.objects.filter(curr_id = ins.curriculum_id.curriculum_id) - exam = request.POST.get('examtype') - score = request.POST.getlist('enteredmarks') - total = request.POST.get('totalmarks') - e_id = GradingScheme.objects.get(pk=int(exam)) - print(e_id) - List = list() - for i in range(len(registered_students)): - m_id = registered_students[i].student_id - s = score[i] +# exam = request.POST.get('examtype') +# score = request.POST.getlist('enteredmarks') - # rows = StudentEvaluation.objects.filter(mid=m_id, exam_type=exam) - num = StudentEvaluation.objects.filter(student_id=m_id, evaluation_id=exam).count() - record = StudentEvaluation.objects.filter(student_id=m_id, evaluation_id=exam).values_list('marks', flat=True) +# List = list() - List.append(list(record)) +# for i in range(len(registered_students)): +# m_id = registered_students[i] +# s = score[i] + +# # rows = StoreMarks.objects.filter(mid=m_id, exam_type=exam) +# num = StoreMarks.objects.filter(mid=m_id, exam_type=exam).count() +# record = StoreMarks.objects.filter(mid=m_id, exam_type=exam).values_list('marks', flat=True) + +# List.append(list(record)) + +# if num==0: +# StoreMarks.objects.create( +# mid=m_id, +# exam_type=exam, +# marks=s +# ) +# else: +# StoreMarks.objects.filter(mid=m_id, exam_type=exam).update(marks=s) + +# #print(registered_students) - if num==0: - StudentEvaluation.objects.create( - student_id=m_id, - evaluation_id=e_id, - marks=s, - total_marks = total - ) - else: - StudentEvaluation.objects.filter(student_id=m_id, evaluation_id=e_id).update(marks=s) - return JsonResponse({"message": "Upload Successful"}, status=200) - return HttpResponse("Unauthorized", status=403) - # context= {'m_id':m_id,'registered_students': registered_students, 'record':List} - # return render(request, 'coursemanagement/assessment.html', context) +# return HttpResponse("Upload successful") +# context= {'m_id':m_id,'registered_students': registered_students, 'record':List} +# return render(request, 'coursemanagement/assessment.html', context) @csrf_protect @login_required -def get_exam_data(request,course_code, version): #it is store the type of exam helpful in storing the marks +def get_exam_data(request,course_code): #it is store the type of exam helpful in storing the marks exam_name = request.POST['exam_name'] - e_id = GradingScheme.objects.get(pk=int(exam_name)) - data = serializers.serialize('json', StudentEvaluation.objects.filter(evaluation_id=e_id)) + data = serializers.serialize('json', StoreMarks.objects.filter(exam_type=exam_name)) return HttpResponse(data, content_type='application/json') #to store the attendance of the student by taking from templates (attendance.html) @login_required -def submit_attendance(request, course_code, version): +def submit_attendance(request, course_code): user = request.user extrainfo = ExtraInfo.objects.select_related().get(user=user) - course_id = Courses.objects.select_related().get(code=course_code, version=version) if extrainfo.user_type == 'faculty': #only faculty can change the attendance of the students - instructor_old = CourseInstructor.objects.select_related().filter(instructor_id=extrainfo, course_id=course_id) + instructor_old = Curriculum_Instructor.objects.select_related().filter(instructor_id=extrainfo) for x in instructor_old: instructor = x @@ -1637,349 +1381,27 @@ def submit_attendance(request, course_code, version): if form.is_valid(): # for item in form.cleaned_data['Present_absent']: # print(item) - try: - date = request.POST['date'] - total_attendance = int(request.POST['total_attendance']) - except: - return HttpResponse("Please Enter The Form Properly",status=400) + date = request.POST['date'] #mark the attendance according to the student roll no. all_students = request.POST.getlist('Roll') + present_students = request.POST.getlist('Present_absent') for student in all_students: s_id = Student.objects.select_related().get(id = student) - presents = int(request.POST.get(f'Present_absent_{student}', 0)) - - # Ensure presents do not exceed total attendance - if presents > total_attendance: - presents = total_attendance - - Attendance.objects.create( - student_id = s_id, - instructor_id = instructor, - date = date, - present = presents, - no_of_attendance = total_attendance - ) - - return HttpResponse("Upload Successful", status=200) - return HttpResponse("Form is not valid", status=400) - -#to store the grading scheme created by the faculty -@login_required -def create_grading_scheme(request, course_code, version): - - user = request.user - extrainfo = ExtraInfo.objects.select_related().get(user=user) - course_id = Courses.objects.select_related().get(code=course_code, version=version) - if extrainfo.user_type == 'faculty': #only faculty can change the attendance of the students - if request.method == 'POST': - - form_data = {} - form_data = request.POST.copy() - form_data.pop('add_item_wtg', None) - form_data.pop('add_eval_type', None) - form_data.pop('csrfmiddlewaretoken', None) - - key_list = list(form_data.keys()) - - no_of_evaluation_types = len(form_data) - 20 - - for i in range(no_of_evaluation_types): - already_existing_data = GradingScheme.objects.filter(course_id=course_id, type_of_evaluation=key_list[i]) - if already_existing_data.exists(): - grading_scheme_object = already_existing_data.first() - grading_scheme_object.weightage = form_data[key_list[i]] - grading_scheme_object.save() - else: - grading_scheme = GradingScheme.objects.create( - course_id=course_id, - type_of_evaluation=key_list[i], - weightage=form_data[key_list[i]] + present = False + if student in present_students: + present = True + + Student_attendance.objects.create( + student_id = s_id, + instructor_id = instructor, + date = date, + present = present ) - grading_scheme.save() - - # for key, value in form_data.items(): - # # Check if the grading scheme already exists - # already_existing_data = GradingScheme.objects.filter(course_id=course_id, type_of_evaluation=key) - # if already_existing_data.exists(): - # grading_scheme_object = already_existing_data.first() - # grading_scheme_object.weightage = value - # grading_scheme_object.save() - # else: - # grading_scheme = GradingScheme.objects.create( - # course_id=course_id, - # type_of_evaluation=key, - # weightage=value - # ) - # grading_scheme.save() - - # data to be sent to gradingscheme_grades table - already_existing_data2 = GradingScheme_grades.objects.filter(course_id=course_id) - if already_existing_data2.exists(): - already_existing_data2.update( - course_id = course_id, - O_Lower = form_data['O_Lower'], - O_Upper = form_data['O_Upper'], - A_plus_Lower = form_data['A_plus_Lower'], - A_plus_Upper = form_data['A_plus_Upper'], - A_Lower = form_data['A_Lower'], - A_Upper = form_data['A_Upper'], - B_plus_Lower = form_data['B_plus_Lower'], - B_plus_Upper = form_data['B_plus_Upper'], - B_Lower = form_data['B_Lower'], - B_Upper = form_data['B_Upper'], - C_plus_Lower = form_data['C_plus_Lower'], - C_plus_Upper = form_data['C_plus_Upper'], - C_Lower = form_data['C_Lower'], - C_Upper = form_data['C_Upper'], - D_plus_Lower = form_data['D_plus_Lower'], - D_plus_Upper = form_data['D_plus_Upper'], - D_Lower = form_data['D_Lower'], - D_Upper = form_data['D_Upper'], - F_Lower = form_data['F_Lower'], - F_Upper = form_data['F_Upper'] - ) - else: - grading_scheme_grades = GradingScheme_grades.objects.create( - course_id = course_id, - O_Lower = form_data['O_Lower'], - O_Upper = form_data['O_Upper'], - A_plus_Lower = form_data['A_plus_Lower'], - A_plus_Upper = form_data['A_plus_Upper'], - A_Lower = form_data['A_Lower'], - A_Upper = form_data['A_Upper'], - B_plus_Lower = form_data['B_plus_Lower'], - B_plus_Upper = form_data['B_plus_Upper'], - B_Lower = form_data['B_Lower'], - B_Upper = form_data['B_Upper'], - C_plus_Lower = form_data['C_plus_Lower'], - C_plus_Upper = form_data['C_plus_Upper'], - C_Lower = form_data['C_Lower'], - C_Upper = form_data['C_Upper'], - D_plus_Lower = form_data['D_plus_Lower'], - D_plus_Upper = form_data['D_plus_Upper'], - D_Lower = form_data['D_Lower'], - D_Upper = form_data['D_Upper'], - F_Lower = form_data['F_Lower'], - F_Upper = form_data['F_Upper'] - ) - grading_scheme_grades.save() - course(request, course_code, version) - return HttpResponse("Upload successful.") - - -@login_required -def add_academic_calendar(request): - """ - to add an entry to the academic calendar to be uploaded - - @param: - request - contains metadata about the requested page. - - @variables: - from_date - The starting date for the academic calendar event. - to_date - The ending date for the academic caldendar event. - desc - Description for the academic calendar event. - c = object to save new event to the academic calendar. - """ - user = request.user - extrainfo = ExtraInfo.objects.select_related().get(user=user) - - if request.session.get('currentDesignationSelected') == 'acadadmin': - calendar = Calendar.objects.all() - context= { - 'academic_calendar' :calendar, - 'tab_id' :['4','1'] - } - if request.method == "POST": - try: - from_date = request.POST.getlist('from_date') - to_date = request.POST.getlist('to_date') - desc = request.POST.getlist('description')[0] - from_date = from_date[0].split('-') - from_date = [int(i) for i in from_date] - from_date = datetime.datetime(*from_date).date() - to_date = to_date[0].split('-') - to_date = [int(i) for i in to_date] - to_date = datetime.datetime(*to_date).date() - except Exception as e: - from_date="" - to_date="" - desc="" - pass - c = Calendar( - from_date=from_date, - to_date=to_date, - description=desc) - c.save() - HttpResponse("Calendar Added") - - return render(request, 'coursemanagement/academicinfo.html', context) - -@login_required -def update_calendar(request): - """ - to update an entry to the academic calendar to be updated. - - @param: - request - contains metadata about the requested page. - - @variables: - from_date - The starting date for the academic calendar event. - to_date - The ending date for the academic caldendar event. - desc - Description for the academic calendar event. - prev_desc - Description for the previous event which is to be updated. - get_calendar_details = Get the object of the calendar instance from the database for the previous Description. - - """ - user = request.user - extrainfo = ExtraInfo.objects.select_related().get(user=user) - - if request.session.get('currentDesignationSelected') == 'acadadmin': - calendar = Calendar.objects.all() - context= { - 'academic_calendar' :calendar, - 'tab_id' :['4','1'] - } - if request.method == "POST": - try: - from_date = request.POST.getlist('from_date') - to_date = request.POST.getlist('to_date') - desc = request.POST.getlist('description')[0] - prev_desc = request.POST.getlist('prev_desc')[0] - from_date = from_date[0].split('-') - from_date = [int(i) for i in from_date] - from_date = datetime.datetime(*from_date).date() - to_date = to_date[0].split('-') - to_date = [int(i) for i in to_date] - to_date = datetime.datetime(*to_date).date() - get_calendar_details = Calendar.objects.all().filter(description=prev_desc).first() - get_calendar_details.description = desc - get_calendar_details.from_date = from_date - get_calendar_details.to_date = to_date - get_calendar_details.save() - except Exception as e: - from_date="" - to_date="" - desc="" - return render(request, 'coursemanagement/academicinfo.html', context) - return render(request, 'coursemanagement/academicinfo.html', context) - -@login_required -def add_timetable(request): - """ - acad-admin can upload the time table(any type of) of the semester. - - @param: - request - contains metadata about the requested page. - - @variables: - acadTtForm - data of delete dictionary in post request - timetable - all timetable from database - """ - user = request.user - extrainfo = ExtraInfo.objects.select_related().get(user=user) - - if request.session.get('currentDesignationSelected') == 'acadadmin': - if request.method == 'POST': - try: - timetable = request.FILES.get('img') - programme = request.POST.get('programme') - batch = request.POST.get('batch') - branch = request.POST.get('branch') - filename, file_extenstion = os.path.splitext(request.FILES.get('img').name) - except: - return HttpResponse("Please Enter The Form Properly", status=400) - - full_path = settings.MEDIA_ROOT + "/Administrator/academic_information/" - url = settings.MEDIA_URL + filename - if not os.path.isdir(full_path): - cmd = "mkdir " + full_path - subprocess.call(cmd, shell=True) - fs = FileSystemStorage(full_path, url) - fs.save(filename+file_extenstion, timetable) - uploaded_file_url = "Administrator/academic_information/" + filename + file_extenstion - # uploaded_file_url = full_path + filename + file_extenstion - timetb = Timetable( - time_table = uploaded_file_url, - programme = programme, - batch = batch, - branch = branch - ) - timetb.save() - - return HttpResponse("Upload successful.") - else: - return HttpResponse("Not found.") - -@login_required -def delete_timetable(request): - """ - acad-admin can delete the outdated timetable from the server. - - @param: - request - contains metadata about the requested page. - - @variables: - data - data of delete dictionary in post request - t - Object of time table to be deleted - """ - user = request.user - extrainfo = ExtraInfo.objects.select_related().get(user=user) - - - if request.session.get('currentDesignationSelected') == 'acadadmin': - if request.method == "POST": - pk = request.POST.get('pk') - t = Timetable.objects.get(pk=pk) - t.delete() - return HttpResponse("TimeTable Deleted") - -@login_required -def submit_marks(request, course_code, version): - - user = request.user - extrainfo = ExtraInfo.objects.select_related().get(user=user) - course_id = Courses.objects.select_related().get(code=course_code, version=version) - if extrainfo.user_type == 'faculty': - if request.method == 'POST': - - form_data = {} - form_data = request.POST.copy() - del form_data['csrfmiddlewaretoken'] - year = datetime.datetime.now().year - - # print('number is ', int(len(form_data.getlist('stu_marks'))/3)) - for i in range(int(len(form_data.getlist('stu_marks'))/3)): - student = Student.objects.select_related().get(id=str(form_data.getlist('stu_marks')[(i*3)])) - batch = str(student.batch) - # print(batch) - already_existing_data = Student_grades.objects.filter(roll_no=str(form_data.getlist('stu_marks')[(i*3)])) - if already_existing_data.exists(): - already_existing_data.update( - semester = student.curr_semester_no, - year = year, - roll_no = str(form_data.getlist('stu_marks')[(i*3)]), - total_marks = (form_data.getlist('stu_marks')[(i*3+1)]), - grade = str(form_data.getlist('stu_marks')[(i*3+2)]), - batch = batch, - course_id = course_id, - ) - else: - student_grades = Student_grades.objects.create( - semester = student.curr_semester_no, - year = year, - roll_no = str(form_data.getlist('stu_marks')[(i*3)]), - total_marks = (form_data.getlist('stu_marks')[(i*3+1)]), - grade = str(form_data.getlist('stu_marks')[(i*3+2)]), - batch = batch, - course_id = course_id, - ) - student_grades.save() - return HttpResponse("Upload successful.") \ No newline at end of file + return HttpResponse("Feedback uploaded") diff --git a/FusionIIIT/applications/otheracademic/__init__.py b/FusionIIIT/applications/otheracademic/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/FusionIIIT/applications/otheracademic/admin.py b/FusionIIIT/applications/otheracademic/admin.py deleted file mode 100644 index 17991a755..000000000 --- a/FusionIIIT/applications/otheracademic/admin.py +++ /dev/null @@ -1,18 +0,0 @@ -from django.contrib import admin - -# Register your models here. - -from applications.otheracademic.models import GraduateSeminarFormTable,LeaveFormTable,BonafideFormTableUpdated,AssistantshipClaimFormStatusUpd,NoDues,LeavePG,LeavePGUpdTable - -admin.site.register(LeaveFormTable) - -admin.site.register(BonafideFormTableUpdated) -admin.site.register(GraduateSeminarFormTable) - -admin.site.register(AssistantshipClaimFormStatusUpd) - -admin.site.register(NoDues) -admin.site.register(LeavePGUpdTable) -admin.site.register(LeavePG) - - diff --git a/FusionIIIT/applications/otheracademic/apps.py b/FusionIIIT/applications/otheracademic/apps.py deleted file mode 100644 index 23aefd05a..000000000 --- a/FusionIIIT/applications/otheracademic/apps.py +++ /dev/null @@ -1,6 +0,0 @@ -from django.apps import AppConfig - - -class OtheracademicConfig(AppConfig): - - name = 'applications.otheracademic' diff --git a/FusionIIIT/applications/otheracademic/migrations/0001_initial.py b/FusionIIIT/applications/otheracademic/migrations/0001_initial.py deleted file mode 100644 index 1598f71a5..000000000 --- a/FusionIIIT/applications/otheracademic/migrations/0001_initial.py +++ /dev/null @@ -1,228 +0,0 @@ -# Generated by Django 3.1.5 on 2024-07-16 15:44 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - ('globals', '0001_initial'), - ] - - operations = [ - migrations.CreateModel( - name='GraduateSeminarFormTable', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('roll_no', models.CharField(max_length=20)), - ('semester', models.CharField(max_length=100)), - ('date_of_seminar', models.DateField()), - ], - options={ - 'db_table': 'GraduateSeminarFormTable', - }, - ), - migrations.CreateModel( - name='NoDues', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=100)), - ('library_clear', models.BooleanField(default=False)), - ('library_notclear', models.BooleanField(default=False)), - ('hostel_clear', models.BooleanField(default=False)), - ('hostel_notclear', models.BooleanField(default=False)), - ('mess_clear', models.BooleanField(default=False)), - ('mess_notclear', models.BooleanField(default=False)), - ('ece_clear', models.BooleanField(default=False)), - ('ece_notclear', models.BooleanField(default=False)), - ('physics_lab_clear', models.BooleanField(default=False)), - ('physics_lab_notclear', models.BooleanField(default=False)), - ('mechatronics_lab_clear', models.BooleanField(default=False)), - ('mechatronics_lab_notclear', models.BooleanField(default=False)), - ('cc_clear', models.BooleanField(default=False)), - ('cc_notclear', models.BooleanField(default=False)), - ('workshop_clear', models.BooleanField(default=False)), - ('workshop_notclear', models.BooleanField(default=False)), - ('signal_processing_lab_clear', models.BooleanField(default=False)), - ('signal_processing_lab_notclear', models.BooleanField(default=False)), - ('vlsi_clear', models.BooleanField(default=False)), - ('vlsi_notclear', models.BooleanField(default=False)), - ('design_studio_clear', models.BooleanField(default=False)), - ('design_studio_notclear', models.BooleanField(default=False)), - ('design_project_clear', models.BooleanField(default=False)), - ('design_project_notclear', models.BooleanField(default=False)), - ('bank_clear', models.BooleanField(default=False)), - ('bank_notclear', models.BooleanField(default=False)), - ('icard_dsa_clear', models.BooleanField(default=False)), - ('icard_dsa_notclear', models.BooleanField(default=False)), - ('account_clear', models.BooleanField(default=False)), - ('account_notclear', models.BooleanField(default=False)), - ('btp_supervisor_clear', models.BooleanField(default=False)), - ('btp_supervisor_notclear', models.BooleanField(default=False)), - ('discipline_office_clear', models.BooleanField(default=False)), - ('discipline_office_notclear', models.BooleanField(default=False)), - ('student_gymkhana_clear', models.BooleanField(default=False)), - ('student_gymkhana_notclear', models.BooleanField(default=False)), - ('alumni_clear', models.BooleanField(default=False)), - ('alumni_notclear', models.BooleanField(default=False)), - ('placement_cell_clear', models.BooleanField(default=False)), - ('placement_cell_notclear', models.BooleanField(default=False)), - ('hostel_credential', models.CharField(max_length=100)), - ('bank_credential', models.CharField(max_length=100)), - ('btp_credential', models.CharField(max_length=100)), - ('cse_credential', models.CharField(max_length=100)), - ('design_credential', models.CharField(max_length=100)), - ('acad_credential', models.CharField(max_length=100)), - ('ece_credential', models.CharField(max_length=100)), - ('library_credential', models.CharField(max_length=100)), - ('me_credential', models.CharField(max_length=100)), - ('mess_credential', models.CharField(max_length=100)), - ('physics_credential', models.CharField(max_length=100)), - ('discipline_credential', models.CharField(max_length=100)), - ('acad_admin_float', models.BooleanField(default=False)), - ('roll_no', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='globals.extrainfo')), - ], - options={ - 'db_table': 'NoDues', - }, - ), - migrations.CreateModel( - name='LeavePGUpdTable', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('student_name', models.CharField(max_length=100)), - ('programme', models.CharField(max_length=100)), - ('discipline', models.CharField(max_length=100)), - ('Semester', models.CharField(max_length=100)), - ('date_from', models.DateField()), - ('date_to', models.DateField()), - ('date_of_application', models.DateField()), - ('upload_file', models.FileField(upload_to='leave_doc')), - ('address', models.CharField(max_length=100)), - ('purpose', models.TextField()), - ('leave_type', models.CharField(choices=[('Casual', 'Casual'), ('Medical', 'Medical'), ('Vacation', 'Vacation'), ('Duty', 'Duty')], max_length=20)), - ('mobile_no', models.CharField(max_length=100)), - ('parent_mobile_no', models.CharField(max_length=100)), - ('alt_mobile_no', models.CharField(max_length=100)), - ('ta_approved', models.BooleanField()), - ('ta_rejected', models.BooleanField()), - ('hod_approved', models.BooleanField()), - ('hod_rejected', models.BooleanField()), - ('ta_supervisor', models.CharField(max_length=100)), - ('hod', models.CharField(max_length=100)), - ('roll_no', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='globals.extrainfo')), - ], - options={ - 'db_table': 'LeavePGUpdTable', - }, - ), - migrations.CreateModel( - name='LeavePG', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('student_name', models.CharField(max_length=100)), - ('programme', models.CharField(max_length=100)), - ('discipline', models.CharField(max_length=100)), - ('Semester', models.CharField(max_length=100)), - ('date_from', models.DateField()), - ('date_to', models.DateField()), - ('date_of_application', models.DateField()), - ('upload_file', models.FileField(blank=True, upload_to='')), - ('address', models.CharField(max_length=100)), - ('purpose', models.TextField()), - ('leave_type', models.CharField(choices=[('Casual', 'Casual'), ('Medical', 'Medical'), ('Vacation', 'Vacation'), ('Duty', 'Duty')], max_length=20)), - ('mobile_no', models.CharField(max_length=100)), - ('parent_mobile_no', models.CharField(max_length=100)), - ('alt_mobile_no', models.CharField(max_length=100)), - ('ta_approved', models.BooleanField()), - ('ta_rejected', models.BooleanField()), - ('thesis_approved', models.BooleanField()), - ('thesis_rejected', models.BooleanField()), - ('hod_approved', models.BooleanField()), - ('hod_rejected', models.BooleanField()), - ('ta_supervisor', models.CharField(max_length=100)), - ('thesis_supervisor', models.CharField(max_length=100)), - ('hod', models.CharField(max_length=100)), - ('roll_no', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='globals.extrainfo')), - ], - options={ - 'db_table': 'LeavePG', - }, - ), - migrations.CreateModel( - name='LeaveFormTable', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('student_name', models.CharField(max_length=100)), - ('date_from', models.DateField()), - ('date_to', models.DateField()), - ('date_of_application', models.DateField()), - ('upload_file', models.FileField(blank=True, upload_to='')), - ('address', models.CharField(max_length=100)), - ('purpose', models.TextField()), - ('leave_type', models.CharField(choices=[('Casual', 'Casual'), ('Medical', 'Medical')], max_length=20)), - ('approved', models.BooleanField()), - ('rejected', models.BooleanField()), - ('hod', models.CharField(max_length=100)), - ('roll_no', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='globals.extrainfo')), - ], - options={ - 'db_table': 'LeaveFormTable', - }, - ), - migrations.CreateModel( - name='BonafideFormTableUpdated', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('student_names', models.CharField(max_length=100)), - ('branch_types', models.CharField(max_length=50)), - ('semester_types', models.CharField(max_length=20)), - ('purposes', models.TextField()), - ('date_of_applications', models.DateField()), - ('approve', models.BooleanField()), - ('reject', models.BooleanField()), - ('download_file', models.FileField(default='not available', upload_to='Bonafide')), - ('roll_nos', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='globals.extrainfo')), - ], - options={ - 'db_table': 'BonafideFormTableUpdated', - }, - ), - migrations.CreateModel( - name='AssistantshipClaimFormStatusUpd', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('student_name', models.CharField(max_length=100)), - ('discipline', models.CharField(max_length=100)), - ('dateFrom', models.DateField()), - ('dateTo', models.DateField()), - ('bank_account', models.CharField(max_length=100)), - ('student_signature', models.FileField(upload_to='student_signatures/')), - ('dateApplied', models.DateField()), - ('ta_supervisor', models.CharField(max_length=100)), - ('thesis_supervisor', models.CharField(max_length=100)), - ('hod', models.CharField(max_length=100)), - ('applicability', models.CharField(max_length=100)), - ('TA_approved', models.BooleanField()), - ('TA_rejected', models.BooleanField()), - ('Ths_approved', models.BooleanField()), - ('Ths_rejected', models.BooleanField()), - ('HOD_approved', models.BooleanField()), - ('HOD_rejected', models.BooleanField()), - ('Acad_approved', models.BooleanField()), - ('Acad_rejected', models.BooleanField()), - ('amount', models.DecimalField(decimal_places=2, default=0, max_digits=10)), - ('rate', models.DecimalField(decimal_places=2, default=0, max_digits=10)), - ('half_day_leave', models.IntegerField(default=0)), - ('full_day_leave', models.IntegerField(default=0)), - ('remark', models.TextField(default='')), - ('roll_no', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='globals.extrainfo')), - ], - options={ - 'db_table': 'AssistantshipClaimFormStausUpd', - }, - ), - ] diff --git a/FusionIIIT/applications/otheracademic/migrations/__init__.py b/FusionIIIT/applications/otheracademic/migrations/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/FusionIIIT/applications/otheracademic/models.py b/FusionIIIT/applications/otheracademic/models.py deleted file mode 100644 index b361b849f..000000000 --- a/FusionIIIT/applications/otheracademic/models.py +++ /dev/null @@ -1,328 +0,0 @@ -from datetime import datetime - -from applications.globals.models import ExtraInfo, HoldsDesignation, Designation -from django.db import models -from django import forms -from django.contrib.auth.models import User -from applications.academic_information.models import Student -from django.core.exceptions import ValidationError - - -class LeaveFormTable(models.Model): - """ - Records information related to student leave requests. - - 'leave_from' and 'leave_to' store the start and end date of the leave request. - 'date_of_application' stores the date when the leave request was applied. - 'related_document' stores any related documents or notes for the leave request. - 'place' stores the location where the leave is requested. - 'reason' stores the reason for the leave request. - 'leave_type' stores the type of leave from a dropdown. - """ - LEAVE_TYPES = ( - ('Casual', 'Casual'), - ('Medical', 'Medical'), - - ) - - - student_name = models.CharField(max_length=100) - roll_no = models.ForeignKey(ExtraInfo, on_delete=models.CASCADE) - date_from = models.DateField() - date_to = models.DateField() - date_of_application = models.DateField() - upload_file = models.FileField(blank=True) - address = models.CharField(max_length=100) - purpose = models.TextField() - leave_type = models.CharField(max_length=20, choices=LEAVE_TYPES) - approved = models.BooleanField() - rejected = models.BooleanField() - hod= models.CharField(max_length=100) - - class Meta: - db_table='LeaveFormTable' - - -class LeavePG(models.Model): - """ - Records information related to student leave requests. - - 'leave_from' and 'leave_to' store the start and end date of the leave request. - 'date_of_application' stores the date when the leave request was applied. - 'related_document' stores any related documents or notes for the leave request. - 'place' stores the location where the leave is requested. - 'reason' stores the reason for the leave request. - 'leave_type' stores the type of leave from a dropdown. - """ - LEAVE_TYPES = ( - ('Casual', 'Casual'), - ('Medical', 'Medical'), - ('Vacation', 'Vacation'), - ('Duty', 'Duty') - - ) - - - - student_name = models.CharField(max_length=100) - roll_no = models.ForeignKey(ExtraInfo, on_delete=models.CASCADE) - programme = models.CharField(max_length=100) - discipline = models.CharField(max_length=100) - Semester = models.CharField(max_length=100) - date_from = models.DateField() - date_to = models.DateField() - date_of_application = models.DateField() - upload_file = models.FileField(blank=True) - address = models.CharField(max_length=100) - purpose = models.TextField() - leave_type = models.CharField(max_length=20, choices=LEAVE_TYPES) - ta_supervisor = models.CharField(max_length=100) - mobile_no = models.CharField(max_length=100) - parent_mobile_no = models.CharField(max_length=100) - alt_mobile_no = models.CharField(max_length=100) - ta_approved = models.BooleanField() - ta_rejected = models.BooleanField() - thesis_approved = models.BooleanField() - thesis_rejected = models.BooleanField() - hod_approved = models.BooleanField() - hod_rejected = models.BooleanField() - ta_supervisor=models.CharField(max_length=100) - thesis_supervisor=models.CharField(max_length=100) - hod=models.CharField(max_length=100) - - - class Meta: - db_table='LeavePG' - - - - -class LeavePGUpdTable(models.Model): - """ - Records information related to student leave requests. - - 'leave_from' and 'leave_to' store the start and end date of the leave request. - 'date_of_application' stores the date when the leave request was applied. - 'related_document' stores any related documents or notes for the leave request. - 'place' stores the location where the leave is requested. - 'reason' stores the reason for the leave request. - 'leave_type' stores the type of leave from a dropdown. - """ - LEAVE_TYPES = ( - ('Casual', 'Casual'), - ('Medical', 'Medical'), - ('Vacation', 'Vacation'), - ('Duty', 'Duty') - - ) - - - student_name = models.CharField(max_length=100) - roll_no = models.ForeignKey(ExtraInfo, on_delete=models.CASCADE) - programme = models.CharField(max_length=100) - discipline = models.CharField(max_length=100) - Semester = models.CharField(max_length=100) - date_from = models.DateField() - date_to = models.DateField() - date_of_application = models.DateField() - upload_file = models.FileField(upload_to='leave_doc') - address = models.CharField(max_length=100) - purpose = models.TextField() - leave_type = models.CharField(max_length=20, choices=LEAVE_TYPES) - ta_supervisor = models.CharField(max_length=100) - mobile_no = models.CharField(max_length=100) - parent_mobile_no = models.CharField(max_length=100) - alt_mobile_no = models.CharField(max_length=100) - ta_approved = models.BooleanField() - ta_rejected = models.BooleanField() - hod_approved = models.BooleanField() - hod_rejected = models.BooleanField() - ta_supervisor=models.CharField(max_length=100) - hod=models.CharField(max_length=100) - - - class Meta: - db_table='LeavePGUpdTable' - - - -class GraduateSeminarFormTable(models.Model): - - roll_no = models.CharField(max_length=20) - semester= models.CharField(max_length=100) - date_of_seminar = models.DateField() - - - class Meta: - db_table='GraduateSeminarFormTable' - - - -class BonafideFormTableUpdated(models.Model): - - - - student_names = models.CharField(max_length=100) - roll_nos = models.ForeignKey(ExtraInfo, on_delete=models.CASCADE) - branch_types = models.CharField(max_length=50) - semester_types = models.CharField(max_length=20) - purposes = models.TextField() - date_of_applications= models.DateField() - approve = models.BooleanField() - reject = models.BooleanField() - download_file = models.FileField(upload_to='Bonafide',default="not available") - - - - class Meta: - db_table='BonafideFormTableUpdated' - - - -# class AssistantshipClaimFormStatusUpd(models.Model): -# roll_no = models.ForeignKey(ExtraInfo, on_delete=models.CASCADE) -# student_name = models.CharField(max_length=100) -# discipline = models.CharField(max_length=100) -# dateFrom = models.DateField() -# dateTo = models.DateField() -# # month = models.CharField(max_length=50) -# # year = models.CharField(max_length=50) -# bank_account = models.CharField(max_length=100) -# student_signature = models.FileField(upload_to='student_signatures/') # Change to FileField -# dateApplied = models.DateField() -# ta_supervisor = models.CharField(max_length=100) -# thesis_supervisor = models.CharField(max_length=100) -# applicability = models.CharField(max_length=100) - -# TA_approved = models.BooleanField() -# TA_rejected = models.BooleanField() -# Ths_approved = models.BooleanField() -# Ths_rejected = models.BooleanField() -# HOD_approved = models.BooleanField() -# HOD_rejected = models.BooleanField() -# Acad_approved = models.BooleanField() -# Acad_rejected = models.BooleanField() - -# class Meta: -# db_table = 'AssistantshipClaimFormStausUpd' - - - - - - - -class AssistantshipClaimFormStatusUpd(models.Model): - roll_no = models.ForeignKey(ExtraInfo, on_delete=models.CASCADE) - student_name = models.CharField(max_length=100) - discipline = models.CharField(max_length=100) - dateFrom = models.DateField() - dateTo = models.DateField() - - bank_account = models.CharField(max_length=100) - student_signature = models.FileField(upload_to='student_signatures/') - dateApplied = models.DateField() - ta_supervisor = models.CharField(max_length=100) - thesis_supervisor = models.CharField(max_length=100) - hod = models.CharField(max_length=100) - applicability = models.CharField(max_length=100) - - TA_approved = models.BooleanField() - TA_rejected = models.BooleanField() - Ths_approved = models.BooleanField() - Ths_rejected = models.BooleanField() - HOD_approved = models.BooleanField() - HOD_rejected = models.BooleanField() - Acad_approved = models.BooleanField() - Acad_rejected = models.BooleanField() - - amount = models.DecimalField(max_digits=10, decimal_places=2, default=0) - rate = models.DecimalField(max_digits=10, decimal_places=2, default=0) - half_day_leave = models.IntegerField(default=0) - full_day_leave = models.IntegerField(default=0) - - remark = models.TextField(default='') # New field with an empty default value - - def clean(self): - start_date = self.cleaned_data['start_date'] - end_date = self.cleaned_data['end_date'] - - if end_date <= start_date: - raise forms.ValidationError("End date must be later than start date") - - return super(AssistantshipClaimFormStatusUpd, self).clean() - - class Meta: - db_table = 'AssistantshipClaimFormStausUpd' - - - - - - - -class NoDues(models.Model): - roll_no = models.ForeignKey(ExtraInfo, on_delete=models.CASCADE) - name = models.CharField(max_length=100) - - library_clear = models.BooleanField(default=False) - library_notclear = models.BooleanField(default=False) - hostel_clear = models.BooleanField(default=False) - hostel_notclear = models.BooleanField(default=False) - mess_clear = models.BooleanField(default=False) - mess_notclear = models.BooleanField(default=False) - ece_clear = models.BooleanField(default=False) - ece_notclear = models.BooleanField(default=False) - physics_lab_clear = models.BooleanField(default=False) - physics_lab_notclear = models.BooleanField(default=False) - mechatronics_lab_clear = models.BooleanField(default=False) - mechatronics_lab_notclear = models.BooleanField(default=False) - cc_clear = models.BooleanField(default=False) - cc_notclear = models.BooleanField(default=False) - workshop_clear = models.BooleanField(default=False) - workshop_notclear = models.BooleanField(default=False) - signal_processing_lab_clear = models.BooleanField(default=False) - signal_processing_lab_notclear = models.BooleanField(default=False) - vlsi_clear = models.BooleanField(default=False) - vlsi_notclear = models.BooleanField(default=False) - design_studio_clear = models.BooleanField(default=False) - design_studio_notclear = models.BooleanField(default=False) - design_project_clear = models.BooleanField(default=False) - design_project_notclear = models.BooleanField(default=False) - bank_clear = models.BooleanField(default=False) - bank_notclear = models.BooleanField(default=False) - icard_dsa_clear = models.BooleanField(default=False) - icard_dsa_notclear = models.BooleanField(default=False) - account_clear = models.BooleanField(default=False) - account_notclear = models.BooleanField(default=False) - btp_supervisor_clear = models.BooleanField(default=False) - btp_supervisor_notclear = models.BooleanField(default=False) - discipline_office_clear = models.BooleanField(default=False) - discipline_office_notclear = models.BooleanField(default=False) - student_gymkhana_clear = models.BooleanField(default=False) - student_gymkhana_notclear = models.BooleanField(default=False) - alumni_clear = models.BooleanField(default=False) - alumni_notclear = models.BooleanField(default=False) - placement_cell_clear = models.BooleanField(default=False) - placement_cell_notclear = models.BooleanField(default=False) - # discipline_office_dsa_clear=models.BooleanField(default=False) - # discipline_office_dsa_notclear=models.BooleanField(default=False) - - hostel_credential = models.CharField(max_length=100) - bank_credential = models.CharField(max_length=100) - btp_credential = models.CharField(max_length=100) - cse_credential = models.CharField(max_length=100) - design_credential = models.CharField(max_length=100) - acad_credential = models.CharField(max_length=100) - ece_credential = models.CharField(max_length=100) - library_credential = models.CharField(max_length=100) - me_credential = models.CharField(max_length=100) - mess_credential = models.CharField(max_length=100) - physics_credential = models.CharField(max_length=100) - discipline_credential = models.CharField(max_length=100) - - acad_admin_float = models.BooleanField(default=False) - - class Meta: - db_table = 'NoDues' - diff --git a/FusionIIIT/applications/otheracademic/tests.py b/FusionIIIT/applications/otheracademic/tests.py deleted file mode 100644 index 7ce503c2d..000000000 --- a/FusionIIIT/applications/otheracademic/tests.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.test import TestCase - -# Create your tests here. diff --git a/FusionIIIT/applications/otheracademic/urls.py b/FusionIIIT/applications/otheracademic/urls.py deleted file mode 100644 index 25cc72051..000000000 --- a/FusionIIIT/applications/otheracademic/urls.py +++ /dev/null @@ -1,213 +0,0 @@ -from django.conf.urls import url - -from . import views - -app_name = 'otheracademic' - -urlpatterns = [ - #UG Leave - url(r'^$', views.otheracademic, name='otheracademic'), - url(r'^leaveform/$', views.leaveform, name='leaveform'), - - url(r'^leave_form_submit/$', views.leave_form_submit, name='leave_form_submit'), - url(r'^leaveApproveForm/$', views.leaveApproveForm, name='leaveApproveForm'), - url(r'^leaveStatus/$', views.leaveStatus, name='leaveStatus'), - - url(r'^leaveStatus_Dip/$', views.leaveStatus_Dip, name='leaveStatus_Dip'), - - url(r'^approve_leave/(?P\d+)/$', views.approve_leave, name='approve_leave'), - url(r'^reject_leave/(?P\d+)/$', views.reject_leave, name='reject_leave'), - - #PG/Mtech Leave - url(r'^leavePG/$', views.leavePG, name='leavePG'), - url(r'^leavePgSubmit/$', views.leavePgSubmit, name='leavePgSubmit'), - url(r'^leaveApproveTA/$', views.leaveApproveTA, name='leaveApproveTA'), - url(r'^approve_leave_ta(?P\d+)/$', views.approve_leave_ta, name='approve_leave_ta'), - url(r'^reject_leave_ta(?P\d+)/$', views.reject_leave_ta, name='reject_leave_ta'), - url(r'^leaveApproveThesis/$', views.leaveApproveThesis, name='leaveApproveThesis'), - url(r'^approve_leave_thesis(?P\d+)/$', views.approve_leave_thesis, name='approve_leave_thesis'), - url(r'^reject_leave_thesis(?P\d+)/$', views.reject_leave_thesis, name='reject_leave_thesis'), - url(r'^leaveApproveHOD/$', views.leaveApproveHOD, name='leaveApproveHOD'), - url(r'^approve_leave_hod(?P\d+)/$', views.approve_leave_hod, name='approve_leave_hod'), - url(r'^reject_leave_hod(?P\d+)/$', views.reject_leave_hod, name='reject_leave_hod'), - url(r'^leaveStatusPG/$', views.leaveStatusPG, name='leaveStatusPG'), - url(r'^leaveStatusPG_Dip/$', views.leaveStatusPG_Dip, name='leaveStatusPG_Dip'), - - url(r'^graduateseminar/$', views.graduateseminar, name='graduateseminar'), - url(r'^graduate_form_submit/$', views.graduate_form_submit, name='graduate_form_submit'), - url(r'^graduate_status/$', views.graduate_status, name='graduate_status'), - url(r'^graduateSeminarStatus_Dip/$', views.graduateSeminarStatus_Dip, name='graduateSeminarStatus_Dip'), - url(r'^bonafide/$', views.bonafide, name='bonafide'), - url(r'^bonafide_form_submit/$', views.bonafide_form_submit, name='bonafide_form_submit'), - url(r'^bonafideApproveForm/$', views.bonafideApproveForm, name='bonafideApproveForm'), - url(r'^approve_bonafide/(?P\d+)/$', views.approve_bonafide, name='approve_bonafide'), - url(r'^reject_bonafide/(?P\d+)/$', views.reject_bonafide, name='reject_bonafide'), - url(r'^bonafideStatus/$', views.bonafideStatus, name='bonafideStatus'), - url(r'^upload_file/(?P\d+)/$', views.upload_file, name='upload_file'), - # url(r'^assistantship/$', views.assistantship, name='assistantship'), - url(r'^submitform/$', views.assistantship_form_submission, name='assistantship_form_submission'), - # url(r'^approveform/$', views.assistantship_form_approval, name='assistantship_approval'), - # url(r'^noduesverification/$', views.nodues, name='nodues'), - url(r'^PG_page/$', views.PG_page, name='PG_page'), - - url(r'^noduesverification/$', views.nodues, name='nodues'), - - url(r'^Bank_nodues/$', views.Bank_nodues, name='Bank_nodues'), - url(r'^BTP_nodues/$', views.BTP_nodues, name='BTP_nodues'), - url(r'^CSE_nodues/$', views.CSE_nodues, name='CSE_nodues'), - url(r'^Design_nodues/$', views.Design_nodues, name='Design_nodues'), - url(r'^dsa_nodues/$', views.dsa_nodues, name='dsa_nodues'), - url(r'^Ece_nodues/$', views.Ece_nodues, name='Ece_nodues'), - url(r'^hostel_nodues/$', views.hostel_nodues, name='hostel_nodues'), - url(r'^ME_nodues/$', views.ME_nodues, name='ME_nodues'), - url(r'^library_nodues/$', views.library_nodues, name='library_nodues'), - url(r'^mess_nodues/$', views.mess_nodues, name='mess_nodues'), - url(r'^Physics_nodues/$', views.Physics_nodues, name='Physics_nodues'), - url(r'^discipline_nodues/$', views.discipline_nodues, name='discipline_nodues'), - - url(r'^nodues_apply/$', views.nodues_apply, name='nodues_apply'), - url(r'^nodues_status/$', views.nodues_status, name='nodues_status'), - - url(r'^update_dues_status/$', views.update_dues_status, name='update_dues_status'), - url(r'^submit_nodues_form/$', views.submit_nodues_form, name='submit_nodues_form'), - url(r'^nodues_apply/$', views.nodues_apply, name='nodues_apply'), - - - url(r'^approve_BTP(?P\d+)/$', views.approve_BTP, name='approve_BTP'), - url(r'^reject_BTP(?P\d+)/$', views.reject_BTP, name='reject_BTP'), - - url(r'^approve_bank(?P\d+)/$', views.approve_bank, name='approve_bank'), - url(r'^reject_bank(?P\d+)/$', views.reject_bank, name='reject_bank'), - - url(r'^approve_CSE(?P\d+)/$', views.approve_CSE, name='approve_CSE'), - url(r'^reject_CSE(?P\d+)/$', views.reject_CSE, name='reject_CSE'), - - url(r'^approve_design_project(?P\d+)/$', views.approve_design_project, name='approve_design_project'), - url(r'^reject_design-project(?P\d+)/$', views.reject_design_project, name='reject_design_project'), - - url(r'^approve_design_studio(?P\d+)/$', views.approve_design_studio, name='approve_design_studio'), - url(r'^reject_design_studio(?P\d+)/$', views.reject_design_studio, name='reject_design_studio'), - - url(r'^approve_icard(?P\d+)/$', views.approve_icard, name='approve_icard'), - url(r'^reject_icard(?P\d+)/$', views.reject_icard, name='reject_icard'), - - url(r'^approve_placement(?P\d+)/$', views.approve_placement, name='approve_placement'), - url(r'^reject_placement(?P\d+)/$', views.reject_placement, name='reject_placement'), - - url(r'^approve_account(?P\d+)/$', views.approve_account, name='approve_account'), - url(r'^reject_account(?P\d+)/$', views.reject_account, name='reject_account'), - - url(r'^approve_alumni(?P\d+)/$', views.approve_alumni, name='approve_alumni'), - url(r'^reject_alumni(?P\d+)/$', views.reject_alumni, name='reject_alumni'), - - url(r'^approve_gym(?P\d+)/$', views.approve_gym, name='approve_gym'), - url(r'^reject_gym(?P\d+)/$', views.reject_gym, name='reject_gym'), - - url(r'^approve_discipline(?P\d+)/$', views.approve_discipline, name='approve_discipline'), - url(r'^reject_discipline(?P\d+)/$', views.reject_discipline, name='reject_discipline'), - - url(r'^approve_signal(?P\d+)/$', views.approve_signal, name='approve_signal'), - url(r'^reject_signal(?P\d+)/$', views.reject_signal, name='reject_signal'), - - url(r'^approve_vlsi(?P\d+)/$', views.approve_vlsi, name='approve_vlsi'), - url(r'^reject_vlsi(?P\d+)/$', views.reject_vlsi, name='reject_vlsi'), - - url(r'^approve_ece(?P\d+)/$', views.approve_ece, name='approve_ece'), - url(r'^reject_ece(?P\d+)/$', views.reject_ece, name='reject_ece'), - - url(r'^approve_hostel(?P\d+)/$', views.approve_hostel, name='approve_hostel'), - url(r'^reject_hostel(?P\d+)/$', views.reject_hostel, name='reject_hostel'), - - url(r'^approve_library(?P\d+)/$', views.approve_library, name='approve_library'), - url(r'^reject_library(?P\d+)/$', views.reject_library, name='reject_library'), - - url(r'^approve_workshop(?P\d+)/$', views.approve_workshop, name='approve_workshop'), - url(r'^reject_workshop(?P\d+)/$', views.reject_workshop, name='reject_workshop'), - - url(r'^approve_mecha(?P\d+)/$', views.approve_mecha, name='approve_mecha'), - url(r'^reject_mecha(?P\d+)/$', views.reject_mecha, name='reject_mecha'), - - url(r'^approve_mess(?P\d+)/$', views.approve_mess, name='approve_mess'), - url(r'^reject_mess(?P\d+)/$', views.reject_mess, name='reject_mess'), - - url(r'^approve_physics(?P\d+)/$', views.approve_physics, name='approve_physics'), - url(r'^reject_physics(?P\d+)/$', views.reject_physics, name='reject_physics'), - - - - - url(r'^approve_BTP_not(?P\d+)/$', views.approve_BTP_not, name='approve_BTP_not'), - url(r'^approve_bank_not(?P\d+)/$', views.approve_bank_not, name='approve_bank_not'), - url(r'^approve_CSE_not(?P\d+)/$', views.approve_CSE_not, name='approve_CSE_not'), - url(r'^approve_design_project_not(?P\d+)/$', views.approve_design_project_not, name='approve_design_project_not'), - url(r'^approve_design_studio_not(?P\d+)/$', views.approve_design_studio_not, name='approve_design_studio_not'), - url(r'^approve_icard_not(?P\d+)/$', views.approve_icard_not, name='approve_icard_not'), - url(r'^approve_placement_not(?P\d+)/$', views.approve_placement_not, name='approve_placement_not'), - url(r'^approve_account_not(?P\d+)/$', views.approve_account_not, name='approve_account_not'), - url(r'^approve_alumni_not(?P\d+)/$', views.approve_alumni_not, name='approve_alumni_not'), - url(r'^approve_gym_not(?P\d+)/$', views.approve_gym_not, name='approve_gym_not'), - url(r'^approve_discipline_not(?P\d+)/$', views.approve_discipline_not, name='approve_discipline_not'), - url(r'^approve_signal_not(?P\d+)/$', views.approve_signal_not, name='approve_signal_not'), - url(r'^approve_vlsi_not(?P\d+)/$', views.approve_vlsi_not, name='approve_vlsi_not'), - url(r'^approve_ece_not(?P\d+)/$', views.approve_ece_not, name='approve_ece_not'), - url(r'^approve_hostel_not(?P\d+)/$', views.approve_hostel_not, name='approve_hostel_not'), - url(r'^approve_library_not(?P\d+)/$', views.approve_library_not, name='approve_library_not'), - url(r'^approve_workshop_not(?P\d+)/$', views.approve_workshop_not, name='approve_workshop_not'), - url(r'^approve_mecha_not(?P\d+)/$', views.approve_mecha_not, name='approve_mecha_not'), - url(r'^approve_mess_not(?P\d+)/$', views.approve_mess_not, name='approve_mess_not'), - url(r'^approve_physics_not(?P\d+)/$', views.approve_physics_not, name='approve_physics_not'), - - - - url(r'^Bank_nodues_not/$', views.Bank_nodues_not, name='Bank_nodues_not'), - url(r'^BTP_nodues_not/$', views.BTP_nodues_not, name='BTP_nodues_not'), - url(r'^CSE_nodues_not/$', views.CSE_nodues_not, name='CSE_nodues_not'), - url(r'^Design_nodues_not/$', views.Design_nodues_not, name='Design_nodues_not'), - url(r'^dsa_nodues_not/$', views.dsa_nodues_not, name='dsa_nodues_not'), - url(r'^Ece_nodues_not/$', views.Ece_nodues_not, name='Ece_nodues_not'), - url(r'^hostel_nodues_not/$', views.hostel_nodues_not, name='hostel_nodues_not'), - url(r'^ME_nodues_not/$', views.ME_nodues_not, name='ME_nodues_not'), - url(r'^library_nodues_not/$', views.library_nodues_not, name='library_nodues_not'), - url(r'^mess_nodues_not/$', views.mess_nodues_not, name='mess_nodues_not'), - url(r'^Physics_nodues_not/$', views.Physics_nodues_not, name='Physics_nodues_not'), - url(r'^discipline_nodues_not/$', views.discipline_nodues_not, name='discipline_nodues_not'), - - - - url(r'^noduesStatus_acad/$', views.noduesStatus_acad, name='noduesStatus_acad'), - - - url(r'^assistantship/$', views.assistantship, name='assistantship'), - url(r'^submitform/$', views.assistantship_form_submission, name='assistantship_form_submission'), - # url(r'^approveform/$', views.assistantship_form_approval, name='assistantship_approval'), - - url(r'^approveform/$', views.assistantship_form_approval, name='assistantship_form_approval'), - url(r'^assitantship/thesis_approveform/$', views.assistantship_thesis, name='assistantship_thesis'), - url(r'^assitantship/hod_approveform/$', views.assistantship_hod, name='assistantship_hod'), - url(r'^assitantship/acad_approveform/$', views.assistantship_acad_approveform, name='assistantship_acad_approveform'), - url(r'^assistantship_status/$', views.assistantship_status, name='assistantship_status'), - url(r'^assistantship_log/$', views.assistantship_log, name='assistantship_log'), - - # url(r'^noduesverification/$', views.nodues, name='nodues'), - - url(r'^assistanship_ta_approve(?P\d+)/$', views.assistanship_ta_approve, name='assistanship_ta_approve'), - url(r'^assistanship_ta_reject(?P\d+)/$', views.assistanship_ta_reject, name='assistanship_ta_reject'), - url(r'^assistanship_thesis_approve(?P\d+)/$', views.assistanship_thesis_approve, name='assistanship_thesis_approve'), - url(r'^assistanship_thesis_reject(?P\d+)/$', views.assistanship_thesis_reject, name='assistanship_thesis_reject'), - url(r'^assistanship_hod_approve(?P\d+)/$', views.assistanship_hod_approve, name='assistanship_hod_approve'), - url(r'^assistanship_hod_reject(?P\d+)/$', views.assistanship_hod_reject, name='assistanship_hod_reject'), - url(r'^assistanship_acad_approve(?P\d+)/$', views.assistanship_acad_approve, name='assistanship_acad_approve'), - url(r'^assistanship_acad_reject(?P\d+)/$', views.assistanship_acad_reject, name='assistanship_acad_reject'), - - - url(r'^othersPage/$', views.othersPage, name='othersPage'), - - url(r'^othersLeave/$', views.othersLeave, name='othersLeave'), - url(r'^othersGraduate/$', views.othersGraduate, name='othersGraduate'), - url(r'^othersAssistantship/$', views.othersAssistantship, name='othersAssistanship'), - url(r'^othersNoDues/$', views.othersNoDues, name='othersNoDues'), - - - -] - diff --git a/FusionIIIT/applications/otheracademic/views.py b/FusionIIIT/applications/otheracademic/views.py deleted file mode 100644 index b099f2f78..000000000 --- a/FusionIIIT/applications/otheracademic/views.py +++ /dev/null @@ -1,2312 +0,0 @@ - -from django.shortcuts import render -from django.contrib import messages -from django.shortcuts import render, get_object_or_404, redirect,render - -from applications.globals.models import ExtraInfo, HoldsDesignation, Designation -from django.core import serializers -from django.template.defaulttags import csrf_token -from django.http import HttpResponse, HttpResponseRedirect, JsonResponse -from django.contrib.auth.decorators import login_required -from django.db import IntegrityError -from django.core import serializers -from django.contrib.auth.models import User -from timeit import default_timer as time -from notification.views import office_module_notif,file_tracking_notif -from django.http import JsonResponse -from django.views.decorators.csrf import csrf_exempt -from .models import LeaveFormTable,BonafideFormTableUpdated,GraduateSeminarFormTable,AssistantshipClaimFormStatusUpd,LeavePG,NoDues -from django.shortcuts import render, get_object_or_404 -from datetime import date -from applications.filetracking.models import * -from applications.filetracking.sdk.methods import * - -from notification.views import otheracademic_notif - - - -@login_required -def otheracademic(request): - """ - - - Description: - This function checks the user's programme (B.Tech, M.Tech, or PhD) to determine whether they are an undergraduate or postgraduate student. It then renders the corresponding academic page accordingly. - - If the user is an undergraduate student (B.Tech), the function renders the UG_page.html template. - - If the user is a postgraduate student (M.Tech or PhD), the function renders the PG_page.html template. - """ - if request.user.extrainfo.user_type != "student": - return render(request, "otheracademic/othersPage.html") - else : - user = get_object_or_404(User, username=request.user.username) - if user.extrainfo.student.programme == "B.Tech": - return render(request, "otheracademic/UG_page.html") - elif user.extrainfo.student.programme == "M.Tech" or user.extrainfo.student.programme == "PhD": - return render(request, "otheracademic/PG_page.html") - else: - return HttpResponse(request,"NOt Available For you") - -@login_required -def leaveform(request): - """ - View function for accessing the leave form page. - - """ - return render(request, 'otheracademic/leaveform.html') - - -@csrf_exempt # Exempt CSRF verification for this view -@login_required -def leave_form_submit(request): - """ - View function for submitting a leave form. - - Description: - This function handles form submission for leave requests, processes the data, and saves it to the database. - It also notifies the relevant authority about the new leave application. - """ - if request.method == 'POST': - # Extract data from the request - data = request.POST - file = request.FILES.get('related_document') - hodname = data.get('hod_credential') - - # Create a new LeaveFormTable instance and save it to the database - leave = LeaveFormTable.objects.create( - student_name=request.user.first_name+request.user.last_name, - roll_no=request.user.extrainfo, - date_from=data.get('date_from'), - date_to=data.get('date_to'), - leave_type=data.get('leave_type'), - upload_file=file, - address=data.get('address'), - purpose=data.get('purpose'), - date_of_application=date.today(), - approved=False, # Initially not approved - rejected=False, # Initially not rejected - hod=data.get('hod_credential') - ) - - leave_hod = User.objects.get(username=hodname) - receiver_value = User.objects.get(username=request.user.username) - receiver_value_designation = HoldsDesignation.objects.filter(user=receiver_value) - lis = list(receiver_value_designation) - obj = lis[0].designation - - - - file_id = create_file( - uploader=request.user.username, - uploader_designation=obj, - receiver=leave_hod, - receiver_designation="student", - src_module="otheracademic", - src_object_id=leave.id, - file_extra_JSON={"value": 2}, - attached_file=None, - subject='ug_leave' - ) - - message = "A new leave application" - otheracademic_notif(request.user, leave_hod, 'ug_leave_hod', leave.id, 'student', message) - if leave: - messages.success(request, "You successfully submitted your form") - - return HttpResponseRedirect('/otheracademic/leaveform') - - -def leaveApproveForm(request): - """ - View function for accessing the leave approval form. - - Description: - This function retrieves leave requests for approval by the designated authority (e.g., HOD) and displays them in a list. - """ - user=get_object_or_404(User,username=request.user.username) - design=request.session['currentDesignationSelected'] - if 'HOD' in design : - inbox = view_inbox(username=request.user.username, designation="student", src_module="otheracademic") - leave_ids = [msg['src_object_id'] for msg in inbox if msg['subject'] == 'ug_leave'] - - form_data = LeaveFormTable.objects.filter(id__in=leave_ids) - return render(request, 'otheracademic/leaveformreciever.html', {'form_data': form_data}) - else: - return HttpResponse("Not available for you or You are not a HOD.") - - -def leaveStatus(request): - """ - View function for accessing the leave status page for the student. - - Description: - This function retrieves and displays the leave status of the currently logged-in student. - """ - form_data = LeaveFormTable.objects.filter(roll_no=request.user.extrainfo) - roll_no = request.user.username - return render(request, 'otheracademic/leaveStatus.html', {'form_data': form_data, 'roll_no' : roll_no}) - - -def leaveStatus_Dip(request): - """ - View function for track the record of leave applied. - - - """ - inbox = view_inbox(username=request.user.username, designation="student", src_module="otheracademic") - leave_ids = [msg['src_object_id'] for msg in inbox if msg['subject'] == 'ug_leave'] - - form_data = LeaveFormTable.objects.filter(id__in=leave_ids) - return render(request, 'otheracademic/leaveStatus_Dip.html', {'form_data': form_data}) - -@login_required -def approve_leave(request, leave_id): - """ - View function for approving a leave request. - - Parameters: - leave_id (int): The ID of the leave request to be approved. - - Description: - This function approves the leave request with the specified ID and updates its status accordingly. - """ - leave_entry = LeaveFormTable.objects.get(id=leave_id) - leave_entry.approved = True - leave_entry.save() - messages.success(request, "Successfully Approved") - leave_receive = User.objects.get(username=leave_entry.roll_no_id) - message='Leave Application Status approve/rejected' - otheracademic_notif(request.user,leave_receive, 'ug_leave_hod_approve', leave_entry.id, 'student', message) - - - - return redirect('/otheracademic/leaveApproveForm') - - -def reject_leave(request, leave_id): - """ - View function for rejecting a leave request. - - Parameters: - leave_id (int): The ID of the leave request to be rejected. - - Description: - This function rejects the leave request with the specified ID and updates its status accordingly. - """ - leave_entry = LeaveFormTable.objects.get(id=leave_id) - leave_entry.rejected = True - leave_entry.save() - messages.success(request, "Successfully Rejected") - leave_receive = User.objects.get(username=leave_entry.roll_no_id) - message='Leave Application Status approve/rejected' - otheracademic_notif(request.user,leave_receive, 'ug_leave_hod_approve', leave_entry.id, 'student', message) - - return redirect('/otheracademic/leaveApproveForm') - - - - - -# PG/MTECh -@login_required -def leavePG(request): - """ - View function for accessing the leave page for postgraduate students. - - Description: - This function checks if the logged-in user is a postgraduate student (M.Tech or PhD). If so, it renders the leavePG.html template, allowing them to apply for leave. - If the user is not a postgraduate student, it returns an "NOT AVAILABLE" message. - """ - user = get_object_or_404(User, username=request.user.username) - if user.extrainfo.student.programme == "M.Tech" or "PhD": - return render(request, 'otheracademic/leavePG.html') - else: - return HttpResponse("NOT AVAILABLE") - - -def leavePgSubmit(request): - """ - View function for submitting a leave form by postgraduate students. - - Description: - This function handles form submission for leave requests by postgraduate students. - It processes the data, saves it to the database, notifies the TA supervisor about the new leave application, - and redirects the user to the leave form page. - - -In this function hod is charfield ,basically it ask the credential in charfied but by using user.get.object() it return the foreign key of hod and after that we can proceed furthur. - this is used to overcome the problem of database data - """ - if request.method == 'POST': - data = request.POST - file = request.FILES.get('related_document') - hodname = data.get('hod_credential') - ta = data.get('ta_supervisor') - thesis = data.get('thesis_credential') - - leave = LeavePG.objects.create( - student_name=request.user.first_name+request.user.last_name, - roll_no=request.user.extrainfo, - programme=request.user.extrainfo.student.programme, - discipline=request.user.extrainfo.department, - Semester=data.get('Semester'), - date_from=data.get('date_from'), - date_to=data.get('date_to'), - date_of_application=date.today(), - upload_file=file, - address=data.get('address'), - purpose=data.get('purpose'), - leave_type=data.get('leave_type'), - ta_supervisor=data.get('ta_supervisor'), - mobile_no=data.get('mobile_no'), - parent_mobile_no=data.get('parent_mobile_no'), - alt_mobile_no=data.get('alt_mobile_no'), - ta_approved=False, - ta_rejected=False, - thesis_approved=False, - thesis_rejected=False, - hod_approved=False, - hod_rejected=False, - hod=hodname, - thesis_supervisor = thesis, - ) - - tasupervisor = User.objects.get(username=ta) - receiver_value = User.objects.get(username=request.user.username) - receiver_value_designation = HoldsDesignation.objects.filter(user=receiver_value) - lis = list(receiver_value_designation) - obj = lis[0].designation - - file_id = create_file( - uploader=request.user.username, - uploader_designation=obj, - receiver=tasupervisor, - receiver_designation="student", - src_module="otheracademic", - src_object_id=leave.id, - file_extra_JSON={"value": 2}, - attached_file=None, - subject='pg_leave' - ) - - message = "A new leave application" - otheracademic_notif(request.user, tasupervisor,'pg_leave_ta', leave.id, 'student', message) - if leave: - messages.success(request, "You have successfully submitted your form") - - return HttpResponseRedirect('/otheracademic/leavePG') - - -def leaveApproveTA(request): - """ - View function for accessing the leave approval page for TA supervisors. - - Description: - This function retrieves leave requests for approval by the TA supervisor and displays them in a list. - """ - inbox = view_inbox(username=request.user.username, designation="student", src_module="otheracademic") - leave_ids = [msg['src_object_id'] for msg in inbox if msg['subject'] == 'pg_leave'] - - form_data = LeavePG.objects.filter(id__in=leave_ids) - roll_no = request.user.username - return render(request, 'otheracademic/leaveApproveTA.html', {'form_data': form_data,'roll_no' : roll_no}) - - -def approve_leave_ta(request, leave_id): - """ - View function for approving a leave request by TA supervisor. - - Description: - This function approves the leave request with the specified ID by TA supervisor and updates its status accordingly and forwarded to hod. - """ - leave_entry = get_object_or_404(LeavePG, id=leave_id) - leave_entry.ta_approved = True - leave_entry.save() - leave_receive = User.objects.get(username=leave_entry.roll_no_id) - message='Leave Application Status approve/rejected' - otheracademic_notif(request.user,leave_receive, 'pg_leave_ta_approve', leave_entry.id, 'student', message) - - - - receiver_value = User.objects.get(username=request.user.username) - receiver_value_designation = HoldsDesignation.objects.filter(user=receiver_value) - lis = list(receiver_value_designation) - obj = lis[0].designation - - - file_id_forward = create_file( - uploader=request.user.username, - uploader_designation=obj, - receiver=leave_entry.thesis_supervisor, - receiver_designation="student", - src_module="otheracademic", - src_object_id=leave_id, - file_extra_JSON={"value": 2}, - attached_file=None, - subject='pg_leave' - ) - - message = "A new leave application forwarded of PG student" - thesis_user = User.objects.get(username=leave_entry.thesis_supervisor) - otheracademic_notif(request.user, thesis_user, 'pg_leave_thesis', leave_id, 'student', message) - - - return redirect('/otheracademic/leaveApproveTA') # Redirect to appropriate page after approval - - -def reject_leave_ta(request, leave_id): - """ - View function for rejecting a leave request by TA supervisor. - - Description: - This function rejects the leave request with the specified ID by TA supervisor and updates its status accordingly. - """ - leave_entry = LeavePG.objects.get(id=leave_id) - leave_entry.ta_rejected = True - leave_entry.save() - leave_receive = User.objects.get(username=leave_entry.roll_no_id) - message='Leave Application Status approve/rejected' - otheracademic_notif(request.user,leave_receive, 'pg_leave_ta_approve', leave_entry.id, 'student', message) - - - return redirect('/otheracademic/leaveApproveTA') # Redirect to appropriate page after rejection - -def leaveApproveThesis(request): - """ - View function for accessing the leave approval page for TA supervisors. - - Description: - This function retrieves leave requests for approval by the TA supervisor and displays them in a list. - """ - inbox = view_inbox(username=request.user.username, designation="student", src_module="otheracademic") - leave_ids = [msg['src_object_id'] for msg in inbox if msg['subject'] == 'pg_leave'] - - form_data = LeavePG.objects.filter(id__in=leave_ids) - roll_no = request.user.username - return render(request, 'otheracademic/leaveApproveThesis.html', {'form_data': form_data, 'roll_no' : roll_no}) - - -def approve_leave_thesis(request, leave_id): - """ - View function for approving a leave request by TA supervisor. - - Description: - This function approves the leave request with the specified ID by TA supervisor and updates its status accordingly and forwarded to hod. - """ - leave_entry = LeavePG.objects.get(id=leave_id) - leave_entry.thesis_approved = True - leave_entry.save() - - leave_receive = User.objects.get(username=leave_entry.roll_no_id) - message='Leave Application Status approve/rejected' - otheracademic_notif(request.user,leave_receive, 'pg_leave_ta_approve', leave_entry.id, 'student', message) - - - receiver_value = User.objects.get(username=request.user.username) - receiver_value_designation = HoldsDesignation.objects.filter(user=receiver_value) - lis = list(receiver_value_designation) - obj = lis[0].designation - leave_entry = get_object_or_404(LeavePG, id=leave_id) - - file_id_forward = create_file( - uploader=request.user.username, - uploader_designation=obj, - receiver=leave_entry.hod, - receiver_designation="student", - src_module="otheracademic", - src_object_id=leave_id, - file_extra_JSON={"value": 2}, - attached_file=None, - subject='pg_leave' - ) - message = "A new leave application" - hod_user = User.objects.get(username=leave_entry.hod) - otheracademic_notif(request.user, hod_user, 'pg_leave_hod', leave_id, 'student', message) - - - return redirect('/otheracademic/leaveApproveThesis') # Redirect to appropriate page after approval - - -def reject_leave_thesis(request, leave_id): - """ - View function for rejecting a leave request by TA supervisor. - - Description: - This function rejects the leave request with the specified ID by TA supervisor and updates its status accordingly. - """ - leave_entry = LeavePG.objects.get(id=leave_id) - leave_entry.thesis_rejected = True - leave_entry.save() - - - return redirect('/otheracademic/leaveApproveThesis') # Redirect to appropriate page after rejection - - -def leaveApproveHOD(request): - """ - View function for accessing the leave approval page for HOD. - - Description: - This function retrieves leave requests for approval by the HOD and displays them in a list. - """ - user=get_object_or_404(User,username=request.user.username) - design=request.session['currentDesignationSelected'] - if 'HOD' in design : - inbox = view_inbox(username=request.user.username, designation="student", src_module="otheracademic") - leave_ids = [msg['src_object_id'] for msg in inbox if msg['subject'] == 'pg_leave'] - form_data = LeavePG.objects.filter(id__in=leave_ids) - roll_no = request.user.username - return render(request, 'otheracademic/leaveApproveHOD.html', {'form_data': form_data, 'roll_no' : roll_no}) - else: - return HttpResponse("Not Avaible For You...OR You are not the HOD") - -def approve_leave_hod(request, leave_id): - """ - View function for approving a leave request by HOD. - - Description: - This function approves the leave request with the specified ID by HOD and updates its status accordingly. - """ - leave_entry = LeavePG.objects.get(id=leave_id) - leave_entry.hod_approved = True - leave_entry.save() - leave_receive = User.objects.get(username=leave_entry.roll_no_id) - message='Leave Application Status approve/rejected' - otheracademic_notif(request.user,leave_receive, 'pg_leave_ta_approve', leave_entry.id, 'student', message) - - - return redirect('/otheracademic/leaveApproveHOD') # Redirect to appropriate page after approval - - -def reject_leave_hod(request, leave_id): - """ - View function for rejecting a leave request by HOD. - - Description: - This function rejects the leave request with the specified ID by HOD and updates its status accordingly. - """ - leave_entry = LeavePG.objects.get(id=leave_id) - leave_entry.hod_rejected = True - - leave_entry.save() - - return redirect('/otheracademic/leaveApproveHOD') # Redirect to appropriate page after rejection - - -def leaveStatusPG(request): - """ - View function for accessing the leave status page for postgraduate students. - - Description: - This function retrieves and displays the leave status of postgraduate students. - """ - form_data = LeavePG.objects.all() - roll_no = request.user.username - return render(request, 'otheracademic/leaveStatusPG.html', {'form_data': form_data, 'roll_no': roll_no}) - - -def leaveStatusPG_Dip(request): - """ - View function for accessing the leave logged data of PG . - - Description: - This function retrieves and displays the leave logged data of PG leave for future reference. - """ - inbox = view_inbox(username=request.user.username, designation="student", src_module="otheracademic") - leave_ids = [msg['src_object_id'] for msg in inbox if msg['subject'] == 'pg_leave'] - - form_data = LeavePG.objects.filter(id__in=leave_ids) - return render(request, 'otheracademic/leaveStatusPG_Dip.html', {'form_data': form_data}) - - - - - - - - -def graduateseminar(request): - """ - This function is used to log the graduate seminar form and show the status. - - """ - user=get_object_or_404(User,username=request.user.username) - design=request.session['currentDesignationSelected'] - if 'deptadmin' in design : - return render(request,'otheracademic/graduateseminarForm.html') - else : - return HttpResponse("Not Available for You") - -def graduate_form_submit(request): - if request.method == 'POST': - # Extract data from the request - data = request.POST - # file = request.FILES.get('related_document') - - - # Create a new LeaveFormTable instance and save it to the database - graduate=GraduateSeminarFormTable.objects.create( - - roll_no=data.get('roll_no'), - semester=data.get('semester'), - date_of_seminar=data.get('date_of_seminar'), - - ) - graduate.save() - return redirect('/otheracademic/graduateseminar') - -def graduate_status(request): - - form_data = GraduateSeminarFormTable.objects.all() - roll_no = request.user.username - return render(request, 'otheracademic/graduateSeminarStatus.html',{'form_data' : form_data, 'roll_no' : roll_no }) - -def graduateSeminarStatus_Dip(request): - receiver_value = User.objects.get(username=request.user.username) - receiver_value_designation= HoldsDesignation.objects.filter(user=receiver_value) - lis = list(receiver_value_designation) - obj=lis[0].designation - - - if obj : - form_data = GraduateSeminarFormTable.objects.all() - return render(request, 'otheracademic/graduateSeminarStatus_Dip.html',{'form_data' : form_data }) - else : - return HttpResponse("Not Available") - - - - - - - - - - -def bonafide(request): - """ - This function is used for solve the problem of Bonafied.In this Student apply for the bonafide . - - - - - - """ - return render(request,'otheracademic/bonafideForm.html') - - - - - -def bonafide_form_submit(request): - """ - Bonafide form submitted to acadadmin - """ - if request.method == 'POST': - # Extract data from the request - data = request.POST - file = request.FILES.get('related_document') - - - # Create a new LeaveFormTable instance and save it to the database - bonafide=BonafideFormTableUpdated.objects.create( - student_names=request.user.first_name+request.user.last_name, - roll_nos=request.user.extrainfo, - branch_types = data.get('branch'), - semester_types = data.get('semester'), - purposes = data.get('purpose'), - date_of_applications = data.get('date_of_application'), - approve=False, # Initially not approved - reject=False, # Initially not rejected - download_file = "not available", - ) - messages.success(request,'form submitted successfully') - bonafide.save() - acad_admin_des_id = Designation.objects.get(name="acadadmin") - user_ids = HoldsDesignation.objects.filter(designation_id=acad_admin_des_id.id).values_list('user_id', flat=True) - # print(user_ids) - # print(user_ids[0]) - # acad_admins = ExtraInfo.objects.get(user_id=user_ids[0]) - # # print(acad_admins) - # user=ExtraInfo.objects.get(pk=acad_admins.id) - bonafide_receiver = User.objects.get(id=user_ids[0]) - message='A Bonafide applicationn received' - otheracademic_notif(request.user,bonafide_receiver, 'bonafide', 1, 'student', message) - return HttpResponseRedirect('/otheracademic/bonafide') - - -def bonafideApproveForm(request): - - """ - Bonafide form approveform where it got option to accept and reject the application - """ - user=get_object_or_404(User,username=request.user.username) - design=request.session['currentDesignationSelected'] - - if 'acadadmin' in design : - form_data = BonafideFormTableUpdated.objects.all() - return render(request, 'otheracademic/bonafideApprove.html', {'form_data': form_data}) - else: - return HttpResponse("Not available For You") - -def approve_bonafide(request, leave_id): - - """ - Approve Bonafide form - """ - file = request.FILES.get('related_document') - leave_entry = BonafideFormTableUpdated.objects.get(id=leave_id) - leave_entry.approve = True - leave_entry.save() - bonafide_aceptor = User.objects.get(username=leave_entry.roll_nos_id) - message='A Bonafide uploaded' - otheracademic_notif(request.user,bonafide_aceptor, 'bonafide_accept', 1, 'student', message) - return redirect('/otheracademic/bonafideApproveForm') # Redirect to appropriate page after approval - -def reject_bonafide(request, leave_id): - """ - Reject Bonafide Form - """ - leave_entry = BonafideFormTableUpdated.objects.get(id=leave_id) - leave_entry.reject = True - leave_entry.save() - bonafide_aceptor = User.objects.get(username=leave_entry.roll_no_id) - message='A Bonafide rejected' - otheracademic_notif(request.user,bonafide_aceptor, 'bonafide_accept', 1, 'student', message) - return redirect('/otheracademic/bonafideApproveForm') # Redirect to appropriate page after rejection - -def bonafideStatus(request): - """ - Bonafide Status shown to student with option of download - """ - - form_data = BonafideFormTableUpdated.objects.all() - roll_no = request.user.username - return render(request, 'otheracademic/bonafideStatus.html',{'form_data' : form_data, 'roll_no' : roll_no }) - - -# views.py - - - - -def upload_file(request, entry_id): - - """ - Bonafide uploaded by acadadmin - """ - if request.method == 'POST' and request.FILES.get('related_document'): - related_document = request.FILES['related_document'] - - # Assuming you want to update the 'download_file' field of your model - bonafide_entry = BonafideFormTableUpdated.objects.get(id=entry_id) - bonafide_entry.download_file = related_document - bonafide_entry.save() - - return HttpResponse("File Uploaded Successfuly") - else: - return HttpResponse("No File Attached") - - - - - -def nodues(request): - """ - No Dues Form where student can apply for no dues - """ - return render(request,'otheracademic/noduesverification.html') - - -def PG_page(request): - return render(request,'otheracademic/PG_page.html') - - -def nodues_status(request): - form_data = NoDues.objects.all() - roll_no = request.user.username - print(form_data) - return render(request, 'otheracademic/nodues_status.html',{'form_data' : form_data, 'roll_no' : roll_no }) - - -def noduesStatus_acad(request): - form_data = NoDues.objects.all() - return render(request, 'otheracademic/noduesStatus_acad.html',{'form_data' : form_data }) - - -def nodues_apply(request): - form_data = NoDues.objects.all() - roll_no = request.user.username - return render(request,'otheracademic/nodues_apply.html', {'form_data' : form_data, 'roll_no' : roll_no}) - - - -def update_dues_status(request): - if request.method == 'POST': - roll_no = request.POST.get('roll_no') - clear = request.POST.get('clear') # 'true' if clear, 'false' if not clear - # Convert clear to boolean - clear = clear.lower() == 'true' - - # Update clearance status in the database - try: - student = NoDues.objects.get(roll_no=roll_no) - if clear: - student.clear_status = True - student.not_clear_status = False - else: - student.clear_status = False - student.not_clear_status = True - student.save() - return JsonResponse({'message': 'Clearance status updated successfully'}, status=200) - except NoDues.DoesNotExist: - return JsonResponse({'error': 'Student not found'}, status=404) - else: - return JsonResponse({'error': 'Invalid request method'}, status=405) - - - - -@csrf_exempt # Exempt CSRF verification for this view -def submit_nodues_form(request): - - """ - Submit the No Dues form for a student - description:- - - There are many actors in this problem like authory of:- - library, - mess, - hostel, - Physics Lab, - ECE Lab, - Computer Centre, - mechatronics_lab, - Vlsi, - Design, - Bank, - BTP, - Placement cell etc....... - - Students apply this form and this forwarded to all this actor to approve/reject - - - There is also example Bank_nodues_not for those student whose in first time application is rejected and when they go to second time it will be approved by them. - - - """ - if request.method == 'POST': - # Extract data from the request - data = request.POST - hostel_credential = data.get('hostel_credential'), - bank_credential = data.get('bank_credential'), - btp_credential = data.get('btp_credential'), - cse_credential = data.get('cse_credential'), - design_credential = data.get('design_credential'), - acad_credential = data.get('acad_credential'), - ece_credential = data.get('ece_credential'), - library_credential = data.get('library_credential'), - me_credential = data.get('me_credential'), - mess_credential = data.get('mess_credential'), - physics_credential = data.get('physics_credential'), - discipline_credential = data.get('discipline_credential'), - - - - nodues=NoDues.objects.create( - roll_no=request.user.extrainfo, - name=data.get('name'), - library_clear=False, - library_notclear=False, - hostel_clear=False, - hostel_notclear=False, - mess_clear=False, - mess_notclear=False, - ece_clear=False, - ece_notclear=False, - physics_lab_clear=False, - physics_lab_notclear=False, - mechatronics_lab_clear=False, - mechatronics_lab_notclear=False, - cc_clear=False, - cc_notclear=False, - workshop_clear=False, - workshop_notclear=False, - signal_processing_lab_clear=False, - signal_processing_lab_notclear=False, - vlsi_clear=False, - vlsi_notclear=False, - design_studio_clear=False, - design_studio_notclear=False, - design_project_clear=False, - design_project_notclear=False, - bank_clear=False, - bank_notclear=False, - icard_dsa_clear=False, - icard_dsa_notclear=False, - account_clear=False, - account_notclear=False, - btp_supervisor_clear=False, - btp_supervisor_notclear=False, - discipline_office_clear=False, - discipline_office_notclear=False, - student_gymkhana_clear=False, - student_gymkhana_notclear=False, - # discipline_office_dsa_clear=False, - # discipline_office_dsa_notclear=False, - alumni_clear=False, - alumni_notclear=False, - placement_cell_clear=False, - placement_cell_notclear=False, - - hostel_credential = data.get('hostel_credential'), - bank_credential = data.get('bank_credential'), - btp_credential = data.get('btp_credential'), - cse_credential = data.get('cse_credential'), - design_credential = data.get('design_credential'), - acad_credential = data.get('acad_credential'), - ece_credential = data.get('ece_credential'), - library_credential = data.get('library_credential'), - me_credential = data.get('me_credential'), - mess_credential = data.get('mess_credential'), - physics_credential = data.get('physics_credential'), - discipline_credential = data.get('discipline_credential'), - - acad_admin_float = False, - - ) - hostel_receiver=User.objects.get(username=data.get('hostel_credential')) - - receiver_value = User.objects.get(username=request.user.username) - receiver_value_designation = HoldsDesignation.objects.filter(user=receiver_value) - lis = list(receiver_value_designation) - obj = lis[0].designation - file_hostel = create_file( - uploader=request.user.username, - uploader_designation=obj, - receiver=hostel_receiver, - receiver_designation="student", - src_module="otheracademic", - src_object_id=nodues.id, - file_extra_JSON={"value": 2}, - attached_file=None, - subject='no_dues' - ) - - message = "A new hostel application of no dues" - otheracademic_notif(request.user,hostel_receiver, 'hostel_nodues', nodues.id, 'student', message) - - - #bnk_receiver - bank_receiver=User.objects.get(username=data.get('bank_credential')) - file_bank= create_file( - uploader=request.user.username, - uploader_designation=obj, - receiver=bank_receiver, - receiver_designation="student", - src_module="otheracademic", - src_object_id=nodues.id, - file_extra_JSON={"value": 2}, - attached_file=None, - subject='no_dues' - ) - - message = "A new bank application of no dues" - otheracademic_notif(request.user,bank_receiver, 'bank_nodues', nodues.id, 'student', message) - - #btp_receiver - btp_receiver=User.objects.get(username=data.get('btp_credential')) - file_bank= create_file( - uploader=request.user.username, - uploader_designation=obj, - receiver=btp_receiver, - receiver_designation="student", - src_module="otheracademic", - src_object_id=nodues.id, - file_extra_JSON={"value": 2}, - attached_file=None, - subject='no_dues' - ) - - message = "A new btp application of no dues" - otheracademic_notif(request.user,btp_receiver, 'btp_nodues', nodues.id, 'student', message) - - #cse_receiver - cse_receiver=User.objects.get(username=data.get('cse_credential')) - file_bank= create_file( - uploader=request.user.username, - uploader_designation=obj, - receiver=bank_receiver, - receiver_designation="student", - src_module="otheracademic", - src_object_id=nodues.id, - file_extra_JSON={"value": 2}, - attached_file=None, - subject='no_dues' - ) - - message = "A new cse application of no dues" - otheracademic_notif(request.user,cse_receiver, 'cse_nodues', nodues.id, 'student', message) - #design_receiver - design_receiver=User.objects.get(username=data.get('design_credential')) - file_bank= create_file( - uploader=request.user.username, - uploader_designation=obj, - receiver=design_receiver, - receiver_designation="student", - src_module="otheracademic", - src_object_id=nodues.id, - file_extra_JSON={"value": 2}, - attached_file=None, - subject='no_dues' - ) - - message = "A new design application of no dues" - otheracademic_notif(request.user,design_receiver, 'design_nodues', nodues.id, 'student', message) - - #acad_receiver - acad_receiver=User.objects.get(username=data.get('acad_credential')) - file_bank= create_file( - uploader=request.user.username, - uploader_designation=obj, - receiver=acad_receiver, - receiver_designation="student", - src_module="otheracademic", - src_object_id=nodues.id, - file_extra_JSON={"value": 2}, - attached_file=None, - subject='no_dues' - ) - - message = "A new acad application of no dues" - otheracademic_notif(request.user,acad_receiver, 'acad_nodues', nodues.id, 'student', message) - - #ece_receiver - ece_receiver=User.objects.get(username=data.get('ece_credential')) - file_bank= create_file( - uploader=request.user.username, - uploader_designation=obj, - receiver=ece_receiver, - receiver_designation="student", - src_module="otheracademic", - src_object_id=nodues.id, - file_extra_JSON={"value": 2}, - attached_file=None, - subject='no_dues' - ) - - message = "A new ece application of no dues" - otheracademic_notif(request.user,ece_receiver, 'ece_nodues', nodues.id, 'student', message) - - #library_receiver - library_receiver=User.objects.get(username=data.get('library_credential')) - file_bank= create_file( - uploader=request.user.username, - uploader_designation=obj, - receiver=library_receiver, - receiver_designation="student", - src_module="otheracademic", - src_object_id=nodues.id, - file_extra_JSON={"value": 2}, - attached_file=None, - subject='no_dues' - ) - - message = "A new library application of no dues" - otheracademic_notif(request.user,library_receiver, 'library_nodues', nodues.id, 'student', message) - # me_receiver - me_receiver=User.objects.get(username=data.get('me_credential')) - file_bank= create_file( - uploader=request.user.username, - uploader_designation=obj, - receiver=me_receiver, - receiver_designation="student", - src_module="otheracademic", - src_object_id=nodues.id, - file_extra_JSON={"value": 2}, - attached_file=None, - subject='no_dues' - ) - - message = "A new ME application of no dues" - otheracademic_notif(request.user,me_receiver, 'me_nodues', nodues.id, 'student', message) - - #mess_receiver - mess_receiver=User.objects.get(username=data.get('mess_credential')) - file_bank= create_file( - uploader=request.user.username, - uploader_designation=obj, - receiver=mess_receiver, - receiver_designation="student", - src_module="otheracademic", - src_object_id=nodues.id, - file_extra_JSON={"value": 2}, - attached_file=None, - subject='no_dues' - ) - - message = "A new mess application of no dues" - otheracademic_notif(request.user,mess_receiver, 'mess_nodues', nodues.id, 'student', message) - - #physics_receiver - physics_receiver=User.objects.get(username=data.get('physics_credential')) - file_bank= create_file( - uploader=request.user.username, - uploader_designation=obj, - receiver=physics_receiver, - receiver_designation="student", - src_module="otheracademic", - src_object_id=nodues.id, - file_extra_JSON={"value": 2}, - attached_file=None, - subject='no_dues' - ) - - message = "A new physics application of no dues" - otheracademic_notif(request.user,physics_receiver, 'physics_nodues', nodues.id, 'student', message) - - - #discipline_receiver - discipline_receiver=User.objects.get(username=data.get('discipline_credential')) - file_bank= create_file( - uploader=request.user.username, - uploader_designation=obj, - receiver=discipline_receiver, - receiver_designation="student", - src_module="otheracademic", - src_object_id=nodues.id, - file_extra_JSON={"value": 2}, - attached_file=None, - subject='no_dues' - ) - - message = "A new discipline application of no dues" - otheracademic_notif(request.user,discipline_receiver, 'discipline_nodues', nodues.id, 'student', message) - messages.success(request,'You successfully applied for no_dues') - - - - # print(roll_no_id) - return HttpResponseRedirect('/otheracademic/nodues_apply') - - - - -def approve_BTP(request, no_dues_id): - leave_entry = NoDues.objects.get(id=no_dues_id) - leave_entry.btp_supervisor_clear= True - leave_entry.btp_supervisor_notclear = False - btp_receiver=User.objects.get(username=leave_entry.roll_no_id) - message = "Btp nodues approved" - otheracademic_notif(request.user,btp_receiver , 'nodues_approve', leave_entry.id, "student", message) - - # Display success message - messages.success(request, "Successfully approved and forwarded.") - - leave_entry.save() - return redirect('/otheracademic/BTP_nodues') # Red - -def approve_BTP_not(request, no_dues_id): - leave_entry = NoDues.objects.get(id=no_dues_id) - leave_entry.btp_supervisor_clear= True - leave_entry.btp_supervisor_notclear = False - leave_entry.save() - return redirect('/otheracademic/BTP_nodues_not') # Red - - - -def reject_BTP(request, no_dues_id): - leave_entry = NoDues.objects.get(id=no_dues_id) - leave_entry.btp_supervisor_clear= False - leave_entry.btp_supervisor_notclear = True - nodues_receiver=User.objects.get(username=leave_entry.roll_no_id) - message = "Btp nodues rejected" - otheracademic_notif(request.user,nodues_receiver , 'nodues_status', leave_entry.id, "student", message) - leave_entry.save() - return redirect('/otheracademic/BTP_nodues') # Red - - - - - -def approve_bank(request, no_dues_id): - leave_entry = NoDues.objects.get(id=no_dues_id) - leave_entry.bank_clear= True - leave_entry.bank_notclear = False - nodues_receiver=User.objects.get(username=leave_entry.roll_no_id) - message = "bank nodues approved" - otheracademic_notif(request.user,nodues_receiver , 'nodues_status', leave_entry.id, "student", message) - leave_entry.save() - return redirect('/otheracademic/Bank_nodues') # Red - -def approve_bank_not(request, no_dues_id): - leave_entry = NoDues.objects.get(id=no_dues_id) - leave_entry.bank_clear= True - leave_entry.bank_notclear = False - leave_entry.save() - return redirect('/otheracademic/Bank_nodues_not') # Red - - - -def reject_bank(request, no_dues_id): - leave_entry = NoDues.objects.get(id=no_dues_id) - leave_entry.bank_clear= False - leave_entry.bank_notclear = True - nodues_receiver=User.objects.get(username=leave_entry.roll_no_id) - message = "bank nodues rejected" - otheracademic_notif(request.user,nodues_receiver , 'nodues_status', leave_entry.id, "student", message) - - leave_entry.save() - return redirect('/otheracademic/Bank_nodues') # Red - -def approve_CSE(request, no_dues_id): - leave_entry = NoDues.objects.get(id=no_dues_id) - leave_entry.cc_clear= True - leave_entry.cc_notclear = False - nodues_receiver=User.objects.get(username=leave_entry.roll_no_id) - message = "Cse nodues approved" - otheracademic_notif(request.user,nodues_receiver , 'nodues_status', leave_entry.id, "student", message) - leave_entry.save() - return redirect('/otheracademic/CSE_nodues') # Red - -def approve_CSE_not(request, no_dues_id): - leave_entry = NoDues.objects.get(id=no_dues_id) - leave_entry.cc_clear= True - leave_entry.cc_notclear = False - leave_entry.save() - return redirect('/otheracademic/CSE_nodues_not') # Red - - - -def reject_CSE(request, no_dues_id): - leave_entry = NoDues.objects.get(id=no_dues_id) - leave_entry.cc_clear= False - leave_entry.cc_notclear = True - nodues_receiver=User.objects.get(username=leave_entry.roll_no_id) - message = "Cse nodues rejected" - otheracademic_notif(request.user,nodues_receiver , 'nodues_status', leave_entry.id, "student", message) - leave_entry.save() - return redirect('/otheracademic/CSE_nodues') # Red - -def approve_design_project(request, no_dues_id): - leave_entry = NoDues.objects.get(id=no_dues_id) - leave_entry.design_project_clear= True - leave_entry.design_project_notclear = False - nodues_receiver=User.objects.get(username=leave_entry.roll_no_id) - message = "Design project nodues approved" - otheracademic_notif(request.user,nodues_receiver , 'nodues_status', leave_entry.id, "student", message) - leave_entry.save() - return redirect('/otheracademic/Design_nodues') # Red - -def approve_design_project_not(request, no_dues_id): - leave_entry = NoDues.objects.get(id=no_dues_id) - leave_entry.design_project_clear= True - leave_entry.design_project_notclear = False - leave_entry.save() - return redirect('/otheracademic/Design_nodues_not') # Red - - - -def reject_design_project(request, no_dues_id): - leave_entry = NoDues.objects.get(id=no_dues_id) - leave_entry.design_project_clear= False - leave_entry.design_project_notclear = True - nodues_receiver=User.objects.get(username=leave_entry.roll_no_id) - message = "Design project nodues rejected" - otheracademic_notif(request.user,nodues_receiver , 'nodues_status', leave_entry.id, "student", message) - leave_entry.save() - return redirect('/otheracademic/Design_nodues') # Red - -def approve_design_studio(request, no_dues_id): - leave_entry = NoDues.objects.get(id=no_dues_id) - leave_entry.design_studio_clear= True - leave_entry.design_studio_notclear = False - nodues_receiver=User.objects.get(username=leave_entry.roll_no_id) - message = "Design studio nodues approved" - otheracademic_notif(request.user,nodues_receiver , 'nodues_status', leave_entry.id, "student", message) - leave_entry.save() - return redirect('/otheracademic/Design_nodues') # Red - -def approve_design_studio_not(request, no_dues_id): - leave_entry = NoDues.objects.get(id=no_dues_id) - leave_entry.design_studio_clear= True - leave_entry.design_studio_notclear = False - leave_entry.save() - return redirect('/otheracademic/Design_nodues_not') # Red - -def reject_design_studio(request, no_dues_id): - leave_entry = NoDues.objects.get(id=no_dues_id) - leave_entry.design_studio_clear= False - leave_entry.design_studio_notclear = True - nodues_receiver=User.objects.get(username=leave_entry.roll_no_id) - message = "Design studio nodues rejected" - otheracademic_notif(request.user,nodues_receiver , 'nodues_status', leave_entry.id, "student", message) - leave_entry.save() - return redirect('/otheracademic/Design_nodues') # Red - - - -def approve_icard(request, no_dues_id): - leave_entry = NoDues.objects.get(id=no_dues_id) - leave_entry.icard_dsa_clear= True - leave_entry.icard_dsa_notclear = False - nodues_receiver=User.objects.get(username=leave_entry.roll_no_id) - message = "icard nodues approved" - otheracademic_notif(request.user,nodues_receiver , 'nodues_status', leave_entry.id, "student", message) - leave_entry.save() - return redirect('/otheracademic/dsa_nodues') # Red - -def approve_icard_not(request, no_dues_id): - leave_entry = NoDues.objects.get(id=no_dues_id) - leave_entry.icard_dsa_clear= True - leave_entry.icard_dsa_notclear = False - leave_entry.save() - return redirect('/otheracademic/dsa_nodues_not') # Red - - - -def reject_icard(request, no_dues_id): - leave_entry = NoDues.objects.get(id=no_dues_id) - leave_entry.icard_dsa_clear= False - leave_entry.icard_dsa_notclear = True - nodues_receiver=User.objects.get(username=leave_entry.roll_no_id) - message = "icard nodues rejected" - otheracademic_notif(request.user,nodues_receiver , 'nodues_status', leave_entry.id, "student", message) - leave_entry.save() - return redirect('/otheracademic/dsa_nodues') # Red - -def approve_placement(request, no_dues_id): - leave_entry = NoDues.objects.get(id=no_dues_id) - leave_entry.placement_cell_clear= True - leave_entry.placement_cell_notclear = False - nodues_receiver=User.objects.get(username=leave_entry.roll_no_id) - message = "placement nodues approved" - otheracademic_notif(request.user,nodues_receiver , 'nodues_status', leave_entry.id, "student", message) - leave_entry.save() - return redirect('/otheracademic/dsa_nodues') # Red - -def approve_placement_not(request, no_dues_id): - leave_entry = NoDues.objects.get(id=no_dues_id) - leave_entry.placement_cell_clear= True - leave_entry.placement_cell_notclear = False - leave_entry.save() - return redirect('/otheracademic/dsa_nodues_not') # Red - - - -def reject_placement(request, no_dues_id): - leave_entry = NoDues.objects.get(id=no_dues_id) - leave_entry.placement_cell_clear= False - leave_entry.placement_cell_notclear = True - nodues_receiver=User.objects.get(username=leave_entry.roll_no_id) - message = "placement nodues rejected" - otheracademic_notif(request.user,nodues_receiver , 'nodues_status', leave_entry.id, "student", message) - leave_entry.save() - return redirect('/otheracademic/dsa_nodues') # Red - -def approve_account(request, no_dues_id): - leave_entry = NoDues.objects.get(id=no_dues_id) - leave_entry.account_clear= True - leave_entry.account_notclear = False - nodues_receiver=User.objects.get(username=leave_entry.roll_no_id) - message = "account nodues approved" - otheracademic_notif(request.user,nodues_receiver , 'nodues_status', leave_entry.id, "student", message) - leave_entry.save() - return redirect('/otheracademic/Bank_nodues') # Red - -def approve_account_not(request, no_dues_id): - leave_entry = NoDues.objects.get(id=no_dues_id) - leave_entry.account_clear= True - leave_entry.account_notclear = False - leave_entry.save() - return redirect('/otheracademic/Bank_nodues_not') # Red - - - -def reject_account(request, no_dues_id): - leave_entry = NoDues.objects.get(id=no_dues_id) - leave_entry.account_clear= False - leave_entry.account_notclear = True - nodues_receiver=User.objects.get(username=leave_entry.roll_no_id) - message = "account nodues rejected" - otheracademic_notif(request.user,nodues_receiver , 'nodues_status', leave_entry.id, "student", message) - leave_entry.save() - return redirect('/otheracademic/Bank_nodues') # Red - -def approve_alumni(request, no_dues_id): - leave_entry = NoDues.objects.get(id=no_dues_id) - leave_entry.alumni_clear= True - leave_entry.alumni_notclear = False - nodues_receiver=User.objects.get(username=leave_entry.roll_no_id) - message = "alumni nodues approved" - otheracademic_notif(request.user,nodues_receiver , 'nodues_status', leave_entry.id, "student", message) - leave_entry.save() - return redirect('/otheracademic/dsa_nodues') # Realumni - -def approve_alumni_not(request, no_dues_id): - leave_entry = NoDues.objects.get(id=no_dues_id) - leave_entry.alumni_clear= True - leave_entry.alumni_notclear = False - leave_entry.save() - return redirect('/otheracademic/dsa_nodues_not') # Realumni - - - -def reject_alumni(request, no_dues_id): - leave_entry = NoDues.objects.get(id=no_dues_id) - leave_entry.alumni_clear= False - leave_entry.alumni_notclear = True - nodues_receiver=User.objects.get(username=leave_entry.roll_no_id) - message = "alumni nodues rejected" - otheracademic_notif(request.user,nodues_receiver , 'nodues_status', leave_entry.id, "student", message) - leave_entry.save() - return redirect('/otheracademic/dsa_nodues') # Red - -def approve_gym(request, no_dues_id): - leave_entry = NoDues.objects.get(id=no_dues_id) - leave_entry.student_gymkhana_clear= True - leave_entry.student_gymkhana_notclear = False - nodues_receiver=User.objects.get(username=leave_entry.roll_no_id) - message = "gym nodues approved" - otheracademic_notif(request.user,nodues_receiver , 'nodues_status', leave_entry.id, "student", message) - leave_entry.save() - return redirect('/otheracademic/dsa_nodues') # Red - -def approve_gym_not(request, no_dues_id): - leave_entry = NoDues.objects.get(id=no_dues_id) - leave_entry.student_gymkhana_clear= True - leave_entry.student_gymkhana_notclear = False - leave_entry.save() - return redirect('/otheracademic/dsa_nodues_not') # Red - - - -def reject_gym(request, no_dues_id): - leave_entry = NoDues.objects.get(id=no_dues_id) - leave_entry.student_gymkhana_clear= False - leave_entry.student_gymkhana_notclear = True - nodues_receiver=User.objects.get(username=leave_entry.roll_no_id) - message = "gym nodues rejected" - otheracademic_notif(request.user,nodues_receiver , 'nodues_status', leave_entry.id, "student", message) - leave_entry.save() - return redirect('/otheracademic/dsa_nodues') # Red - -def approve_discipline(request, no_dues_id): - leave_entry = NoDues.objects.get(id=no_dues_id) - leave_entry.discipline_office_clear= True - leave_entry.discipline_office_notclear = False - nodues_receiver=User.objects.get(username=leave_entry.roll_no_id) - message = "Discipline office nodues approved" - otheracademic_notif(request.user,nodues_receiver , 'nodues_status', leave_entry.id, "student", message) - leave_entry.save() - return redirect('/otheracademic/discipline_nodues') # Red - -def approve_discipline_not(request, no_dues_id): - leave_entry = NoDues.objects.get(id=no_dues_id) - leave_entry.discipline_office_clear= True - leave_entry.discipline_office_notclear = False - leave_entry.save() - return redirect('/otheracademic/discipline_nodues_not') # Red - - - -def reject_discipline(request, no_dues_id): - leave_entry = NoDues.objects.get(id=no_dues_id) - leave_entry.discipline_office_clear= False - leave_entry.discipline_office_notclear = True - nodues_receiver=User.objects.get(username=leave_entry.roll_no_id) - message = "Discipline office nodues rejected" - otheracademic_notif(request.user,nodues_receiver , 'nodues_status', leave_entry.id, "student", message) - leave_entry.save() - return redirect('/otheracademic/dsa_nodues') # Red - -def approve_signal(request, no_dues_id): - leave_entry = NoDues.objects.get(id=no_dues_id) - leave_entry.signal_processing_lab_clear= True - leave_entry.signal_processing_lab_notclear = False - nodues_receiver=User.objects.get(username=leave_entry.roll_no_id) - message = "signal nodues approved" - otheracademic_notif(request.user,nodues_receiver , 'nodues_status', leave_entry.id, "student", message) - leave_entry.save() - return redirect('/otheracademic/Ece_nodues') # Realumni - -def approve_signal_not(request, no_dues_id): - leave_entry = NoDues.objects.get(id=no_dues_id) - leave_entry.signal_processing_lab_clear= True - leave_entry.signal_processing_lab_notclear = False - leave_entry.save() - return redirect('/otheracademic/Ece_nodues_not') # Realumni - - - -def reject_signal(request, no_dues_id): - leave_entry = NoDues.objects.get(id=no_dues_id) - leave_entry.signal_processing_lab_clear= False - leave_entry.signal_processing_lab_notclear = True - nodues_receiver=User.objects.get(username=leave_entry.roll_no_id) - message = "signal nodues rejected" - otheracademic_notif(request.user,nodues_receiver , 'nodues_status', leave_entry.id, "student", message) - leave_entry.save() - return redirect('/otheracademic/Ece_nodues') # Red - - -def approve_vlsi(request, no_dues_id): - leave_entry = NoDues.objects.get(id=no_dues_id) - leave_entry.vlsi_clear= True - leave_entry.vlsi_notclear = False - nodues_receiver=User.objects.get(username=leave_entry.roll_no_id) - message = "vlsi nodues approved" - otheracademic_notif(request.user,nodues_receiver , 'nodues_status', leave_entry.id, "student", message) - leave_entry.save() - return redirect('/otheracademic/Ece_nodues') # Realumni - -def approve_vlsi_not(request, no_dues_id): - leave_entry = NoDues.objects.get(id=no_dues_id) - leave_entry.vlsi_clear= True - leave_entry.vlsi_notclear = False - leave_entry.save() - return redirect('/otheracademic/Ece_nodues_not') # Realumni - - - -def reject_vlsi(request, no_dues_id): - leave_entry = NoDues.objects.get(id=no_dues_id) - leave_entry.vlsi_clear= False - leave_entry.vlsi_notclear = True - nodues_receiver=User.objects.get(username=leave_entry.roll_no_id) - message = "Vlsi nodues rejected" - otheracademic_notif(request.user,nodues_receiver , 'nodues_status', leave_entry.id, "student", message) - leave_entry.save() - return redirect('/otheracademic/Ece_nodues') # Red - -def approve_ece(request, no_dues_id): - leave_entry = NoDues.objects.get(id=no_dues_id) - leave_entry.ece_clear= True - leave_entry.ece_notclear = False - nodues_receiver=User.objects.get(username=leave_entry.roll_no_id) - message = "Ece nodues approved" - otheracademic_notif(request.user,nodues_receiver , 'nodues_status', leave_entry.id, "student", message) - leave_entry.save() - return redirect('/otheracademic/Ece_nodues') # Realumni - -def approve_ece_not(request, no_dues_id): - leave_entry = NoDues.objects.get(id=no_dues_id) - leave_entry.ece_clear= True - leave_entry.ece_notclear = False - leave_entry.save() - return redirect('/otheracademic/Ece_nodues_not') # Realumni - - - -def reject_ece(request, no_dues_id): - leave_entry = NoDues.objects.get(id=no_dues_id) - leave_entry.ece_clear= False - leave_entry.ece_notclear = True - nodues_receiver=User.objects.get(username=leave_entry.roll_no_id) - message = "Ece nodues rejected" - otheracademic_notif(request.user,nodues_receiver , 'nodues_status', leave_entry.id, "student", message) - leave_entry.save() - return redirect('/otheracademic/Ece_nodues') # Red - - -def approve_hostel(request, no_dues_id): - leave_entry = NoDues.objects.get(id=no_dues_id) - leave_entry.hostel_clear= True - leave_entry.hostel_notclear = False - nodues_receiver=User.objects.get(username=leave_entry.roll_no_id) - message = "hostel nodues approved" - otheracademic_notif(request.user,nodues_receiver , 'nodues_status', leave_entry.id, "student", message) - leave_entry.save() - return redirect('/otheracademic/hostel_nodues') # Realumni - -def approve_hostel_not(request, no_dues_id): - leave_entry = NoDues.objects.get(id=no_dues_id) - leave_entry.hostel_clear= True - leave_entry.hostel_notclear = False - leave_entry.save() - return redirect('/otheracademic/hostel_nodues_not') # Realumni - - - -def reject_hostel(request, no_dues_id): - leave_entry = NoDues.objects.get(id=no_dues_id) - leave_entry.hostel_clear= False - leave_entry.hostel_notclear = True - nodues_receiver=User.objects.get(username=leave_entry.roll_no_id) - message = "hostel nodues rejected" - otheracademic_notif(request.user,nodues_receiver , 'nodues_status', leave_entry.id, "student", message) - leave_entry.save() - return redirect('/otheracademic/hostel_nodues') # Red - - - -def approve_library(request, no_dues_id): - leave_entry = NoDues.objects.get(id=no_dues_id) - leave_entry.library_clear= True - leave_entry.library_notclear = False - nodues_receiver=User.objects.get(username=leave_entry.roll_no_id) - message = "library nodues approved" - otheracademic_notif(request.user,nodues_receiver , 'nodues_status', leave_entry.id, "student", message) - leave_entry.save() - return redirect('/otheracademic/library_nodues') # Realumni - -def approve_library_not(request, no_dues_id): - leave_entry = NoDues.objects.get(id=no_dues_id) - leave_entry.library_clear= True - leave_entry.library_notclear = False - leave_entry.save() - return redirect('/otheracademic/library_nodues_not') # Realumni - - - -def reject_library(request, no_dues_id): - leave_entry = NoDues.objects.get(id=no_dues_id) - leave_entry.library_clear= False - leave_entry.library_notclear = True - nodues_receiver=User.objects.get(username=leave_entry.roll_no_id) - message = "library nodues rejected" - otheracademic_notif(request.user,nodues_receiver , 'nodues_status', leave_entry.id, "student", message) - leave_entry.save() - return redirect('/otheracademic/library_nodues') # Red - - -def approve_workshop(request, no_dues_id): - leave_entry = NoDues.objects.get(id=no_dues_id) - leave_entry.workshop_clear= True - leave_entry.workshop_notclear = False - nodues_receiver=User.objects.get(username=leave_entry.roll_no_id) - message = "workshop nodues approved" - otheracademic_notif(request.user,nodues_receiver , 'nodues_status', leave_entry.id, "student", message) - leave_entry.save() - return redirect('/otheracademic/ME_nodues') # Realumni - -def approve_workshop_not(request, no_dues_id): - leave_entry = NoDues.objects.get(id=no_dues_id) - leave_entry.workshop_clear= True - leave_entry.workshop_notclear = False - leave_entry.save() - return redirect('/otheracademic/ME_nodues_not') # Realumni - - - -def reject_workshop(request, no_dues_id): - leave_entry = NoDues.objects.get(id=no_dues_id) - leave_entry.workshop_clear= False - leave_entry.workshop_notclear = True - nodues_receiver=User.objects.get(username=leave_entry.roll_no_id) - message = "workshop nodues rejected" - otheracademic_notif(request.user,nodues_receiver , 'nodues_status', leave_entry.id, "student", message) - leave_entry.save() - return redirect('/otheracademic/ME_nodues') # Red - - -def approve_mecha(request, no_dues_id): - leave_entry = NoDues.objects.get(id=no_dues_id) - leave_entry.mechatronics_lab_clear= True - leave_entry.mechatronics_lab_notclear = False - nodues_receiver=User.objects.get(username=leave_entry.roll_no_id) - message = "mecha nodues approved" - otheracademic_notif(request.user,nodues_receiver , 'nodues_status', leave_entry.id, "student", message) - leave_entry.save() - return redirect('/otheracademic/ME_nodues') # Realumni - -def approve_mecha_not(request, no_dues_id): - leave_entry = NoDues.objects.get(id=no_dues_id) - leave_entry.mechatronics_lab_clear= True - leave_entry.mechatronics_lab_notclear = False - leave_entry.save() - return redirect('/otheracademic/ME_nodues_not') # Realumni - - - -def reject_mecha(request, no_dues_id): - leave_entry = NoDues.objects.get(id=no_dues_id) - leave_entry.mechatronics_lab_clear= False - leave_entry.mechatronics_lab_notclear = True - nodues_receiver=User.objects.get(username=leave_entry.roll_no_id) - message = "mecha nodues rejected" - otheracademic_notif(request.user,nodues_receiver , 'nodues_status', leave_entry.id, "student", message) - leave_entry.save() - return redirect('/otheracademic/ME_nodues') # Red - - -def approve_mess(request, no_dues_id): - leave_entry = NoDues.objects.get(id=no_dues_id) - leave_entry.mess_clear= True - leave_entry.mess_notclear = False - nodues_receiver=User.objects.get(username=leave_entry.roll_no_id) - message = "mess nodues approved" - otheracademic_notif(request.user,nodues_receiver , 'nodues_status', leave_entry.id, "student", message) - leave_entry.save() - return redirect('/otheracademic/mess_nodues') # Realumni - -def approve_mess_not(request, no_dues_id): - leave_entry = NoDues.objects.get(id=no_dues_id) - leave_entry.mess_clear= True - leave_entry.mess_notclear = False - leave_entry.save() - return redirect('/otheracademic/mess_nodues_not') # Realumni - - - -def reject_mess(request, no_dues_id): - leave_entry = NoDues.objects.get(id=no_dues_id) - leave_entry.mess_clear= False - leave_entry.mess_notclear = True - nodues_receiver=User.objects.get(username=leave_entry.roll_no_id) - message = "mess nodues rejected" - otheracademic_notif(request.user,nodues_receiver , 'nodues_status', leave_entry.id, "student", message) - leave_entry.save() - return redirect('/otheracademic/mess_nodues') # Red - -def approve_physics(request, no_dues_id): - leave_entry = NoDues.objects.get(id=no_dues_id) - leave_entry.physics_lab_clear= True - leave_entry.physics_lab_notclear = False - nodues_receiver=User.objects.get(username=leave_entry.roll_no_id) - message = "physics nodues approved" - otheracademic_notif(request.user,nodues_receiver , 'nodues_status', leave_entry.id, "student", message) - leave_entry.save() - return redirect('/otheracademic/Physics_nodues') # Realumni - -def approve_physics_not(request, no_dues_id): - leave_entry = NoDues.objects.get(id=no_dues_id) - leave_entry.physics_lab_clear= True - leave_entry.physics_lab_notclear = False - leave_entry.save() - return redirect('/otheracademic/Physics_nodues_not') # Realumni - - - -def reject_physics(request, no_dues_id): - leave_entry = NoDues.objects.get(id=no_dues_id) - leave_entry.physics_lab_clear= False - leave_entry.physics_lab_notclear = True - nodues_receiver=User.objects.get(username=leave_entry.roll_no_id) - message = "physics nodues rejected" - otheracademic_notif(request.user,nodues_receiver , 'nodues_status', leave_entry.id, "student", message) - leave_entry.save() - return redirect('/otheracademic/Physics_nodues') # Red - - -def Bank_nodues(request): - inbox=view_inbox(username=request.user.username,designation="student",src_module="otheracademic") - leave_ids=[msg ['src_object_id'] for msg in inbox if msg['subject']=='no_dues'] - - form_data = NoDues.objects.filter(id__in=leave_ids) - # form_data=NoDues.objects.all() - return render(request,'otheracademic/Bank_nodues.html',{'form_data': form_data}) - -def Bank_nodues_not(request): - inbox=view_inbox(username=request.user.username,designation="student",src_module="otheracademic") - leave_ids=[msg ['src_object_id'] for msg in inbox if msg['subject']=='no_dues'] - - form_data = NoDues.objects.filter(id__in=leave_ids) - return render(request,'otheracademic/Bank_nodues_not.html',{'form_data': form_data}) - -def BTP_nodues(request): - inbox=view_inbox(username=request.user.username,designation="student",src_module="otheracademic") - leave_ids=[msg ['src_object_id'] for msg in inbox if msg['subject']=='no_dues'] - - form_data = NoDues.objects.filter(id__in=leave_ids) - return render(request,'otheracademic/BTP_nodues.html',{'form_data': form_data}) - -def BTP_nodues_not(request): - inbox=view_inbox(username=request.user.username,designation="student",src_module="otheracademic") - leave_ids=[msg ['src_object_id'] for msg in inbox if msg['subject']=='no_dues'] - - form_data = NoDues.objects.filter(id__in=leave_ids) - return render(request,'otheracademic/BTP_nodues_not.html',{'form_data': form_data}) - -def CSE_nodues(request): - inbox=view_inbox(username=request.user.username,designation="student",src_module="otheracademic") - leave_ids=[msg ['src_object_id'] for msg in inbox if msg['subject']=='no_dues'] - - form_data = NoDues.objects.filter(id__in=leave_ids) - return render(request,'otheracademic/CSE_nodues.html',{'form_data': form_data}) - -def CSE_nodues_not(request): - inbox=view_inbox(username=request.user.username,designation="student",src_module="otheracademic") - leave_ids=[msg ['src_object_id'] for msg in inbox if msg['subject']=='no_dues'] - - form_data = NoDues.objects.filter(id__in=leave_ids) - return render(request,'otheracademic/CSE_nodues_not.html',{'form_data': form_data}) - - -def Design_nodues(request): - inbox=view_inbox(username=request.user.username,designation="student",src_module="otheracademic") - leave_ids=[msg ['src_object_id'] for msg in inbox if msg['subject']=='no_dues'] - - form_data = NoDues.objects.filter(id__in=leave_ids) - return render(request,'otheracademic/Design_nodues.html',{'form_data': form_data}) - -def Design_nodues_not(request): - inbox=view_inbox(username=request.user.username,designation="student",src_module="otheracademic") - leave_ids=[msg ['src_object_id'] for msg in inbox if msg['subject']=='no_dues'] - - form_data = NoDues.objects.filter(id__in=leave_ids) - return render(request,'otheracademic/Design_nodues_not.html',{'form_data': form_data}) - - -def dsa_nodues(request): - inbox=view_inbox(username=request.user.username,designation="student",src_module="otheracademic") - leave_ids=[msg ['src_object_id'] for msg in inbox if msg['subject']=='no_dues'] - - form_data = NoDues.objects.filter(id__in=leave_ids) - return render(request,'otheracademic/dsa_nodues.html',{'form_data': form_data}) - -def dsa_nodues_not(request): - inbox=view_inbox(username=request.user.username,designation="student",src_module="otheracademic") - leave_ids=[msg ['src_object_id'] for msg in inbox if msg['subject']=='no_dues'] - - form_data = NoDues.objects.filter(id__in=leave_ids) - return render(request,'otheracademic/dsa_nodues_not.html',{'form_data': form_data}) - - -def Ece_nodues(request): - inbox=view_inbox(username=request.user.username,designation="student",src_module="otheracademic") - leave_ids=[msg ['src_object_id'] for msg in inbox if msg['subject']=='no_dues'] - - form_data = NoDues.objects.filter(id__in=leave_ids) - return render(request,'otheracademic/Ece_nodues.html',{'form_data': form_data}) - -def Ece_nodues_not(request): - inbox=view_inbox(username=request.user.username,designation="student",src_module="otheracademic") - leave_ids=[msg ['src_object_id'] for msg in inbox if msg['subject']=='no_dues'] - - form_data = NoDues.objects.filter(id__in=leave_ids) - return render(request,'otheracademic/Ece_nodues_not.html',{'form_data': form_data}) - - -def hostel_nodues(request): - inbox=view_inbox(username=request.user.username,designation="student",src_module="otheracademic") - leave_ids=[msg ['src_object_id'] for msg in inbox if msg['subject']=='no_dues'] - - form_data = NoDues.objects.filter(id__in=leave_ids) - return render(request,'otheracademic/hostel_nodues.html',{'form_data': form_data}) - -def hostel_nodues_not(request): - inbox=view_inbox(username=request.user.username,designation="student",src_module="otheracademic") - leave_ids=[msg ['src_object_id'] for msg in inbox if msg['subject']=='no_dues'] - - form_data = NoDues.objects.filter(id__in=leave_ids) - return render(request,'otheracademic/hostel_nodues_not.html',{'form_data': form_data}) - - -def ME_nodues(request): - inbox=view_inbox(username=request.user.username,designation="student",src_module="otheracademic") - leave_ids=[msg ['src_object_id'] for msg in inbox if msg['subject']=='no_dues'] - - form_data = NoDues.objects.filter(id__in=leave_ids) - return render(request,'otheracademic/ME_nodues.html',{'form_data': form_data}) - -def ME_nodues_not(request): - inbox=view_inbox(username=request.user.username,designation="student",src_module="otheracademic") - leave_ids=[msg ['src_object_id'] for msg in inbox if msg['subject']=='no_dues'] - - form_data = NoDues.objects.filter(id__in=leave_ids) - return render(request,'otheracademic/ME_nodues_not.html',{'form_data': form_data}) - - -def mess_nodues(request): - inbox=view_inbox(username=request.user.username,designation="student",src_module="otheracademic") - leave_ids=[msg ['src_object_id'] for msg in inbox if msg['subject']=='no_dues'] - - form_data = NoDues.objects.filter(id__in=leave_ids) - return render(request,'otheracademic/mess_nodues.html',{'form_data': form_data}) - -def mess_nodues_not(request): - inbox=view_inbox(username=request.user.username,designation="student",src_module="otheracademic") - leave_ids=[msg ['src_object_id'] for msg in inbox if msg['subject']=='no_dues'] - - form_data = NoDues.objects.filter(id__in=leave_ids) - return render(request,'otheracademic/mess_nodues_not.html',{'form_data': form_data}) - - -def Physics_nodues(request): - inbox=view_inbox(username=request.user.username,designation="student",src_module="otheracademic") - leave_ids=[msg ['src_object_id'] for msg in inbox if msg['subject']=='no_dues'] - - form_data = NoDues.objects.filter(id__in=leave_ids) - return render(request,'otheracademic/Physics_nodues.html',{'form_data': form_data}) - -def Physics_nodues_not(request): - inbox=view_inbox(username=request.user.username,designation="student",src_module="otheracademic") - leave_ids=[msg ['src_object_id'] for msg in inbox if msg['subject']=='no_dues'] - - form_data = NoDues.objects.filter(id__in=leave_ids) - return render(request,'otheracademic/Physics_nodues_not.html',{'form_data': form_data}) - -def discipline_nodues(request): - inbox=view_inbox(username=request.user.username,designation="student",src_module="otheracademic") - leave_ids=[msg ['src_object_id'] for msg in inbox if msg['subject']=='no_dues'] - - form_data = NoDues.objects.filter(id__in=leave_ids) - return render(request,'otheracademic/Discipline_nodues.html',{'form_data': form_data}) - -def discipline_nodues_not(request): - inbox=view_inbox(username=request.user.username,designation="student",src_module="otheracademic") - leave_ids=[msg ['src_object_id'] for msg in inbox if msg['subject']=='no_dues'] - - form_data = NoDues.objects.filter(id__in=leave_ids) - return render(request,'otheracademic/Discipline_nodues_not.html',{'form_data': form_data}) - -def library_nodues(request): - inbox=view_inbox(username=request.user.username,designation="student",src_module="otheracademic") - leave_ids=[msg ['src_object_id'] for msg in inbox if msg['subject']=='no_dues'] - - form_data = NoDues.objects.filter(id__in=leave_ids) - return render(request,'otheracademic/library_nodues.html',{'form_data': form_data}) - -def library_nodues_not(request): - inbox=view_inbox(username=request.user.username,designation="student",src_module="otheracademic") - leave_ids=[msg ['src_object_id'] for msg in inbox if msg['subject']=='no_dues'] - - form_data = NoDues.objects.filter(id__in=leave_ids) - return render(request,'otheracademic/library_nodues_not.html',{'form_data': form_data}) - - - - - - - -def noduesStatus_acad(request): - if(request.user.username == "acadadmin"): - form_data = NoDues.objects.all() - return render(request, 'otheracademic/noduesStatus_acad.html',{'form_data' : form_data }) - else : - return HttpResponse("Not Available for you.") - -def nodues_apply(request): - return render(request,'otheracademic/nodues_apply.html') - - - -def update_dues_status(request): - if request.method == 'POST': - roll_no = request.POST.get('roll_no') - clear = request.POST.get('clear') # 'true' if clear, 'false' if not clear - # Convert clear to boolean - clear = clear.lower() == 'true' - - # Update clearance status in the database - try: - student = NoDues.objects.get(roll_no=roll_no) - if clear: - student.clear_status = True - student.not_clear_status = False - else: - student.clear_status = False - student.not_clear_status = True - student.save() - return JsonResponse({'message': 'Clearance status updated successfully'}, status=200) - except NoDues.DoesNotExist: - return JsonResponse({'error': 'Student not found'}, status=404) - else: - return JsonResponse({'error': 'Invalid request method'}, status=405) - -#assistantship - - - -@login_required -def assistantship(request): - - """ - This function solve the problem of assistantship apply by mtech and phd students for thhe monthly stipend. - The form firstly approved by TA_supervisor ---->then it forwarded to---->Thesis_Supervisor------>HOD------>acadadmin - After approval status is shown to the student - - - - - """ - user = get_object_or_404(User, username=request.user.username) - if user.extrainfo.student.programme == 'M.Tech' or 'PhD': - return render(request, 'otheracademic/assistantshipclaimform.html') - else: - return HttpResponse("NOt available") -@login_required -def assistantship_form_submission(request): - a=get_object_or_404(User,username=request.user.username) - y=ExtraInfo.objects.all().select_related('user','department').filter(user=a).first() - # num=1 - # comp_id=y.id - if request.method == 'POST': - # Retrieve form data - print(request.POST) - student_name = request.POST.get('student_name') - roll_no = request.POST.get('roll_no') - discipline = request.POST.get('discipline') - date_from = request.POST.get('date_from') - date_to = request.POST.get('date_to') - bank_account_no = request.POST.get('bank_account_no') - signature = request.FILES.get('signature') - applicability = request.POST.get('applicability') - ta_supervisor = request.POST.get('ta_supervisor') - thesis_supervisor = request.POST.get('thesis_supervisor') - date_applied = request.POST.get('date_of_application'), - hod=request.POST.get('hod_credential') - - - current_date = date.today() - - - - assistant = AssistantshipClaimFormStatusUpd.objects.create(student_name=request.user.first_name+request.user.last_name,roll_no=request.user.extrainfo, - discipline=discipline, - dateFrom=date_from, - dateTo=date_to, - bank_account=bank_account_no, - student_signature=signature, - dateApplied= current_date , - ta_supervisor=ta_supervisor, - thesis_supervisor=thesis_supervisor, - applicability=applicability, - TA_approved=False, - TA_rejected=False, - Ths_approved=False, - Ths_rejected=False, - HOD_approved=False, - HOD_rejected=False, - Acad_approved=False, - Acad_rejected=False, - hod=hod - - - - ) - - current_user=get_object_or_404(User,username=request.user.username) - ta_supervisor_id=User.objects.get(username=ta_supervisor) - ts=User.objects.get(username=thesis_supervisor) - - user_details=User.objects.get(id=y.user_id) - des=HoldsDesignation.objects.filter(user=user_details).all() - file_id = create_file(uploader = request.user.username, uploader_designation=des[0].designation, receiver =ta_supervisor, receiver_designation = "student", src_module = "otheracademic", src_object_id = assistant.id, file_extra_JSON = {"value": 2}, attached_file = None,subject="assistantship") - print(request.user) - print(file_id) - message="A new assistantship application raised" - otheracademic_notif(request.user,ta_supervisor_id ,'ast_ta',assistant.id,"student",message) - # if date_from>=date_to: - # messages.warning(request,"enter") - # return HttpResponseRedirect('/otheracademic/assistantship') - # Redirect to a success page or return a success message - messages.success(request,"Your form is successfully submitted") - return HttpResponseRedirect('/otheracademic/assistantship') # Replace '/otheracademic/assistantship' with the actual URL you want to redirect to - - else: - # Return an error response for invalid request method - return HttpResponse('Invalid request method') - - - - - - -def assistantship_form_approval(request): - # Retrieve data from the database - inbox = view_inbox(username=request.user.username, designation="student", src_module="otheracademic") - # print(inbox) - # print(request.user.username) - - # Find the src_object_id where subject is 'assistantship' - assistantship_ids = [msg['src_object_id'] for msg in inbox if msg['subject'] == 'assistantship'] - - # Filter form_data queryset based on the ids found - form_data = AssistantshipClaimFormStatusUpd.objects.filter(id__in=assistantship_ids) - roll_no = request.user.username - return render(request, 'otheracademic/assistantship_approval.html', {'form_data': form_data, 'roll_no' : roll_no}) - -def assistantship_thesis(request): - # Retrieve data from the database - inbox = view_inbox(username=request.user.username, designation="student", src_module="otheracademic") - - assistantship_ids = [msg['src_object_id' ] for msg in inbox if msg['subject'] == 'assistantship'] - - # Filter form_data queryset based on the ids found - form_data = AssistantshipClaimFormStatusUpd.objects.filter(id__in=assistantship_ids) - roll_no = request.user.username - return render(request, 'otheracademic/thesis_supervisor_approve.html', {'form_data': form_data, 'roll_no' : roll_no}) - - -def assistantship_hod(request): - # Retrieve data from the database - inbox = view_inbox(username=request.user.username, designation="student", src_module="otheracademic") - print(inbox) - assistantship_ids = [msg['src_object_id'] for msg in inbox if msg['subject'] == 'assistantship'] - - # Filter form_data queryset based on the ids found - form_data = AssistantshipClaimFormStatusUpd.objects.filter(id__in=assistantship_ids) - roll_no = request.user.username - return render(request, 'otheracademic/hod_approval.html', {'form_data': form_data, 'roll_no' : roll_no}) - - - - -def assistantship_status(request): - form_data = AssistantshipClaimFormStatusUpd.objects.all() - roll_no = request.user.username - return render(request, 'otheracademic/assistantship_status.html', { 'form_data' : form_data, 'roll_no' : roll_no}) - - -def assistantship_log(request): - user=get_object_or_404(User,username=request.user.username) - if(user.extrainfo.department.name == 'Academics'): - form_data = AssistantshipClaimFormStatusUpd.objects.all() - return render(request, 'otheracademic/assistantship_log.html', { 'form_data' : form_data}) - else: - return HttpResponse("Not Avalable For You.") - -def find_id_from_inbox(inbox_data, src_obj_id): - for item in inbox_data: - if item.get('src_object_id') == src_obj_id: - return item.get('id') - return None # Return None if src_obj_id is not found in the inbox - - - -def assistanship_ta_approve(request, ass_id): - # Obtain inbox data - inbox = view_inbox(username=request.user.username, designation="student", src_module="otheracademic") - - # Find the object with the given ID from the AssistantshipClaimFormStatusUpd model - leave_entry = get_object_or_404(AssistantshipClaimFormStatusUpd, id=ass_id) - - # Access the thesis_supervisor attribute of leave_entry - print(leave_entry.thesis_supervisor) - thesis_supervisor_user = User.objects.get(username=leave_entry.thesis_supervisor) - - # Update TA_approved field to True - leave_entry.TA_approved = True - leave_entry.save() - leave_receive = User.objects.get(username=leave_entry.roll_no_id) - message='Assistanstship Claim form status' - otheracademic_notif(request.user,leave_receive, 'ast_ta_accept', leave_entry.id, 'student', message) - ass_id_from_inbox = find_id_from_inbox(inbox, ass_id) - print(ass_id_from_inbox) - a=get_object_or_404(User,username=request.user.username) - y=ExtraInfo.objects.all().select_related('user','department').filter(user=a).first() - user_details=User.objects.get(id=y.user_id) - des=HoldsDesignation.objects.filter(user=user_details).all() - - forwarded_file_id = create_file(uploader = request.user.username, uploader_designation=des[0].designation, receiver =leave_entry.thesis_supervisor, receiver_designation = "student", src_module = "otheracademic", src_object_id =ass_id, file_extra_JSON = {"value": 2}, attached_file = None,subject="assistantship") - - message = "Assistantship status form forwarded" - otheracademic_notif(request.user, thesis_supervisor_user, 'ast_thesis', ass_id, "student", message) - - # Display success message - messages.success(request, "Successfully approved and forwarded.") - - # Redirect to the approveform page - return redirect('/otheracademic/approveform') - - - -def assistanship_ta_reject(request, ass_id): - leave_entry = AssistantshipClaimFormStatusUpd.objects.get(id = ass_id) - leave_entry.TA_rejected = True - leave_entry.save() - leave_receive = User.objects.get(username=leave_entry.roll_no_id) - message='Assistanstship Claim form status' - otheracademic_notif(request.user,leave_receive, 'ast_ta_accept', leave_entry.id, 'student', message) - messages.success(request, "Successfully rejected.") - return redirect('/otheracademic/approveform') - -def assistanship_thesis_approve(request, ass_id): - - leave_entry = get_object_or_404(AssistantshipClaimFormStatusUpd, id=ass_id) - - # Access the thesis_supervisor attribute of leave_entry - print(leave_entry.thesis_supervisor) - csehod_user = User.objects.get(username=leave_entry.hod) - - # Update TA_approved field to True - leave_entry.Ths_approved = True - leave_entry.save() - leave_receive = User.objects.get(username=leave_entry.roll_no_id) - message='Assistanstship Claim form status' - otheracademic_notif(request.user,leave_receive, 'ast_ta_accept', leave_entry.id, 'student', message) - # ass_id_from_inbox = find_id_from_inbox(inbox, ass_id) - # print(ass_id_from_inbox) - a=get_object_or_404(User,username=request.user.username) - y=ExtraInfo.objects.all().select_related('user','department').filter(user=a).first() - user_details=User.objects.get(id=y.user_id) - des=HoldsDesignation.objects.filter(user=user_details).all() - - forwarded_hod = create_file(uploader = request.user.username, uploader_designation=des[0].designation, receiver =leave_entry.hod, receiver_designation = "student", src_module = "otheracademic", src_object_id =ass_id, file_extra_JSON = {"value": 2}, attached_file = None,subject="assistantship") - - - - message = "Assistantship status received" - otheracademic_notif(request.user,csehod_user , 'ast_hod', ass_id, "student", message) - - # Display success message - messages.success(request, "Successfully approved and forwarded.") - - # Redirect to the approveform page - return redirect('/otheracademic/assitantship/thesis_approveform') - - - -def assistanship_thesis_reject(request, ass_id): - leave_entry = AssistantshipClaimFormStatusUpd.objects.get(id = ass_id) - leave_entry.Ths_rejected = True - leave_entry.save() - leave_receive = User.objects.get(username=leave_entry.roll_no_id) - message='Assistanstship Claim form status' - otheracademic_notif(request.user,leave_receive, 'ast_ta_accept', leave_entry.id, 'student', message) - messages.success(request, "Successfully rejected.") - return redirect('/otheracademic/assitantship/thesis_approveform') - - -def assistanship_hod_approve(request, ass_id): - - inbox = view_inbox(username=request.user.username, designation="student", src_module="otheracademic") - leave_entry = get_object_or_404(AssistantshipClaimFormStatusUpd, id=ass_id) - leave_entry.HOD_approved = True - leave_entry.save() - leave_receive = User.objects.get(username=leave_entry.roll_no_id) - message='Assistanstship Claim form status' - otheracademic_notif(request.user,leave_receive, 'ast_ta_accept', leave_entry.id, 'student', message) - - a=get_object_or_404(User,username=request.user.username) - y=ExtraInfo.objects.all().select_related('user','department').filter(user=a).first() - user_details=User.objects.get(id=y.user_id) - des=HoldsDesignation.objects.filter(user=user_details).all() - - acad_admin_des_id = Designation.objects.get(name="acadadmin") - user_ids = HoldsDesignation.objects.filter(designation_id=acad_admin_des_id.id).values_list('user_id', flat=True) - - acadadmin_receiver = User.objects.get(id=user_ids[0]) - - - forwarded_acad = create_file(uploader = request.user.username, uploader_designation=des[0].designation, receiver =acadadmin_receiver, receiver_designation ="student", src_module = "otheracademic", src_object_id =ass_id, file_extra_JSON = {"value": 2}, attached_file = None,subject="assistantship") - - message = "Assistantship status received" - otheracademic_notif(request.user,acadadmin_receiver , 'ast_acadadmin', ass_id, "student", message) - - # Display success message - messages.success(request, "Successfully approved and forwarded.") - - # Redirect to the approveform page - return redirect('/otheracademic/assitantship/hod_approveform') - - - -def assistanship_hod_reject(request, ass_id): - leave_entry = AssistantshipClaimFormStatusUpd.objects.get(id = ass_id) - leave_entry.HOD_rejected = True - leave_entry.save() - leave_receive = User.objects.get(username=leave_entry.roll_no_id) - message='Assistanstship Claim form status' - otheracademic_notif(request.user,leave_receive, 'ast_ta_accept', leave_entry.id, 'student', message) - messages.success(request, "Successfully rejected.") - - return redirect('/otheracademic/assitantship/hod_approveform') - - -def assistantship_acad_approveform(request): - inbox = view_inbox(username=request.user.username, designation="student", src_module="otheracademic") - print(inbox) - assistantship_ids = [msg['src_object_id'] for msg in inbox if msg['subject'] == 'assistantship'] - - # Filter form_data queryset based on the ids found - user=get_object_or_404(User,username=request.user.username) - if(user.extrainfo.department.name == 'Academics'): - form_data = AssistantshipClaimFormStatusUpd.objects.filter(id__in=assistantship_ids) - return render(request, 'otheracademic/acadadmin_approval.html', { 'form_data' : form_data}) - else: - return HttpResponse("Not Avalable For You.") - - -def assistanship_acad_approve(request, ass_id): - # Obtain inbox data - inbox = view_inbox(username=request.user.username, designation="student", src_module="otheracademic") - - # Find the object with the given ID from the AssistantshipClaimFormStatusUpd model - leave_entry = get_object_or_404(AssistantshipClaimFormStatusUpd, id=ass_id) - - # Access the thesis_supervisor attribute of leave_entry - # print(leave_entry.thesis_supervisor) - # acadadmin = User.objects.get(username='acadadmin') - - # Update TA_approved field to True - leave_entry.Acad_approved = True - leave_entry.save() - leave_receive = User.objects.get(username=leave_entry.roll_no_id) - message='Assistanstship Claim form status' - otheracademic_notif(request.user,leave_receive, 'ast_ta_accept', leave_entry.id, 'student', message) - - a=get_object_or_404(User,username=request.user.username) - y=ExtraInfo.objects.all().select_related('user','department').filter(user=a).first() - user_details=User.objects.get(id=y.user_id) - des=HoldsDesignation.objects.filter(user=user_details).all() - - - # forwarded_acad = create_file(uploader = request.user.username, uploader_designation=des[0].designation, receiver ='acadadmin', receiver_designation ="student", src_module = "otheracademic", src_object_id =ass_id, file_extra_JSON = {"value": 2}, attached_file = None,subject="assistantship") - - # message = "Assistantship status received" - # otheracademic_notif(request.user,acadadmin , 'alert', ass_id, "student", message) - - # Display success message - messages.success(request, "Successfully approved and forwarded.") - - # Redirect to the approveform page - return redirect('/otheracademic/assitantship/hod_approveform') - - - -def assistanship_acad_reject(request, ass_id): - leave_entry = AssistantshipClaimFormStatusUpd.objects.get(id = ass_id) - leave_entry.Acad_rejected = True - leave_entry.save() - leave_receive = User.objects.get(username=leave_entry.roll_no_id) - message='Assistanstship Claim form status' - otheracademic_notif(request.user,leave_receive, 'ast_ta_accept', leave_entry.id, 'student', message) - messages.success(request, "Successfully rejected.") - - return redirect('/otheracademic/assitantship/hod_approveform') - - - -def othersPage(request): - return render(request, 'otheracademic/othersPage.html') - -def othersLeave(request): - return render(request, 'otheracademic/othersLeave.html') - -def othersNoDues(request): - return render(request, 'otheracademic/othersNoDues.html') - -def othersAssistantship(request): - return render(request, 'otheracademic/othersAssistantship.html') - -def othersGraduate(request): - return render(request, 'otheracademic/othersGraduate.html') - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/FusionIIIT/applications/placement_cell/__init__.py b/FusionIIIT/applications/placement_cell/__init__.py new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/FusionIIIT/applications/placement_cell/__init__.py @@ -0,0 +1 @@ + diff --git a/FusionIIIT/applications/placement_cell/admin.py b/FusionIIIT/applications/placement_cell/admin.py index e333e2a68..0af314e13 100644 --- a/FusionIIIT/applications/placement_cell/admin.py +++ b/FusionIIIT/applications/placement_cell/admin.py @@ -82,7 +82,7 @@ class StudentRecordAdmin(admin.ModelAdmin): class ChairmanVisitAdmin(admin.ModelAdmin): - list_display = ('company_name', 'location', 'visiting_date', 'timestamp') + list_display = ('company_name', 'location', 'visiting_date', 'start_date', 'end_date', 'timestamp') class PlacementScheduleAdmin(admin.ModelAdmin): diff --git a/FusionIIIT/applications/placement_cell/api/__init__.py b/FusionIIIT/applications/placement_cell/api/__init__.py new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/FusionIIIT/applications/placement_cell/api/__init__.py @@ -0,0 +1 @@ + diff --git a/FusionIIIT/applications/placement_cell/api/serializers.py b/FusionIIIT/applications/placement_cell/api/serializers.py index e6960adb0..7e0e0a2c3 100644 --- a/FusionIIIT/applications/placement_cell/api/serializers.py +++ b/FusionIIIT/applications/placement_cell/api/serializers.py @@ -4,7 +4,14 @@ from applications.placement_cell.models import (Achievement, Course, Education, Experience, Has, Patent, Project, Publication, Skill, - PlacementStatus, NotifyStudent) + PlacementAppeal, PlacementStatus, + NotifyStudent) + + +class PlacementAppealSerializer(serializers.ModelSerializer): + class Meta: + model = PlacementAppeal + fields = '__all__' class SkillSerializer(serializers.ModelSerializer): @@ -17,7 +24,7 @@ class HasSerializer(serializers.ModelSerializer): class Meta: model = Has - fields = ('skill_id','skill_rating') + fields = ('id', 'skill_id', 'skill_rating') def create(self, validated_data): skill = validated_data.pop('skill_id') @@ -28,6 +35,22 @@ def create(self, validated_data): raise serializers.ValidationError({'skill': 'This skill is already present'}) return has_obj + def update(self, instance, validated_data): + skill = validated_data.pop('skill_id', None) + if skill: + skill_id, _ = Skill.objects.get_or_create(**skill) + duplicate = Has.objects.filter( + unique_id=instance.unique_id, + skill_id=skill_id, + ).exclude(pk=instance.pk).exists() + if duplicate: + raise serializers.ValidationError({'skill': 'This skill is already present'}) + instance.skill_id = skill_id + if 'skill_rating' in validated_data: + instance.skill_rating = validated_data['skill_rating'] + instance.save() + return instance + class EducationSerializer(serializers.ModelSerializer): class Meta: diff --git a/FusionIIIT/applications/placement_cell/api/urls.py b/FusionIIIT/applications/placement_cell/api/urls.py new file mode 100644 index 000000000..aae911611 --- /dev/null +++ b/FusionIIIT/applications/placement_cell/api/urls.py @@ -0,0 +1,118 @@ +from django.conf.urls import url + +from . import views + +app_name = "placement" + +urlpatterns = [ + url(r"^api/placement/$", views.placement_api, name="placement_api"), + url(r"^api/placement/(?P[0-9]+)/$", views.placement_detail_api, name="placement_detail_api"), + url(r"^api/statistics/$", views.placement_statistics_api, name="placement_statistics_api"), + url(r"^api/reports/$", views.placement_reports_api, name="placement_reports_api"), + url(r"^api/reports/export/$", views.placement_reports_export_api, name="placement_reports_export_api"), + url(r"^api/report-schedules/$", views.placement_report_schedules_api, name="placement_report_schedules_api"), + url(r"^api/report-schedules/(?P[0-9]+)/$", views.placement_report_schedule_detail_api, name="placement_report_schedule_detail_api"), + url(r"^api/delete-statistics/(?P[0-9]+)/$", views.delete_placement_statistics_api, name="delete_placement_statistics_api"), + url(r"^api/higher-studies/$", views.higher_studies_api, name="higher_studies_api"), + url(r"^api/higher-studies/(?P[0-9]+)/$", views.higher_studies_detail_api, name="higher_studies_detail_api"), + url(r"^api/registration/$", views.registration_api, name="registration_api"), + url(r"^api/add-field/$", views.placement_fields_api, name="placement_fields_api"), + url(r"^api/form-fields/$", views.form_fields_api, name="form_fields_api"), + url(r"^api/profile/$", views.placement_profile_api, name="placement_profile_api"), + url(r"^api/notification-preferences/$", views.notification_preferences_api, name="notification_preferences_api"), + url(r"^api/apply-for-placement/$", views.apply_for_placement_api, name="apply_for_placement_api"), + url(r"^api/apply-for-placement/(?P[0-9]+)/$", views.withdraw_application_api, name="withdraw_application_api"), + url(r"^api/my-applications/$", views.my_applications_api, name="my_applications_api"), + url(r"^api/my-offers/$", views.my_offers_api, name="my_offers_api"), + url(r"^api/offer/(?P[0-9]+)/$", views.offer_detail_api, name="offer_detail_api"), + url(r"^api/offer/(?P[0-9]+)/respond/$", views.offer_respond_api, name="offer_respond_api"), + url(r"^api/student-applications/(?P[0-9]+)/$", views.student_applications_api, name="student_applications_api"), + url(r"^api/application-detail/(?P[0-9]+)/$", views.application_detail_api, name="application_detail_api"), + url(r"^api/application-detail/(?P[0-9]+)/interview/$", views.application_interview_schedule_api, name="application_interview_schedule_api"), + url(r"^api/download-applications/(?P[0-9]+)/$", views.download_applications_api, name="download_applications_api"), + url(r"^api/nextround/(?P[0-9]+)/$", views.next_round_api, name="next_round_api"), + url(r"^api/timeline/(?P[0-9]+)/$", views.timeline_api, name="timeline_api"), + url(r"^api/calender/$", views.calendar_api, name="calendar_api"), + url(r"^api/generate-cv/$", views.generate_cv_api, name="generate_cv_api"), + url(r"^api/debared-students/$", views.debarred_students_api, name="debarred_students_api"), + url(r"^api/debared-status/(?P[A-Za-z0-9]+)/$", views.debarred_status_api, name="debarred_status_api"), + url(r"^api/send-notification/$", views.send_notification_api, name="send_notification_api"), + url(r"^api/restrictions/$", views.restrictions_api, name="restrictions_api"), + url(r"^api/restrictions/(?P[0-9]+)/$", views.restriction_detail_api, name="restriction_detail_api"), + url(r"^api/policies/$", views.placement_policies_api, name="placement_policies_api"), + url(r"^api/policies/(?P[0-9]+)/$", views.placement_policy_detail_api, name="placement_policy_detail_api"), + url(r"^api/alumni/profile/$", views.alumni_profile_api, name="alumni_profile_api"), + url(r"^api/alumni/directory/$", views.alumni_directory_api, name="alumni_directory_api"), + url(r"^api/alumni/verification/$", views.alumni_verification_list_api, name="alumni_verification_list_api"), + url(r"^api/alumni/verification/(?P[0-9]+)/$", views.alumni_verification_detail_api, name="alumni_verification_detail_api"), + url(r"^api/alumni/referrals/$", views.alumni_referrals_api, name="alumni_referrals_api"), + url(r"^api/alumni/connections/$", views.alumni_connections_api, name="alumni_connections_api"), + url(r"^api/alumni/connections/(?P[0-9]+)/$", views.alumni_connection_detail_api, name="alumni_connection_detail_api"), + url(r"^api/alumni/sessions/$", views.alumni_sessions_api, name="alumni_sessions_api"), + url(r"^api/alumni/sessions/(?P[0-9]+)/$", views.alumni_session_detail_api, name="alumni_session_detail_api"), + url(r"^$", views.placement, name="placement"), + url(r"^get_reference_list/$", views.get_reference_list, name="get_reference_list"), + url(r"^checking_roles/$", views.checking_roles, name="checking_roles"), + url( + r"^companyname_dropdown/$", + views.company_name_dropdown, + name="companyname_dropdown", + ), + url( + r"^student_records/invitation_status$", + views.invitation_status, + name="invitation_status", + ), + url( + r"^student_records/delete_invitation_status$", + views.delete_invitation_status, + name="delete_invitation_status", + ), + url(r"^student_records/$", views.student_records, name="student_records"), + url(r"^manage_records/$", views.manage_records, name="manage_records"), + url(r"^statistics/$", views.placement_statistics, name="placement_statistics"), + url( + r"^delete_placement_statistics/$", + views.delete_placement_statistics, + name="delete_placement_statistics", + ), + url(r"^cv/(?P[a-zA-Z0-9\.]{1,20})/$", views.cv, name="cv"), + url( + r"^add_placement_schedule/$", + views.add_placement_schedule, + name="add_placement_schedule", + ), + url( + r"^placement_schedule_save/$", + views.placement_schedule_save, + name="placement_schedule_save", + ), + url( + r"^delete_placement_record/$", + views.delete_placement_record, + name="delete_placement_record", + ), + url( + r"^add_placement_record/$", + views.add_placement_record, + name="add_placement_record", + ), + url( + r"^placement_record_save/$", + views.placement_record_save, + name="placement_record_save", + ), + url( + r"^add_placement_visit/$", + views.add_placement_visit, + name="add_placement_visit", + ), + url( + r"^placement_visit_save/$", + views.placement_visit_save, + name="placement_visit_save", + ), + # PlacementAppeal API endpoints + url(r"^api/placement-appeals/$", views.placement_appeal_list_create_api, name="placement_appeal_list_create_api"), + url(r"^api/placement-appeals/(?P[0-9]+)/$", views.placement_appeal_detail_api, name="placement_appeal_detail_api"), +] diff --git a/FusionIIIT/applications/placement_cell/api/views.py b/FusionIIIT/applications/placement_cell/api/views.py new file mode 100644 index 000000000..699791eb1 --- /dev/null +++ b/FusionIIIT/applications/placement_cell/api/views.py @@ -0,0 +1,6268 @@ +from rest_framework import status +from rest_framework.authentication import TokenAuthentication +from rest_framework.decorators import api_view, authentication_classes, permission_classes +from rest_framework.permissions import IsAuthenticated +from rest_framework.response import Response + +# --- Offer Detail and Respond APIs --- +@api_view(['GET']) +@permission_classes([IsAuthenticated]) +@authentication_classes([TokenAuthentication]) +def offer_detail_api(request, offer_id): + """Return offer details for a student (PlacementStatus).""" + student = selectors.get_student_for_user(request.user) + offer = PlacementStatus.objects.select_related('notify_id').filter(pk=offer_id, unique_id=student).first() + if not offer: + return Response({'detail': 'Offer not found.'}, status=status.HTTP_404_NOT_FOUND) + notify = offer.notify_id + schedule = PlacementSchedule.objects.select_related('role').filter(notify_id=notify).order_by('-id').first() + response_deadline = offer.timestamp + datetime.timedelta(days=offer.no_of_days) if offer.timestamp else None + data = { + 'id': offer.id, + 'schedule_id': schedule.id if schedule else None, + 'company_name': notify.company_name, + 'role': schedule.get_role if schedule else '', + 'ctc': str(notify.ctc), + 'invitation': offer.invitation, + 'response_deadline': response_deadline.isoformat() if response_deadline else None, + 'deadline_hours': offer.no_of_days * 24, + } + return Response(data, status=status.HTTP_200_OK) + + +@api_view(['POST']) +@permission_classes([IsAuthenticated]) +@authentication_classes([TokenAuthentication]) +def offer_respond_api(request, offer_id): + """Accept or decline an offer (PlacementStatus).""" + student = selectors.get_student_for_user(request.user) + offer = PlacementStatus.objects.select_related('notify_id').filter(pk=offer_id, unique_id=student).first() + if not offer: + return Response({'detail': 'Offer not found.'}, status=status.HTTP_404_NOT_FOUND) + action = str(request.data.get('action', '')).upper() + if offer.invitation != 'PENDING': + return Response({'detail': 'This invitation has already been responded to.'}, status=status.HTTP_409_CONFLICT) + deadline = offer.timestamp + datetime.timedelta(days=offer.no_of_days) + current_time = timezone.now() + if timezone.is_naive(deadline) and timezone.is_aware(current_time): + current_time = timezone.make_naive(current_time) + elif timezone.is_aware(deadline) and timezone.is_naive(current_time): + current_time = timezone.make_aware(current_time) + if current_time > deadline: + offer.invitation = 'IGNORE' + offer.save() + return Response({'detail': 'This placement invitation has expired.'}, status=status.HTTP_403_FORBIDDEN) + if action == 'ACCEPTED': + # Only allow if no other accepted offer + blocking_offer = PlacementStatus.objects.filter(unique_id=student, invitation='ACCEPTED').exclude(pk=offer.pk).exists() + if blocking_offer: + return Response({'detail': 'You already have an accepted offer and cannot accept another.'}, status=status.HTTP_409_CONFLICT) + offer.invitation = 'ACCEPTED' + offer.timestamp = timezone.now() + offer.save() + officer_recipients = User.objects.filter( + current_designation__designation__name__in=['placement officer', 'placement chairman'], + ).distinct() + _send_placement_notifications( + actor=request.user, + recipients=list(officer_recipients), + description='{} accepted the offer for {}.'.format(student.id.id, offer.notify_id.company_name), + ) + return Response({'message': 'Offer accepted successfully.'}, status=status.HTTP_200_OK) + elif action == 'REJECTED': + offer.invitation = 'REJECTED' + offer.timestamp = timezone.now() + offer.save() + officer_recipients = User.objects.filter( + current_designation__designation__name__in=['placement officer', 'placement chairman'], + ).distinct() + _send_placement_notifications( + actor=request.user, + recipients=list(officer_recipients), + description='{} declined the offer for {}.'.format(student.id.id, offer.notify_id.company_name), + ) + return Response({'message': 'Offer declined successfully.'}, status=status.HTTP_200_OK) + else: + return Response({'detail': 'Invalid action.'}, status=status.HTTP_400_BAD_REQUEST) +from .serializers import PlacementAppealSerializer +from applications.placement_cell.models import PlacementAppeal + +# PlacementAppeal API +@api_view(['GET', 'POST']) +@permission_classes([IsAuthenticated]) +@authentication_classes([TokenAuthentication]) +def placement_appeal_list_create_api(request): + if request.method == 'GET': + appeals = PlacementAppeal.objects.select_related( + 'student__id__user', + 'placement_status__notify_id', + ).order_by('-created_at') + if not _is_tpo_user(request.user): + student = selectors.get_student_for_user(request.user) + appeals = appeals.filter(student=student) + return Response([_serialize_appeal(item) for item in appeals], status=status.HTTP_200_OK) + + student = selectors.get_student_for_user(request.user) + placement_status = get_object_or_404( + PlacementStatus.objects.select_related('notify_id'), + pk=request.data.get('placement_status'), + unique_id=student, + ) + application = PlacementApplication.objects.filter( + schedule__notify_id=placement_status.notify_id, + student=student, + ).first() + if application is None or application.status != 'reject': + return Response( + {'detail': 'Appeals can only be raised after an application is rejected.'}, + status=status.HTTP_400_BAD_REQUEST, + ) + if PlacementAppeal.objects.filter(student=student, placement_status=placement_status).exists(): + return Response( + {'detail': 'An appeal has already been submitted for this rejection.'}, + status=status.HTTP_409_CONFLICT, + ) + reason = (request.data.get('reason') or '').strip() + if not reason: + return Response({'reason': ['This field is required.']}, status=status.HTTP_400_BAD_REQUEST) + appeal = PlacementAppeal.objects.create( + student=student, + placement_status=placement_status, + reason=reason, + ) + officer_recipients = User.objects.filter( + current_designation__designation__name__in=['placement officer', 'placement chairman'], + ).distinct() + _send_placement_notifications( + actor=request.user, + recipients=list(officer_recipients), + description='{} submitted an appeal for {}.'.format(student.id.id, placement_status.notify_id.company_name), + ) + return Response(_serialize_appeal(appeal), status=status.HTTP_201_CREATED) + +@api_view(['GET', 'PUT']) +@permission_classes([IsAuthenticated]) +@authentication_classes([TokenAuthentication]) +def placement_appeal_detail_api(request, pk): + appeal = get_object_or_404( + PlacementAppeal.objects.select_related( + 'student__id__user', + 'placement_status__notify_id', + ), + pk=pk, + ) + is_tpo = _is_tpo_user(request.user) + if not is_tpo: + student = selectors.get_student_for_user(request.user) + if appeal.student_id != student.id: + return Response({'detail': 'Appeal not found.'}, status=status.HTTP_404_NOT_FOUND) + + if request.method == 'GET': + return Response(_serialize_appeal(appeal), status=status.HTTP_200_OK) + + if not is_tpo: + return Response({'detail': 'Only TPO users can update appeals.'}, status=status.HTTP_403_FORBIDDEN) + next_status = str(request.data.get('status') or '').lower() + if next_status not in ['pending', 'reviewed', 'accepted', 'rejected']: + return Response({'status': ['Invalid appeal status.']}, status=status.HTTP_400_BAD_REQUEST) + appeal.status = next_status + appeal.response = request.data.get('response', appeal.response) + appeal.reviewed_at = timezone.now() if next_status != 'pending' else None + appeal.save(update_fields=['status', 'response', 'reviewed_at']) + _send_placement_notifications( + actor=request.user, + recipients=[appeal.student.id.user], + description='Your placement appeal for {} has been updated to {}.'.format( + appeal.placement_status.notify_id.company_name, + next_status, + ), + ) + return Response(_serialize_appeal(appeal), status=status.HTTP_200_OK) +import os +import shutil +import datetime +import decimal +import zipfile +import xlwt +import logging +import json +from collections import defaultdict + +from html import escape +from datetime import date +from io import BytesIO +from wsgiref.util import FileWrapper +from django.conf import settings +from django.contrib.auth.decorators import login_required +from django.contrib.auth.models import User +from django.contrib import messages +from django.core.cache import cache +from django.core.exceptions import ValidationError +from django.core.files.storage import FileSystemStorage +from django.core.mail import send_mail +from django.core.paginator import Paginator +from django.core.validators import validate_email +from django.db.models import Count, Max, Prefetch, Q +from django.http import HttpResponse, JsonResponse +from django.shortcuts import get_object_or_404, redirect, render +from django.template.loader import get_template, render_to_string +from django.utils.html import strip_tags +from django.utils import timezone +from django.utils.encoding import smart_str +from xhtml2pdf import pisa +from django.core import serializers +from notifications.signals import notify +from rest_framework import status +from rest_framework.authentication import TokenAuthentication +from rest_framework.decorators import api_view, authentication_classes, permission_classes +from rest_framework.permissions import IsAuthenticated +from rest_framework.response import Response +from applications.academic_information.models import Student +from notification.views import placement_cell_notif +from applications.globals.models import (DepartmentInfo, ExtraInfo, + HoldsDesignation, Designation) +from .. import selectors, services +from ..forms import (AddAchievement, AddChairmanVisit, AddCourse, AddEducation, + AddExperience, AddReference, AddPatent, AddProfile, AddProject, + AddPublication, AddSchedule, AddSkill, ManageHigherRecord, + ManagePbiRecord, ManagePlacementRecord, SearchHigherRecord, + SearchPbiRecord, SearchPlacementRecord, + SearchStudentRecord, SendInvite) + +from ..models import (Achievement, ChairmanVisit, Course, Education, Experience, Conference, + Has, NotifyStudent, Patent, PlacementRecord, Extracurricular, Reference, + PlacementSchedule, PlacementStatus, Project, Publication, + Skill, StudentPlacement, StudentRecord, Role, CompanyDetails, + PlacementField, PlacementApplication, PlacementApplicationResponse, + PlacementRound, PlacementRestriction, PlacementPolicy, PlacementProfileDocument, + PlacementProfileAuditLog, PlacementNotificationPreference, + PlacementReportSchedule, AlumniConnection, AlumniMentorshipSession, AlumniProfile, + AlumniReferral, PlacementApplicationTimeline, PlacementInterviewSchedule) +''' + @variables: + user - logged in user + profile - variable for extrainfo + studentrecord - storing all fetched student record from database + years - yearwise record of student placement + records - all the record of placement record table + tcse - all record of cse + tece - all record of ece + tme - all record of me + tadd - all record of student + form respective form object + stuname - student name obtained from the form + ctc - salary offered obtained from the form + cname - company name obtained from the form + rollno - roll no of student obtained from the form + year - year of placement obtained from the form + s - extra info data of the student obtained from the form + p - placement data of the student obtained from the form + placementrecord - placement record of the student obtained from the form + pbirecord - pbi data of the student obtained from the form + test_type - type of higher study test obtained from the form + uname - name of universty obtained from the form + test_score - score in the test obtained from the form + higherrecord - higher study record of the student obtained from the form + current - current user on a particular designation + status - status of the sent invitation by placement cell regarding placement/pbi + institute - institute for previous education obtained from the form + degree - degree for previous education obtained from the form + grade - grade for previous education obtained from the form + stream - stream for previous education obtained from the form + sdate - start date for previous education obtained from the form + edate - end date for previous education obtained from the form + education_obj - object variable of Education table + about_me - about me data obtained from the form + age - age data obtained from the form + address - address obtained from the form + contact - contact obtained from the form + pic - picture obtained from the form + skill - skill of the user obtained from the form + skill_rating - rating of respective skill obtained from the form + has_obj - object variable of Has table + achievement - achievement of user obtained from the form + achievement_type - type of achievement obtained from the form + description - description of respective achievement obtained from the form + issuer - certifier of respective achievement obtained from the form + date_earned - date of the respective achievement obtained from the form + achievement_obj - object variable of Achievement table + publication_title - title of the publication obtained from the form + description - description of respective publication obtained from the form + publisher - publisher of respective publication obtained from the form + publication_date - date of respective publication obtained from the form + publication_obj - object variable of Publication table + patent_name - name of patent obtained from the form + description - description of respective patent obtained from the form + patent_office - office of respective patent obtained from the form + patent_date - date of respective patent obtained from the form + patent_obj - object variable of Patent table + course_name - name of the course obtained from the form + description description of respective course obtained from the form + license_no - license_no of respective course obtained from the form + sdate - start date of respective course obtained from the form + edate - end date of respective course obtained from the form + course_obj - object variable of Course table + project_name - name of project obtained from the form + project_status - status of respective project obtained from the form + summary - summery of the respective project obtained from the form + project_link - link of the respective project obtained from the form + sdate - start date of respective project obtained from the form + edate - end date of respective project obtained from the form + project_obj - object variable of Project table + title - title of any kind of experience obtained from the form + status - status of the respective experience obtained from the form + company - company from which respective experience is gained as obtained from the form + location - location of the respective experience obtained from the form + description - description of respective experience obtained from the form + sdate - start date of respective experience obtained from the form + edate - end date of respective experience obtained from the form + experience_obj - object variable of Experience table + context - to sent the relevant context for html rendering + company_name - name of visiting comapany obtained from the form + location -location of visiting company obtained from the form + description - description of respective company obtained from the form + visiting_date - visiting date of respective company obtained from the form + visit_obj -object variable of ChairmanVisit table + notify - object of NotifyStudent table + schedule - object variable of PlacementSchedule table + q1 - all data of Has table + q3 - all data of Student table + st - all data of Student table + spid - id of student to be debar + sr - record from StudentPlacement of student having id=spid + achievementcheck - checking for achievent to be shown in cv + educationcheck - checking for education to be shown in cv + publicationcheck - checking for publication to be shown in cv + patentcheck - checking for patent to be shown in cv + internshipcheck - checking for internship to be shown in cv + projectcheck - checking for project to be shown in cv + coursecheck - checking for course to be shown in cv + skillcheck - checking for skill to be shown in cv +''' + +logger = logging.getLogger('django.server') + + +def _today(): + return timezone.now().date() + +def get_reference_list(request): + if request.method == 'POST': + student = selectors.get_student_for_user(request.user) + reference_objects = selectors.get_reference_list_json(student) + + context = { + 'reference_objs': reference_objects + } + return JsonResponse(context) + + +# Ajax for the company name dropdown for CompanyName when filling AddSchedule +def company_name_dropdown(request): + if request.method == 'POST': + current_value = request.POST.get('current_value') + company_name = selectors.get_company_names_by_prefix(current_value) + + context = { + 'company_names': company_name + } + + return JsonResponse(context) + + +# Ajax for all the roles in the dropdown +def checking_roles(request): + if request.method == 'POST': + current_value = request.POST.get('current_value') + role_name = selectors.get_role_names_by_prefix(current_value) + return JsonResponse({'all_roles': role_name}) + + +@login_required +def placement(request): + ''' + function include the functionality of first tab of UI + for student, placement officer & placement chairman + + placement officer & placement chairman + - can add schedule + - can delete schedule + student + - accepted or declined schedule + + ''' + user = request.user + profile = get_object_or_404(ExtraInfo, Q(user=user)) + schedule_tab = 1 + placementstatus = '' + + + form5 = AddSchedule(initial={}) + current1 = selectors.get_designation_queryset(user, "placement chairman") + current2 = selectors.get_designation_queryset(user, "placement officer") + current = selectors.get_designation_queryset(user, "student") + + # If the user is Student + if current: + student = selectors.get_student_for_user(user) + + # Student view for showing accepted or declined schedule + if request.method == 'POST': + if 'studentapprovesubmit' in request.POST: + services.update_invitation_status( + request.POST['studentapprovesubmit'], 'ACCEPTED' + ) + if 'studentdeclinesubmit' in request.POST: + services.update_invitation_status( + request.POST['studentdeclinesubmit'], 'REJECTED' + ) + + if 'educationsubmit' in request.POST: + form = AddEducation(request.POST) + if form.is_valid(): + institute = form.cleaned_data['institute'] + degree = form.cleaned_data['degree'] + grade = form.cleaned_data['grade'] + stream = form.cleaned_data['stream'] + sdate = form.cleaned_data['sdate'] + edate = form.cleaned_data['edate'] + education_obj = Education.objects.select_related('unique_id').create( + unique_id=student, degree=degree, + grade=grade, institute=institute, + stream=stream, sdate=sdate, edate=edate) + education_obj.save() + if 'profilesubmit' in request.POST: + about_me = request.POST.get('about') + age = request.POST.get('age') + address = request.POST.get('address') + contact = request.POST.get('contact') + pic = request.POST.get('pic') + # futu = request.POST.get('futu') + # print(studentplacement_obj.future_aspect) + # print('fut=', fut) + # print('futu=', futu) + # if studentplacement_obj.future_aspect == "HIGHER STUDIES": + # if futu == 2: + # studentplacement_obj.future_aspect = "PLACEMENT" + # elif studentplacement_obj.future_aspect == "PLACEMENT": + # if futu == None: + # studentplacement_obj.future_aspect = "HIGHER STUDIES" + extrainfo_obj = ExtraInfo.objects.get(user=user) + extrainfo_obj.about_me = about_me + extrainfo_obj.age = age + extrainfo_obj.address = address + extrainfo_obj.phone_no = contact + extrainfo_obj.profile_picture = pic + extrainfo_obj.save() + profile = get_object_or_404(ExtraInfo, Q(user=user)) + if 'skillsubmit' in request.POST: + form = AddSkill(request.POST) + if form.is_valid(): + skill = form.cleaned_data['skill'] + skill_rating = form.cleaned_data['skill_rating'] + has_obj = Has.objects.select_related('skill_id','unique_id').create(unique_id=student, + skill_id=Skill.objects.get(skill=skill), + skill_rating = skill_rating) + has_obj.save() + if 'achievementsubmit' in request.POST: + form = AddAchievement(request.POST) + if form.is_valid(): + achievement = form.cleaned_data['achievement'] + achievement_type = form.cleaned_data['achievement_type'] + description = form.cleaned_data['description'] + issuer = form.cleaned_data['issuer'] + date_earned = form.cleaned_data['date_earned'] + achievement_obj = Achievement.objects.select_related('unique_id').create(unique_id=student, + achievement=achievement, + achievement_type=achievement_type, + description=description, + issuer=issuer, + date_earned=date_earned) + achievement_obj.save() + if 'publicationsubmit' in request.POST: + form = AddPublication(request.POST) + if form.is_valid(): + publication_title = form.cleaned_data['publication_title'] + description = form.cleaned_data['description'] + publisher = form.cleaned_data['publisher'] + publication_date = form.cleaned_data['publication_date'] + publication_obj = Publication.objects.select_related('unique_id').create(unique_id=student, + publication_title= + publication_title, + publisher=publisher, + description=description, + publication_date=publication_date) + publication_obj.save() + if 'patentsubmit' in request.POST: + form = AddPatent(request.POST) + if form.is_valid(): + patent_name = form.cleaned_data['patent_name'] + description = form.cleaned_data['description'] + patent_office = form.cleaned_data['patent_office'] + patent_date = form.cleaned_data['patent_date'] + patent_obj = Patent.objects.select_related('unique_id').create(unique_id=student, patent_name=patent_name, + patent_office=patent_office, + description=description, + patent_date=patent_date) + patent_obj.save() + if 'coursesubmit' in request.POST: + form = AddCourse(request.POST) + if form.is_valid(): + course_name = form.cleaned_data['course_name'] + description = form.cleaned_data['description'] + license_no = form.cleaned_data['license_no'] + sdate = form.cleaned_data['sdate'] + edate = form.cleaned_data['edate'] + course_obj = Course.objects.select_related('unique_id').create(unique_id=student, course_name=course_name, + license_no=license_no, + description=description, + sdate=sdate, edate=edate) + course_obj.save() + if 'projectsubmit' in request.POST: + form = AddProject(request.POST) + if form.is_valid(): + project_name = form.cleaned_data['project_name'] + project_status = form.cleaned_data['project_status'] + summary = form.cleaned_data['summary'] + project_link = form.cleaned_data['project_link'] + sdate = form.cleaned_data['sdate'] + edate = form.cleaned_data['edate'] + project_obj = Project.objects.create(unique_id=student, summary=summary, + project_name=project_name, + project_status=project_status, + project_link=project_link, + sdate=sdate, edate=edate) + project_obj.save() + if 'experiencesubmit' in request.POST: + form = AddExperience(request.POST) + if form.is_valid(): + title = form.cleaned_data['title'] + status = form.cleaned_data['status'] + company = form.cleaned_data['company'] + location = form.cleaned_data['location'] + description = form.cleaned_data['description'] + sdate = form.cleaned_data['sdate'] + edate = form.cleaned_data['edate'] + experience_obj = Experience.objects.select_related('unique_id').create(unique_id=student, title=title, + company=company, location=location, + status=status, + description=description, + sdate=sdate, edate=edate) + experience_obj.save() + + if 'deleteskill' in request.POST: + hid = request.POST['deleteskill'] + hs = Has.objects.select_related('skill_id','unique_id').get(Q(pk=hid)) + hs.delete() + if 'deleteedu' in request.POST: + hid = request.POST['deleteedu'] + hs = Education.objects.select_related('unique_id').get(Q(pk=hid)) + hs.delete() + if 'deletecourse' in request.POST: + hid = request.POST['deletecourse'] + hs = Course.objects.get(Q(pk=hid)) + hs.delete() + if 'deleteexp' in request.POST: + hid = request.POST['deleteexp'] + hs = Experience.objects.get(Q(pk=hid)) + hs.delete() + if 'deletepro' in request.POST: + hid = request.POST['deletepro'] + hs = Project.objects.get(Q(pk=hid)) + hs.delete() + if 'deleteach' in request.POST: + hid = request.POST['deleteach'] + hs = Achievement.objects.get(Q(pk=hid)) + hs.delete() + if 'deletepub' in request.POST: + hid = request.POST['deletepub'] + hs = Publication.objects.select_related('unique_id').get(Q(pk=hid)) + hs.delete() + if 'deletepat' in request.POST: + hid = request.POST['deletepat'] + hs = Patent.objects.get(Q(pk=hid)) + hs.delete() + + placementstatus = selectors.get_student_upcoming_placement_status(student) + + + check_invitation_date(placementstatus) + + # facult and other staff view only statistics + if not (current or current1 or current2): + return redirect('/placement/statistics/') + + # delete the schedule + if 'deletesch' in request.POST: + delete_sch_key = request.POST['delete_sch_key'] + try: + services.delete_schedule(delete_sch_key) + messages.success(request, 'Schedule Deleted Successfully') + except Exception as e: + messages.error(request, 'Problem Occurred for Schedule Delete!!!') + + # saving all the schedule details + if 'schedulesubmit' in request.POST: + form5 = AddSchedule(request.POST, request.FILES) + if form5.is_valid(): + company_name = form5.cleaned_data['company_name'] + placement_date = form5.cleaned_data['placement_date'] + location = form5.cleaned_data['location'] + ctc = form5.cleaned_data['ctc'] + time = form5.cleaned_data['time'] + attached_file = form5.cleaned_data['attached_file'] + placement_type = form5.cleaned_data['placement_type'] + role_offered = request.POST.get('role') + description = form5.cleaned_data['description'] + + try: + services.create_schedule_and_notification( + placement_type=placement_type, + company_name=company_name, + ctc=ctc, + description=description, + placement_date=placement_date, + location=location, + time=time, + role_name=role_offered, + attached_file=attached_file, + ) + except Exception: + messages.error(request, "Problem Occurred for Schedule Add!!!") + else: + messages.success(request, "Schedule Added Successfull!!") + schedules = selectors.get_all_schedules() + + + context = { + 'current': current, + 'current1': current1, + 'current2': current2, + 'schedule_tab': schedule_tab, + 'schedules': schedules, + 'placementstatus': placementstatus, + 'form5': form5, + } + + return render(request, 'placementModule/placement.html', context) + + +@login_required +def delete_invitation_status(request): + ''' + function to delete the invitation that has been sent to the students + ''' + user = request.user + strecord_tab = 1 + mnpbi_tab = 0 + mnplacement_post = 0 + mnpbi_post = 0 + invitation_status_tab = 1 + placementstatus = [] + + no_pagination = 1 + is_disabled = 0 + paginator = '' + page_range = '' + + if 'deleteinvitationstatus' in request.POST: + delete_invit_status_key = request.POST['deleteinvitationstatus'] + + try: + PlacementStatus.objects.select_related('unique_id','notify_id').get(pk=delete_invit_status_key).delete() + messages.success(request, 'Invitation Deleted Successfully') + except Exception as e: + logger.error(e) + + if 'pbi_tab_active' in request.POST: + mnpbi_tab = 1 + else: + mnplacement_tab = 1 + + form1 = SearchStudentRecord(initial={}) + form9 = ManagePbiRecord(initial={}) + form11 = ManagePlacementRecord(initial={}) + form13 = SendInvite(initial={}) + current1 = HoldsDesignation.objects.filter(Q(working=user, designation__name="placement chairman")) + current2 = HoldsDesignation.objects.filter(Q(working=user, designation__name="placement officer")) + + context = { + 'form1': form1, + 'form9': form9, + 'form11': form11, + 'form13': form13, + 'invitation_status_tab': invitation_status_tab, + 'mnplacement_post': mnplacement_post, + 'mnpbi_tab': mnpbi_tab, + 'placementstatus': placementstatus, + # 'current':current, + 'current1': current1, + 'current2': current2, + 'strecord_tab': strecord_tab, + 'mnpbi_post': mnpbi_post, + 'page_range': page_range, + 'paginator': paginator, + 'no_pagination': no_pagination, + 'is_disabled': is_disabled, + } + + return render(request, 'placementModule/studentrecords.html', context) + + +def invitation_status(request): + ''' + function to check the invitation status + ''' + user = request.user + strecord_tab = 1 + mnpbi_tab = 0 + mnplacement_post = 0 + mnpbi_post = 0 + invitation_status_tab = 1 + placementstatus_placement = [] + placementstatus_pbi = [] + mnplacement_tab = 1 + + no_pagination = 1 + is_disabled = 0 + paginator = '' + page_range = '' + placement_get_request = False + pbi_get_request = False + + # invitation status for placement + if 'studentplacementsearchsubmit' in request.POST: + mnplacement_post = 1 + mnpbi_post = 0 + form = ManagePlacementRecord(request.POST) + + if form.is_valid(): + if form.cleaned_data['stuname']: + stuname = form.cleaned_data['stuname'] + else: + stuname = '' + if form.cleaned_data['ctc']: + ctc = form.cleaned_data['ctc'] + else: + ctc = 0 + if form.cleaned_data['company']: + cname = form.cleaned_data['company'] + else: + cname = '' + if form.cleaned_data['roll']: + rollno = form.cleaned_data['roll'] + else: + rollno = '' + + request.session['mn_stuname'] = stuname + request.session['mn_ctc'] = ctc + request.session['mn_cname'] = cname + request.session['mn_rollno'] = rollno + + placementstatus_placement = PlacementStatus.objects.select_related('unique_id','notify_id').filter(Q(notify_id__in=NotifyStudent.objects.filter + (Q(placement_type="PLACEMENT", + company_name__icontains=cname, + ctc__gte=ctc)), + unique_id__in=Student.objects.filter + ((Q(id__in=ExtraInfo.objects.filter + (Q(user__in=User.objects.filter + (Q(first_name__icontains=stuname)), + id__icontains=rollno)) + ))))) + # pagination stuff starts from here + total_query = placementstatus_placement.count() + + if total_query > 30: + no_pagination = 1 + paginator = Paginator(placementstatus_placement, 30) + page = request.GET.get('page', 1) + placementstatus_placement = paginator.page(page) + page = int(page) + total_page = int(page + 3) + + if page<(paginator.num_pages-3): + if total_query > 30 and total_query <=60: + page_range = range(1, 3) + else: + page_range = range(1, total_page+1) + + if page >= 5: + is_disabled = 1 + page_range = range(page-2, total_page) + else: + if page >= 5: + is_disabled = 1 + page_range = range(page-2, paginator.num_pages+1) + else: + page_range = range(1, paginator.num_pages+1) + else: + no_pagination = 0 + else: + # when the request from pagination with some page number + if request.GET.get('placement_page') != None: + mnplacement_post = 1 + mnpbi_post = 0 + no_pagination = 1 + try: + placementstatus_placement = PlacementStatus.objects.select_related('unique_id','notify_id').filter(Q(notify_id__in=NotifyStudent.objects.filter + (Q(placement_type="PLACEMENT", + company_name__icontains=request.session['mn_cname'], + ctc__gte=request.session['mn_ctc'])), + unique_id__in=Student.objects.filter + ((Q(id__in=ExtraInfo.objects.filter + (Q(user__in=User.objects.filter + (Q(first_name__icontains=request.session['mn_stuname'])), + id__icontains=request.session['mn_rollno'])) + ))))) + except: + placementstatus_placement = [] + + if placementstatus_placement != '': + total_query = placementstatus_placement.count() + else: + total_query = 0 + + if total_query > 30: + paginator = Paginator(placementstatus_placement, 30) + page = request.GET.get('placement_page', 1) + placementstatus_placement = paginator.page(page) + page = int(page) + total_page = int(page + 3) + + if page<(paginator.num_pages-3): + if total_query > 30 and total_query <=60: + page_range = range(1, 3) + else: + page_range = range(1, total_page+1) + + if page >= 5: + is_disabled = 1 + page_range = range(page-2, total_page) + else: + if page >= 5: + is_disabled = 1 + page_range = range(page-2, paginator.num_pages+1) + else: + page_range = range(1, paginator.num_pages+1) + else: + no_pagination = 0 + + # invitation status for pbi + if 'studentpbisearchsubmit' in request.POST: + mnpbi_tab = 1 + mnpbi_post = 1 + mnplacement_post = 0 + form = ManagePbiRecord(request.POST) + if form.is_valid(): + if form.cleaned_data['stuname']: + stuname = form.cleaned_data['stuname'] + else: + stuname = '' + if form.cleaned_data['ctc']: + ctc = form.cleaned_data['ctc'] + else: + ctc = 0 + if form.cleaned_data['company']: + cname = form.cleaned_data['company'] + else: + cname = '' + if form.cleaned_data['roll']: + rollno = form.cleaned_data['roll'] + else: + rollno = '' + request.session['mn_pbi_stuname'] = stuname + request.session['mn_pbi_ctc'] = ctc + request.session['mn_pbi_cname'] = cname + request.session['mn_pbi_rollno'] = rollno + placementstatus_pbi = PlacementStatus.objects.select_related('unique_id','notify_id').filter( + Q(notify_id__in=NotifyStudent.objects.filter( + Q(placement_type="PBI", + company_name__icontains=cname, + ctc__gte=ctc)), + unique_id__in=Student.objects.filter( + (Q(id__in=ExtraInfo.objects.filter( + Q(user__in=User.objects.filter( + Q(first_name__icontains=stuname)), + id__icontains=rollno))))))).order_by('id') + + total_query = placementstatus_pbi.count() + + if total_query > 30: + no_pagination = 1 + paginator = Paginator(placementstatus_pbi, 30) + page = request.GET.get('pbi_page', 1) + placementstatus_pbi = paginator.page(page) + page = int(page) + total_page = int(page + 3) + + if page<(paginator.num_pages-3): + if total_query > 30 and total_query <=60: + page_range = range(1, 3) + else: + page_range = range(1, total_page+1) + + if page >= 5: + is_disabled = 1 + page_range = range(page-2, total_page) + else: + if page >= 5: + is_disabled = 1 + page_range = range(page-2, paginator.num_pages+1) + else: + page_range = range(1, paginator.num_pages+1) + else: + no_pagination = 0 + else: + if request.GET.get('pbi_page') != None: + mnpbi_tab = 1 + mnpbi_post = 1 + no_pagination = 1 + try: + placementstatus_pbi = PlacementStatus.objects.select_related('unique_id','notify_id').filter( + Q(notify_id__in=NotifyStudent.objects.filter( + Q(placement_type="PBI", + company_name__icontains=request.session['mn_pbi_cname'], + ctc__gte=request.session['mn_pbi_ctc'])), + unique_id__in=Student.objects.filter( + (Q(id__in=ExtraInfo.objects.filter( + Q(user__in=User.objects.filter( + Q(first_name__icontains=request.session['mn_pbi_stuname'])), + id__icontains=request.session['mn_pbi_rollno'])) + ))))) + except: + placementstatus_pbi = '' + + if placementstatus_pbi != '': + total_query = placementstatus_pbi.count() + else: + total_query = 0 + if total_query > 30: + paginator = Paginator(placementstatus_pbi, 30) + page = request.GET.get('pbi_page', 1) + placementstatus_pbi = paginator.page(page) + page = int(page) + total_page = int(page + 3) + + if page<(paginator.num_pages-3): + if total_query > 30 and total_query <=60: + page_range = range(1, 3) + else: + page_range = range(1, total_page+1) + + if page >= 5: + is_disabled = 1 + page_range = range(page-2, total_page) + else: + if page >= 5: + is_disabled = 1 + page_range = range(page-2, paginator.num_pages+1) + else: + page_range = range(1, paginator.num_pages+1) + else: + no_pagination = 0 + + + if 'pdf_gen_invitation_status' in request.POST: + + placementstatus = None + if 'pdf_gen_invitation_status_placement' in request.POST: + stuname = request.session['mn_stuname'] + ctc = request.session['mn_ctc'] + cname = request.session['mn_cname'] + rollno = request.session['mn_rollno'] + + placementstatus = PlacementStatus.objects.select_related('unique_id','notify_id').filter(Q(notify_id__in=NotifyStudent.objects.filter + (Q(placement_type="PLACEMENT", + company_name__icontains=cname, + ctc__gte=ctc)), + unique_id__in=Student.objects.filter + ((Q(id__in=ExtraInfo.objects.filter + (Q(user__in=User.objects.filter + (Q(first_name__icontains=stuname)), + id__icontains=rollno)) + ))))) + + if 'pdf_gen_invitation_status_pbi' in request.POST: + stuname = request.session['mn_pbi_stuname'] + ctc = request.session['mn_pbi_ctc'] + cname = request.session['mn_pbi_cname'] + rollno = request.session['mn_pbi_rollno'] + + placementstatus = PlacementStatus.objects.select_related('unique_id','notify_id').filter( + Q(notify_id__in=NotifyStudent.objects.filter( + Q(placement_type="PBI", + company_name__icontains=cname, + ctc__gte=ctc)), + unique_id__in=Student.objects.filter( + (Q(id__in=ExtraInfo.objects.filter( + Q(user__in=User.objects.filter( + Q(first_name__icontains=stuname)), + id__icontains=rollno))))))).order_by('id') + + context = { + 'placementstatus' : placementstatus + } + + return render_to_pdf('placementModule/pdf_invitation_status.html', context) + + if 'excel_gen_invitation_status' in request.POST: + + placementstatus = None + if 'excel_gen_invitation_status_placement' in request.POST: + stuname = request.session['mn_stuname'] + ctc = request.session['mn_ctc'] + cname = request.session['mn_cname'] + rollno = request.session['mn_rollno'] + + placementstatus = PlacementStatus.objects.select_related('unique_id','notify_id').filter(Q(notify_id__in=NotifyStudent.objects.filter + (Q(placement_type="PLACEMENT", + company_name__icontains=cname, + ctc__gte=ctc)), + unique_id__in=Student.objects.filter + ((Q(id__in=ExtraInfo.objects.filter + (Q(user__in=User.objects.filter + (Q(first_name__icontains=stuname)), + id__icontains=rollno)) + ))))) + + if 'excel_gen_invitation_status_pbi' in request.POST: + stuname = request.session['mn_pbi_stuname'] + ctc = request.session['mn_pbi_ctc'] + cname = request.session['mn_pbi_cname'] + rollno = request.session['mn_pbi_rollno'] + + placementstatus = PlacementStatus.objects.select_related('unique_id','notify_id').filter( + Q(notify_id__in=NotifyStudent.objects.filter( + Q(placement_type="PBI", + company_name__icontains=cname, + ctc__gte=ctc)), + unique_id__in=Student.objects.filter( + (Q(id__in=ExtraInfo.objects.filter( + Q(user__in=User.objects.filter( + Q(first_name__icontains=stuname)), + id__icontains=rollno))))))).order_by('id') + + context = { + 'placementstatus' : placementstatus + } + + + return export_to_xls_invitation_status(placementstatus) + + form1 = SearchStudentRecord(initial={}) + form9 = ManagePbiRecord(initial={}) + form11 = ManagePlacementRecord(initial={}) + form13 = SendInvite(initial={}) + current1 = HoldsDesignation.objects.filter(Q(working=user, designation__name="placement chairman")) + current2 = HoldsDesignation.objects.filter(Q(working=user, designation__name="placement officer")) + + context = { + 'form1': form1, + 'form9': form9, + 'form11': form11, + 'form13': form13, + 'invitation_status_tab': invitation_status_tab, + 'mnplacement_post': mnplacement_post, + 'mnpbi_tab': mnpbi_tab, + 'mnplacement_tab': mnplacement_tab, + 'placementstatus_placement': placementstatus_placement, + 'placementstatus_pbi': placementstatus_pbi, + 'current1': current1, + 'current2': current2, + 'strecord_tab': strecord_tab, + 'mnpbi_post': mnpbi_post, + 'page_range': page_range, + 'paginator': paginator, + 'no_pagination': no_pagination, + 'is_disabled': is_disabled, + } + + return render(request, 'placementModule/studentrecords.html', context) + + +@login_required +def student_records(request): + ''' + function for searching the records of student + ''' + if request.user.is_staff==True: + user = request.user + strecord_tab = 1 + no_pagination = 0 + is_disabled = 0 + paginator = '' + page_range = '' + mnplacement_tab = 1 + + form1 = SearchStudentRecord(initial={}) + form9 = ManagePbiRecord(initial={}) + form11 = ManagePlacementRecord(initial={}) + form13 = SendInvite(initial={}) + current1 = HoldsDesignation.objects.filter(Q(working=user, designation__name="placement chairman")) + current2 = HoldsDesignation.objects.filter(Q(working=user, designation__name="placement officer")) + + # querying the students details a/c to the input data + if 'recordsubmit' in request.POST: + student_record_check = 1 + form1 = SearchStudentRecord(request.POST) + if form1.is_valid(): + if form1.cleaned_data['name']: + name = form1.cleaned_data['name'] + else: + name = '' + if form1.cleaned_data['rollno']: + rollno = form1.cleaned_data['rollno'] + else: + rollno = '' + + programme = form1.cleaned_data['programme'] + + department = [] + if form1.cleaned_data['dep_btech']: + department.extend(form1.cleaned_data['dep_btech']) + if form1.cleaned_data['dep_mtech']: + department.extend(form1.cleaned_data['dep_mtech']) + if form1.cleaned_data['dep_bdes']: + department.extend(form1.cleaned_data['dep_bdes']) + if form1.cleaned_data['dep_mdes']: + department.extend(form1.cleaned_data['dep_mdes']) + if form1.cleaned_data['dep_phd']: + department.extend(form1.cleaned_data['dep_phd']) + + if form1.cleaned_data['cpi']: + cpi = form1.cleaned_data['cpi'] + else: + cpi = 0 + debar = form1.cleaned_data['debar'] + placed_type = form1.cleaned_data['placed_type'] + + request.session['name'] = name + request.session['rollno'] = rollno + request.session['programme'] = programme + request.session['department'] = department + request.session['cpi'] = str(cpi) + request.session['debar'] = debar + request.session['placed_type'] = placed_type + + + students = Student.objects.filter( + Q(id__in=ExtraInfo.objects.filter(Q( + user__in=User.objects.filter(Q(first_name__icontains=name)), + department__in=DepartmentInfo.objects.filter(Q(name__in=department)), + id__icontains=rollno)), + programme=programme, + cpi__gte=cpi)).filter(Q(pk__in=StudentPlacement.objects.filter( + Q(debar=debar, placed_type=placed_type)).values('unique_id_id'))).order_by('id') + + # pagination stuff starts from here + st = students + student_record_check= 1 + total_query = students.count() + + if total_query > 30: + no_pagination = 1 + paginator = Paginator(students, 30) + page = request.GET.get('page', 1) + students = paginator.page(page) + page = int(page) + total_page = int(page + 3) + + if page<(paginator.num_pages-3): + if total_query > 30 and total_query <=60: + page_range = range(1, 3) + else: + page_range = range(1, total_page+1) + + if page >= 5: + is_disabled = 1 + page_range = range(page-2, total_page) + else: + if page >= 5: + is_disabled = 1 + page_range = range(page-2, paginator.num_pages+1) + else: + page_range = range(1, paginator.num_pages+1) + else: + no_pagination = 0 + else: + # when the request came from pagintion with some page no. + if request.GET.get('page') != None: + try: + students = Student.objects.filter( + Q(id__in=ExtraInfo.objects.filter( + Q(user__in=User.objects.filter( + Q(first_name__icontains=request.session['name']) + ), + department__in=DepartmentInfo.objects.filter( + Q(name__in=request.session['department']) + ), + id__icontains=request.session['rollno'] + ) + ), + programme=request.session['programme'], + cpi__gte=decimal.Decimal(request.session['cpi']))).filter(Q(pk__in=StudentPlacement.objects.filter(Q(debar=request.session['debar'], + placed_type=request.session['placed_type'])).values('unique_id_id'))).order_by('id') + except: + students = '' + + if students != '': + total_query = students.count() + else: + total_query = 0 + + if total_query > 30: + no_pagination = 1 + paginator = Paginator(students, 30) + page = request.GET.get('page', 1) + students = paginator.page(page) + page = int(page) + total_page = int(page + 3) + + if page<(paginator.num_pages-3): + if total_query > 30 and total_query <=60: + page_range = range(1, 3) + else: + page_range = range(1, total_page+1) + + if page >= 5: + is_disabled = 1 + page_range = range(page-2, total_page) + else: + if page >= 5: + is_disabled = 1 + page_range = range(page-2, paginator.num_pages+1) + else: + page_range = range(1, paginator.num_pages+1) + else: + no_pagination = 0 + else: + students = '' + + if 'debar' in request.POST: + spid = request.POST['debar'] + sr = StudentPlacement.objects.get(Q(pk=spid)) + sr.debar = "DEBAR" + sr.save() + if 'undebar' in request.POST: + spid = request.POST['undebar'] + sr = StudentPlacement.objects.get(Q(pk=spid)) + sr.debar = "NOT DEBAR" + sr.save() + + # pdf generation logic + if 'pdf_gen_std_record' in request.POST: + + name = request.session['name'] + rollno = request.session['rollno'] + programme = request.session['programme'] + department = request.session['department'] + cpi = int(request.session['cpi']) + debar = request.session['debar'] + placed_type = request.session['placed_type'] + + students = Student.objects.filter( + Q(id__in=ExtraInfo.objects.filter(Q( + user__in=User.objects.filter(Q(first_name__icontains=name)), + department__in=DepartmentInfo.objects.filter(Q(name__in=department)), + id__icontains=rollno)), + programme=programme, + cpi__gte=cpi)).filter(Q(pk__in=StudentPlacement.objects.filter( + Q(debar=debar, placed_type=placed_type)).values('unique_id_id'))).order_by('id') + + context = { + 'students' : students + } + + + return render_to_pdf('placementModule/pdf_student_record.html', context) + + # excel generation logic + if 'excel_gen_std_record' in request.POST: + + name = request.session['name'] + rollno = request.session['rollno'] + programme = request.session['programme'] + department = request.session['department'] + cpi = int(request.session['cpi']) + debar = request.session['debar'] + placed_type = request.session['placed_type'] + + students = Student.objects.filter( + Q(id__in=ExtraInfo.objects.filter(Q( + user__in=User.objects.filter(Q(first_name__icontains=name)), + department__in=DepartmentInfo.objects.filter(Q(name__in=department)), + id__icontains=rollno)), + programme=programme, + cpi__gte=cpi)).filter(Q(pk__in=StudentPlacement.objects.filter( + Q(debar=debar, placed_type=placed_type)).values('unique_id_id'))).order_by('id') + + context = { + 'students' : students + } + + + return export_to_xls_std_records(students) + + + # for sending the invite to students for particular schedule + if 'sendinvite' in request.POST: + # invitecheck=1; + + form13 = SendInvite(request.POST) + + if form13.is_valid(): + if form13.cleaned_data['company']: + if form13.cleaned_data['rollno']: + rollno = form13.cleaned_data['rollno'] + else: + rollno = '' + + programme = form13.cleaned_data['programme'] + + department = [] + if form13.cleaned_data['dep_btech']: + department.extend(form13.cleaned_data['dep_btech']) + if form13.cleaned_data['dep_mtech']: + department.extend(form13.cleaned_data['dep_mtech']) + if form13.cleaned_data['dep_bdes']: + department.extend(form13.cleaned_data['dep_bdes']) + if form13.cleaned_data['dep_mdes']: + department.extend(form13.cleaned_data['dep_mdes']) + if form13.cleaned_data['dep_phd']: + department.extend(form13.cleaned_data['dep_phd']) + + + if form13.cleaned_data['cpi']: + cpi = form13.cleaned_data['cpi'] + else: + cpi = 0 + + if form13.cleaned_data['no_of_days']: + no_of_days = form13.cleaned_data['no_of_days'] + else: + no_of_days = 10 + + + comp = form13.cleaned_data['company'] + + notify = NotifyStudent.objects.get(company_name=comp.company_name, + placement_type=comp.placement_type) + + students = Student.objects.filter( + Q( + id__in = ExtraInfo.objects.filter( + Q( + department__in = DepartmentInfo.objects.filter(Q(name__in=department)), + id__icontains = rollno + ) + ), + programme = programme, + cpi__gte = cpi + ) + ).exclude(id__in = PlacementStatus.objects.select_related('unique_id','notify_id').filter( + notify_id=notify).values_list('unique_id', flat=True)) + + PlacementStatus.objects.bulk_create( [PlacementStatus(notify_id=notify, + unique_id=student, no_of_days=no_of_days) for student in students] ) + + for st in students: + #print(request.user, '-----------------------', st.id.user,'-----------------') + placement_cell_notif(request.user, st.id.user, "") + + students = '' + messages.success(request, 'Notification Sent') + else: + messages.error(request, 'Problem Occurred!! Please Try Again!!') + + context = { + 'form1': form1, + 'form9': form9, + 'form11': form11, + 'form13': form13, + 'current1': current1, + 'current2': current2, + 'mnplacement_tab': mnplacement_tab, + 'strecord_tab': strecord_tab, + 'students': students, + 'page_range': page_range, + 'paginator': paginator, + 'no_pagination': no_pagination, + 'is_disabled': is_disabled, + } + + return render(request, 'placementModule/studentrecords.html', context) + return redirect('/placement') + + +@login_required +def manage_records(request): + ''' + function to manage the records + - can add the records under placement | pbi | higher studies + - can also search the records under placement | pbi | higher studies + ''' + user = request.user + mnrecord_tab = 1 + pagination_placement = 0 + pagination_pbi = 0 + pagination_higher = 0 + is_disabled = 0 + years = None + records = None + paginator = '' + page_range = '' + pbirecord = None + placementrecord = None + higherrecord = None + officer_statistics_past_pbi_search = 0 + officer_statistics_past_higher_search = 0 + + profile = get_object_or_404(ExtraInfo, Q(user=user)) + studentrecord = StudentRecord.objects.all() + + years = PlacementRecord.objects.filter(~Q(placement_type="HIGHER STUDIES")).values('year').annotate(Count('year')) + records = PlacementRecord.objects.values('name', 'year', 'ctc', 'placement_type').annotate(Count('name'), Count('year'), Count('placement_type'), Count('ctc')) + + + form2 = SearchPlacementRecord(initial={}) + form3 = SearchPbiRecord(initial={}) + form4 = SearchHigherRecord(initial={}) + current1 = HoldsDesignation.objects.filter(Q(working=user, designation__name="placement chairman")) + current2 = HoldsDesignation.objects.filter(Q(working=user, designation__name="placement officer")) + + current = HoldsDesignation.objects.filter(Q(working=user, designation__name="student")) + + if len(current) == 0: + current = None + + + try: + # for adding the new data for student under higher studies category + if 'studenthigheraddsubmit' in request.POST: + officer_statistics_past_higher_add = 1 + form = SearchHigherRecord(request.POST) + if form.is_valid(): + rollno = form.cleaned_data['roll'] + uname = form.cleaned_data['uname'] + test_score = form.cleaned_data['test_score'] + test_type = form.cleaned_data['test_type'] + year = form.cleaned_data['year'] + placementr = PlacementRecord.objects.create(year=year, name=uname, + placement_type="HIGHER STUDIES", + test_type=test_type, + test_score=test_score) + studentr = StudentRecord.objects.select_related('unique_id','record_id').create(record_id=placementr, + unique_id=Student.objects.get + ((Q(id=ExtraInfo.objects.get + (Q(id=rollno)))))) + studentr.save() + placementr.save() + messages.success(request, 'Record Added Successfully!!') + + # for adding the new data for student under pbi category + if 'studentpbiaddsubmit' in request.POST: + officer_statistics_past_pbi_add = 1 + form = SearchPbiRecord(request.POST) + if form.is_valid(): + rollno = form.cleaned_data['roll'] + ctc = form.cleaned_data['ctc'] + year = form.cleaned_data['year'] + cname = form.cleaned_data['cname'] + placementr = PlacementRecord.objects.create(year=year, ctc=ctc, + placement_type="PBI", + name=cname) + studentr = StudentRecord.objects.select_related('unique_id','record_id').create(record_id=placementr, + unique_id=Student.objects.get + ((Q(id=ExtraInfo.objects.get + (Q(id=rollno)))))) + studentr.save() + placementr.save() + messages.success(request, 'Record Added Successfully!!') + + # for adding the new data for student under placement category + if 'studentplacementaddsubmit' in request.POST: + officer_statistics_past_add = 1 + form = SearchPlacementRecord(request.POST) + if form.is_valid(): + rollno = form.cleaned_data['roll'] + ctc = form.cleaned_data['ctc'] + year = form.cleaned_data['year'] + cname = form.cleaned_data['cname'] + placementr = PlacementRecord.objects.create(year=year, ctc=ctc, + placement_type="PLACEMENT", + name=cname) + studentr = StudentRecord.objects.select_related('unique_id','record_id').create(record_id=placementr, + unique_id=Student.objects.get + ((Q(id=ExtraInfo.objects.get + (Q(id=rollno)))))) + studentr.save() + placementr.save() + messages.success(request, 'Record Added Successfully!!') + + # for searching the student details under placement category + if 'studentplacementrecordsubmit' in request.POST: + officer_statistics_past = 1 + form = SearchPlacementRecord(request.POST) + if form.is_valid(): + if form.cleaned_data['stuname']: + stuname = form.cleaned_data['stuname'] + else: + stuname = '' + if form.cleaned_data['ctc']: + ctc = form.cleaned_data['ctc'] + else: + ctc = 0 + if form.cleaned_data['cname']: + cname = form.cleaned_data['cname'] + else: + cname = '' + if form.cleaned_data['roll']: + rollno = form.cleaned_data['roll'] + else: + rollno = '' + if form.cleaned_data['year']: + year = form.cleaned_data['year'] + s = Student.objects.filter((Q(id__in=ExtraInfo.objects.filter + (Q(user__in=User.objects.filter + (Q(first_name__icontains=stuname)), + id__icontains=rollno)) + ))) + + p = PlacementRecord.objects.filter(Q(placement_type="PLACEMENT", name__icontains=cname, ctc__gte=ctc, year=year)) + + placementrecord = StudentRecord.objects.select_related('unique_id','record_id').select_related('unique_id','record_id').filter(Q(record_id__in=PlacementRecord.objects.filter(Q(placement_type="PLACEMENT", name__icontains=cname, ctc__gte=ctc, year=year)), unique_id__in=Student.objects.filter((Q(id__in=ExtraInfo.objects.filter(Q(user__in=User.objects.filter(Q(first_name__icontains=stuname)),id__icontains=rollno))))))) + else: + s = Student.objects.filter((Q(id__in=ExtraInfo.objects.filter + (Q(user__in=User.objects.filter + (Q(first_name__icontains=stuname)), + id__icontains=rollno)) + ))) + + p = PlacementRecord.objects.filter(Q(placement_type="PLACEMENT", name__icontains=cname, ctc__gte=ctc)) + + placementrecord = StudentRecord.objects.select_related('unique_id','record_id').filter( + Q(record_id__in=PlacementRecord.objects.filter( + Q(placement_type="PLACEMENT", name__icontains=cname, ctc__gte=ctc)), + unique_id__in=Student.objects.filter( + (Q(id__in=ExtraInfo.objects.filter( + Q(user__in=User.objects.filter(Q(first_name__icontains=stuname)), + id__icontains=rollno))))))) + + request.session['stuname'] = stuname + request.session['ctc'] = ctc + request.session['cname'] = cname + request.session['rollno'] = rollno + request.session['year'] = form.cleaned_data['year'] + + total_query = placementrecord.count() + + if total_query > 30: + pagination_placement = 1 + paginator = Paginator(placementrecord, 30) + page = request.GET.get('page', 1) + placementrecord = paginator.page(page) + page = int(page) + total_page = int(page + 3) + + if page<(paginator.num_pages-3): + if total_query > 30 and total_query <=60: + page_range = range(1, 3) + else: + page_range = range(1, total_page+1) + + if page >= 5: + is_disabled = 1 + page_range = range(page-2, total_page) + else: + if page >= 5: + is_disabled = 1 + page_range = range(page-2, paginator.num_pages+1) + else: + page_range = range(1, paginator.num_pages+1) + else: + pagination_placement = 0 + else: + if request.GET.get('page') != None: + try: + if request.session['year']: + s = Student.objects.filter( + (Q(id__in=ExtraInfo.objects.filter( + Q(user__in=User.objects.filter( + Q(first_name__icontains=request.session['stuname'])), + id__icontains=request.session['rollno'])) + ))) + + p = PlacementRecord.objects.filter( + Q(placement_type="PLACEMENT", + name__icontains=request.session['cname'], + ctc__gte=request.session['ctc'], + year=request.session['year'])) + + placementrecord = StudentRecord.objects.select_related('unique_id','record_id').filter( + Q(record_id__in=PlacementRecord.objects.filter( + Q(placement_type="PLACEMENT", + name__icontains=request.session['cname'], + ctc__gte=request.session['ctc'], + year=request.session['year'])), + unique_id__in=Student.objects.filter( + (Q(id__in=ExtraInfo.objects.filter( + Q(user__in=User.objects.filter( + Q(first_name__icontains=request.session['stuname'])), + id__icontains=request.session['rollno']))))))) + else: + s = Student.objects.filter((Q(id__in=ExtraInfo.objects.filter + (Q(user__in=User.objects.filter + (Q(first_name__icontains=request.session['stuname'])), + id__icontains=request.session['rollno'])) + ))) + + p = PlacementRecord.objects.filter( + Q(placement_type="PLACEMENT", + name__icontains=request.session['cname'], + ctc__gte=request.session['ctc'])) + + placementrecord = StudentRecord.objects.select_related('unique_id','record_id').filter( + Q(record_id__in=PlacementRecord.objects.filter( + Q(placement_type="PLACEMENT", + name__icontains=request.session['cname'], + ctc__gte=request.session['ctc'])), + unique_id__in=Student.objects.filter( + (Q(id__in=ExtraInfo.objects.filter( + Q(user__in=User.objects.filter( + Q(first_name__icontains=request.session['stuname'])), + id__icontains=request.session['rollno']))))))) + except: + placementrecord = '' + + if placementrecord != '': + total_query = placementrecord.count() + else: + total_query = 0 + + if total_query > 30: + pagination_placement = 1 + paginator = Paginator(placementrecord, 30) + page = request.GET.get('page', 1) + placementrecord = paginator.page(page) + page = int(page) + total_page = int(page + 3) + + if page<(paginator.num_pages-3): + if total_query > 30 and total_query <=60: + page_range = range(1, 3) + else: + page_range = range(1, total_page+1) + + if page >= 5: + is_disabled = 1 + page_range = range(page-2, total_page) + else: + if page >= 5: + is_disabled = 1 + page_range = range(page-2, paginator.num_pages+1) + else: + page_range = range(1, paginator.num_pages+1) + else: + pagination_placement = 0 + else: + placementrecord = '' + + # for searching the student details under pbi category + if 'studentpbirecordsubmit' in request.POST: + officer_statistics_past_pbi_search = 1 + form = SearchPbiRecord(request.POST) + if form.is_valid(): + if form.cleaned_data['stuname']: + stuname = form.cleaned_data['stuname'] + else: + stuname = '' + if form.cleaned_data['ctc']: + ctc = form.cleaned_data['ctc'] + else: + ctc = 0 + if form.cleaned_data['cname']: + cname = form.cleaned_data['cname'] + else: + cname = '' + if form.cleaned_data['roll']: + rollno = form.cleaned_data['roll'] + else: + rollno = '' + if form.cleaned_data['year']: + year = form.cleaned_data['year'] + pbirecord = StudentRecord.objects.select_related('unique_id','record_id').filter(Q(record_id__in=PlacementRecord.objects.filter + (Q(placement_type="PBI", + name__icontains=cname, + ctc__gte=ctc, year=year)), + unique_id__in=Student.objects.filter + ((Q(id__in=ExtraInfo.objects.filter + (Q(user__in=User.objects.filter + (Q(first_name__icontains=stuname)), + id__icontains=rollno)) + ))))) + else: + pbirecord = StudentRecord.objects.select_related('unique_id','record_id').filter(Q(record_id__in=PlacementRecord.objects.filter + (Q(placement_type="PBI", + name__icontains=cname, + ctc__gte=ctc)), + unique_id__in=Student.objects.filter + ((Q(id__in=ExtraInfo.objects.filter + (Q(user__in=User.objects.filter + (Q(first_name__icontains=stuname)), + id__icontains=rollno)) + ))))) + request.session['stuname'] = stuname + request.session['ctc'] = ctc + request.session['cname'] = cname + request.session['rollno'] = rollno + request.session['year'] = form.cleaned_data['year'] + + total_query = pbirecord.count() + + if total_query > 30: + pagination_pbi = 1 + paginator = Paginator(pbirecord, 30) + page = request.GET.get('page', 1) + pbirecord = paginator.page(page) + page = int(page) + total_page = int(page + 3) + + if page<(paginator.num_pages-3): + if total_query > 30 and total_query <=60: + page_range = range(1, 3) + else: + page_range = range(1, total_page+1) + + if page >= 5: + is_disabled = 1 + page_range = range(page-2, total_page) + else: + if page >= 5: + is_disabled = 1 + page_range = range(page-2, paginator.num_pages+1) + else: + page_range = range(1, paginator.num_pages+1) + else: + pagination_pbi = 0 + else: + if request.GET.get('page') != None: + try: + if request.session['year']: + pbirecord = StudentRecord.objects.select_related('unique_id','record_id').filter( + Q(record_id__in=PlacementRecord.objects.filter( + Q(placement_type="PBI", + name__icontains=request.session['cname'], + ctc__gte=ctc, year=request.session['year'])), + unique_id__in=Student.objects.filter(( + Q(id__in=ExtraInfo.objects.filter( + Q(user__in=User.objects.filter( + Q(first_name__icontains=request.session['stuname'])), + id__icontains=request.session['rollno']))))))) + else: + pbirecord = StudentRecord.objects.select_related('unique_id','record_id').filter( + Q(record_id__in=PlacementRecord.objects.filter(Q(placement_type="PBI", + name__icontains=request.session['cname'], + ctc__gte=request.session['ctc'])), + unique_id__in=Student.objects.filter( + (Q(id__in=ExtraInfo.objects.filter( + Q(user__in=User.objects.filter( + Q(first_name__icontains=request.session['stuname'])), + id__icontains=request.session['rollno']))))))) + except: + print('except') + pbirecord = '' + + if pbirecord != '': + total_query = pbirecord.count() + else: + total_query = 0 + + if total_query > 30: + pagination_pbi = 1 + paginator = Paginator(pbirecord, 30) + page = request.GET.get('page', 1) + pbirecord = paginator.page(page) + page = int(page) + total_page = int(page + 3) + + if page<(paginator.num_pages-3): + if total_query > 30 and total_query <=60: + page_range = range(1, 3) + else: + page_range = range(1, total_page+1) + + if page >= 5: + is_disabled = 1 + page_range = range(page-2, total_page) + else: + if page >= 5: + is_disabled = 1 + page_range = range(page-2, paginator.num_pages+1) + else: + page_range = range(1, paginator.num_pages+1) + else: + pagination_pbi = 0 + else: + pbirecord = '' + + # for searching the student details under higher studies category + if 'studenthigherrecordsubmit' in request.POST: + officer_statistics_past_higher_search = 1 + form = SearchHigherRecord(request.POST) + if form.is_valid(): + if form.cleaned_data['stuname']: + stuname = form.cleaned_data['stuname'] + else: + stuname = '' + if form.cleaned_data['test_type']: + test_type = form.cleaned_data['test_type'] + else: + test_type = '' + if form.cleaned_data['uname']: + uname = form.cleaned_data['uname'] + else: + uname = '' + if form.cleaned_data['test_score']: + test_score = form.cleaned_data['test_score'] + else: + test_score = 0 + if form.cleaned_data['roll']: + rollno = form.cleaned_data['roll'] + else: + rollno = '' + if form.cleaned_data['year']: + year = form.cleaned_data['year'] + higherrecord = StudentRecord.objects.select_related('unique_id','record_id').filter(Q(record_id__in=PlacementRecord.objects.filter + (Q(placement_type="HIGHER STUDIES", + test_type__icontains=test_type, + name__icontains=uname, year=year, + test_score__gte=test_score)), + unique_id__in=Student.objects.filter + ((Q(id__in=ExtraInfo.objects.filter + (Q(user__in=User.objects.filter + (Q(first_name__icontains=stuname)), + id__icontains=rollno)) + ))))) + else: + higherrecord = StudentRecord.objects.select_related('unique_id','record_id').filter( + Q(record_id__in=PlacementRecord.objects.filter + (Q(placement_type="HIGHER STUDIES", + test_type__icontains=test_type, + name__icontains=uname, + test_score__gte=test_score)), + unique_id__in=Student.objects.filter + ((Q(id__in=ExtraInfo.objects.filter + (Q(user__in=User.objects.filter + (Q(first_name__icontains=stuname)), + id__icontains=rollno)) + ))))) + request.session['stuname'] = stuname + request.session['test_score'] = test_score + request.session['uname'] = uname + request.session['test_type'] = test_type + request.session['rollno'] = rollno + request.session['year'] = form.cleaned_data['year'] + + total_query = higherrecord.count() + + if total_query > 30: + pagination_higher = 1 + paginator = Paginator(higherrecord, 30) + page = request.GET.get('page', 1) + higherrecord = paginator.page(page) + page = int(page) + total_page = int(page+3) + + if page < (paginator.num_pages-3): + if total_query > 30 and total_query <= 60: + page_range = range(1, 3) + else: + page_range = range(1, total_page+1) + + if page >= 5: + is_disabled = 1 + page_range = range(page-2, total_page) + else: + if page >= 5: + is_disabled = 1 + page_range = range(page-2, paginator.num_pages+1) + else: + page_range = range(page-2, paginator.num_pages+1) + else: + pagination_higher = 0 + else: + if request.GET.get('page') != None: + try: + if request.session['year']: + higherrecord = StudentRecord.objects.select_related('unique_id','record_id').filter( + Q(record_id__in=PlacementRecord.objects.filter( + Q(placement_type="HIGHER STUDIES", + test_type__icontains=request.session['test_type'], + name__icontains=request.session['uname'], + year=request.session['year'], + test_score__gte=request.session['test_score'])), + unique_id__in=Student.objects.filter( + (Q(id__in=ExtraInfo.objects.filter( + Q(user__in=User.objects.filter( + Q(first_name__icontains=request.session['stuname'])), + id__icontains=request.session['rollno'])) + ))))) + else: + higherrecord = StudentRecord.objects.select_related('unique_id','record_id').filter( + Q(record_id__in=PlacementRecord.objects.filter + (Q(placement_type="HIGHER STUDIES", + test_type__icontains=request.session['test_type'], + name__icontains=request.session['uname'], + test_score__gte=request.session['test_score'])), + unique_id__in=Student.objects.filter + ((Q(id__in=ExtraInfo.objects.filter + (Q(user__in=User.objects.filter + (Q(first_name__icontains=request.session['stuname'])), + id__icontains=request.session['rollno'])) + ))))) + except: + print('except') + higherrecord = '' + + if higherrecord != '': + total_query = higherrecord.count() + else: + total_query = 0 + + if total_query > 30: + no_pagination = 1 + paginator = Paginator(higherrecord, 30) + page = request.GET.get('page', 1) + higherrecord = paginator.page(page) + page = int(page) + total_page = int(page + 3) + + if page<(paginator.num_pages-3): + if total_query > 30 and total_query <=60: + page_range = range(1, 3) + else: + page_range = range(1, total_page+1) + + if page >= 5: + is_disabled = 1 + page_range = range(page-2, total_page) + else: + if page >= 5: + is_disabled = 1 + page_range = range(page-2, paginator.num_pages+1) + else: + page_range = range(1, paginator.num_pages+1) + else: + no_pagination = 0 + else: + higherrecord = '' + + + except Exception as e: + messages.error(request, "Problem Occurred!!! Please Try Again with Correct Info!!") + print(e) + + + context = { + 'form2' : form2, + 'form3' : form3, + 'form4' : form4, + 'current' : current, + 'current1' : current1, + 'current2' : current2, + 'mnrecord_tab' : mnrecord_tab, + 'pbirecord' : pbirecord, + 'placementrecord' : placementrecord, + 'higherrecord' : higherrecord, + 'years' : years, + 'records' : records, + 'page_range': page_range, + 'paginator': paginator, + 'pagination_placement': pagination_placement, + 'pagination_pbi': pagination_pbi, + 'pagination_higher': pagination_higher, + 'is_disabled': is_disabled, + 'officer_statistics_past_pbi_search': officer_statistics_past_pbi_search, + 'officer_statistics_past_higher_search': officer_statistics_past_higher_search + } + + return render(request, 'placementModule/managerecords.html', context) + + + + +@login_required +def placement_statistics(request): + ''' + logic of the view shown under Placement Statistics tab + ''' + user = request.user + + statistics_tab = 1 + strecord_tab=1 + delete_operation = 0 + pagination_placement = 0 + pagination_pbi = 0 + pagination_higher = 0 + is_disabled = 0 + paginator = '' + page_range = '' + officer_statistics_past_pbi_search = 0 + officer_statistics_past_higher_search = 0 + + profile = get_object_or_404(ExtraInfo, Q(user=user)) + studentrecord = StudentRecord.objects.select_related('unique_id','record_id').all() + + years = PlacementRecord.objects.filter(~Q(placement_type="HIGHER STUDIES")).values('year').annotate(Count('year')) + records = PlacementRecord.objects.values('name', 'year', 'ctc', 'placement_type').annotate(Count('name'), Count('year'), Count('placement_type'), Count('ctc')) + + + + + #working here to fetch all placement record + all_records=PlacementRecord.objects.all() + print(all_records) + + + + + + + invitecheck=0 + for r in records: + r['name__count'] = 0 + r['year__count'] = 0 + r['placement_type__count'] = 0 + tcse = dict() + tece = dict() + tme = dict() + tadd = dict() + for y in years: + tcse[y['year']] = 0 + tece[y['year']] = 0 + tme[y['year']] = 0 + for r in records: + if r['year'] == y['year']: + if r['placement_type'] != "HIGHER STUDIES": + for z in studentrecord: + if z.record_id.name == r['name'] and z.record_id.year == r['year'] and z.unique_id.id.department.name == "CSE": + tcse[y['year']] = tcse[y['year']]+1 + r['name__count'] = r['name__count']+1 + if z.record_id.name == r['name'] and z.record_id.year == r['year'] and z.unique_id.id.department.name == "ECE": + tece[y['year']] = tece[y['year']]+1 + r['year__count'] = r['year__count']+1 + if z.record_id.name == r['name'] and z.record_id.year == r['year'] and z.unique_id.id.department.name == "ME": + tme[y['year']] = tme[y['year']]+1 + r['placement_type__count'] = r['placement_type__count']+1 + tadd[y['year']] = tcse[y['year']]+tece[y['year']]+tme[y['year']] + y['year__count'] = [tadd[y['year']], tcse[y['year']], tece[y['year']], tme[y['year']]] + + form2 = SearchPlacementRecord(initial={}) + form3 = SearchPbiRecord(initial={}) + form4 = SearchHigherRecord(initial={}) + + + current1 = HoldsDesignation.objects.filter(Q(working=user, designation__name="placement chairman")) + current2 = HoldsDesignation.objects.filter(Q(working=user, designation__name="placement officer")) + current = HoldsDesignation.objects.filter(Q(working=user, designation__name="student")) + + if len(current1)!=0 or len(current2)!=0: + delete_operation = 1 + if len(current) == 0: + current = None + pbirecord= '' + placementrecord= '' + higherrecord= '' + total_query=0 + total_query1 = 0 + total_query2= 0 + p="" + p1="" + p2="" + placement_search_record=" " + pbi_search_record=" " + higher_search_record=" " + # results of the searched query under placement tab + if 'studentplacementrecordsubmit' in request.POST: + officer_statistics_past = 1 + form = SearchPlacementRecord(request.POST) + if form.is_valid(): + + + + + print("IS VALID") + + + + #for student name + if form.cleaned_data['stuname']: + stuname = form.cleaned_data['stuname'] + try: + first_name = stuname.split(" ")[0] + last_name = stuname.split(" ")[1] + except Exception as e: + print("Error") + print(e) + first_name = stuname + last_name = '' + else: + stuname = '' + first_name = '' + last_name = '' + + + # for student CTC + if form.cleaned_data['ctc']: + ctc = form.cleaned_data['ctc'] + else: + ctc = 0 + + #for company name + if form.cleaned_data['cname']: + cname = form.cleaned_data['cname'] + else: + cname = '' + + #for student roll + if form.cleaned_data['roll']: + rollno = form.cleaned_data['roll'] + else: + rollno = '' + + #for admission year + if form.cleaned_data['year']: + year = form.cleaned_data['year'] + s = Student.objects.filter((Q(id__in=ExtraInfo.objects.filter( + Q(user__in=User.objects.filter( + first_name__icontains=first_name, + last_name__icontains=last_name), + id__icontains=rollno)) + ))) + + p = PlacementRecord.objects.filter(Q(placement_type="PLACEMENT", name__icontains=stuname, ctc__icontains=ctc, year__icontains=year)) + + + + + """placementrecord = StudentRecord.objects.select_related('unique_id','record_id').filter( + Q(record_id__in=PlacementRecord.objects.filter( + Q(placement_type="PLACEMENT", name__icontains=cname, ctc__gte=ctc, year=year)), + unique_id__in=Student.objects.filter( + (Q(id__in=ExtraInfo.objects.filter( + Q(user__in=User.objects.filter( + first_name__icontains=first_name, + last_name__icontains=last_name, + id__icontains=rollno)))))))) + #print("In if:", placementrecord) + else: + s = Student.objects.filter((Q(id__in=ExtraInfo.objects.filter( + Q(user__in=User.objects.filter( + first_name__icontains=first_name, + last_name__icontains=last_name), + id__icontains=rollno)) + ))) + + p = PlacementRecord.objects.filter(Q(placement_type="PLACEMENT", name__icontains=cname, ctc__gte=ctc)) + print("Agein p:",p) + placementrecord = StudentRecord.objects.select_related('unique_id','record_id').filter( + Q(record_id__in=PlacementRecord.objects.filter( + Q(placement_type="PLACEMENT", name__icontains=cname, ctc__gte=ctc)), + unique_id__in=Student.objects.filter( + (Q(id__in=ExtraInfo.objects.filter( + Q(user__in=User.objects.filter( + first_name__icontains=first_name, + last_name__icontains=last_name), + id__icontains=rollno))))))) + + request.session['first_name'] = first_name + request.session['last_name'] = last_name + request.session['ctc'] = ctc + request.session['cname'] = cname + request.session['rollno'] = rollno + request.session['year'] = form.cleaned_data['year']""" + + print(p) + + + total_query = p.count() + + if total_query > 30: + pagination_placement = 1 + paginator = Paginator(placementrecord, 30) + page = request.GET.get('page', 1) + placementrecord = paginator.page(page) + page = int(page) + total_page = int(page + 3) + + if page<(paginator.num_pages-3): + if total_query > 30 and total_query <=60: + page_range = range(1, 3) + else: + page_range = range(1, total_page+1) + + if page >= 5: + is_disabled = 1 + page_range = range(page-2, total_page) + else: + if page >= 5: + is_disabled = 1 + page_range = range(page-2, paginator.num_pages+1) + else: + page_range = range(1, paginator.num_pages+1) + else: + pagination_placement = 0 + else: + if request.GET.get('page') != None: + try: + if request.session['year']: + s = Student.objects.filter( + (Q(id__in=ExtraInfo.objects.filter( + Q(user__in=User.objects.filter( + Q(first_name__icontains=request.session['first_name'], + last_name__icontains=request.session['last_name'])), + id__icontains=request.session['rollno'])) + ))) + + p = PlacementRecord.objects.filter( + Q(placement_type="PLACEMENT", + name__icontains=request.session['cname'], + ctc__gte=request.session['ctc'], + year=request.session['year'])) + + + placementrecord = StudentRecord.objects.select_related('unique_id','record_id').filter( + Q(record_id__in=PlacementRecord.objects.filter( + Q(placement_type="PLACEMENT", + name__icontains=request.session['cname'], + ctc__gte=request.session['ctc'], + year=request.session['year'])), + unique_id__in=Student.objects.filter( + (Q(id__in=ExtraInfo.objects.filter( + Q(user__in=User.objects.filter( + Q(first_name__icontains=request.session['first_name'], + last_name__icontains=request.session['last_name'])), + id__icontains=request.session['rollno']))))))) + else: + s = Student.objects.filter((Q(id__in=ExtraInfo.objects.filter + (Q(user__in=User.objects.filter + (Q(first_name__icontains=request.session['first_name'], + last_name__icontains=request.session['last_name'])), + id__icontains=request.session['rollno'])) + ))) + + p = PlacementRecord.objects.filter( + Q(placement_type="PLACEMENT", + name__icontains=request.session['cname'], + ctc__gte=request.session['ctc'])) + + placementrecord = StudentRecord.objects.select_related('unique_id','record_id').filter( + Q(record_id__in=PlacementRecord.objects.filter( + Q(placement_type="PLACEMENT", + name__icontains=request.session['cname'], + ctc__gte=request.session['ctc'])), + unique_id__in=Student.objects.filter( + (Q(id__in=ExtraInfo.objects.filter( + Q(user__in=User.objects.filter( + Q(first_name__icontains=request.session['first_name'], + last_name__icontains=request.session['last_name'])), + id__icontains=request.session['rollno']))))))) + except Exception as e: + print(e) + placementrecord = '' + + if placementrecord != '': + total_query = placementrecord.count() + else: + total_query = 0 + no_records=1 + print(placementrecord) + if total_query > 30: + pagination_placement = 1 + paginator = Paginator(placementrecord, 30) + page = request.GET.get('page', 1) + placementrecord = paginator.page(page) + page = int(page) + total_page = int(page + 3) + + if page<(paginator.num_pages-3): + if total_query > 30 and total_query <=60: + page_range = range(1, 3) + else: + page_range = range(1, total_page+1) + + if page >= 5: + is_disabled = 1 + page_range = range(page-2, total_page) + else: + if page >= 5: + is_disabled = 1 + page_range = range(page-2, paginator.num_pages+1) + else: + page_range = range(1, paginator.num_pages+1) + else: + pagination_placement = 0 + else: + placementrecord = '' + + if total_query!=0: + placement_search_record=p + # results of the searched query under pbi tab + if 'studentpbirecordsubmit' in request.POST: + officer_statistics_past_pbi_search = 1 + form = SearchPbiRecord(request.POST) + if form.is_valid(): + if form.cleaned_data['stuname']: + stuname = form.cleaned_data['stuname'] + try: + first_name = stuname.split(" ")[0] + last_name = stuname.split(" ")[1] + except: + first_name = stuname + last_name = '' + else: + stuname = '' + first_name = '' + last_name = '' + if form.cleaned_data['ctc']: + ctc = form.cleaned_data['ctc'] + else: + ctc = 0 + if form.cleaned_data['cname']: + cname = form.cleaned_data['cname'] + else: + cname = '' + if form.cleaned_data['roll']: + rollno = form.cleaned_data['roll'] + else: + rollno = '' + if form.cleaned_data['year']: + year = form.cleaned_data['year'] + pbirecord = StudentRecord.objects.select_related('unique_id','record_id').filter(Q(record_id__in=PlacementRecord.objects.filter + (Q(placement_type="PBI", + name__icontains=cname, + ctc__gte=ctc, year=year)), + unique_id__in=Student.objects.filter + ((Q(id__in=ExtraInfo.objects.filter + (Q(user__in=User.objects.filter + (Q(first_name__icontains=first_name, + last_name__icontains=last_name)), + id__icontains=rollno)) + ))))) + p1 = PlacementRecord.objects.filter( + Q(placement_type="PBI", name__icontains=stuname, ctc__icontains=ctc, year__icontains=year)) + """else: + pbirecord = StudentRecord.objects.select_related('unique_id','record_id').filter(Q(record_id__in=PlacementRecord.objects.filter + (Q(placement_type="PBI", + name__icontains=cname, + ctc__gte=ctc)), + unique_id__in=Student.objects.filter + ((Q(id__in=ExtraInfo.objects.filter + (Q(user__in=User.objects.filter + (Q(first_name__icontains=first_name, + last_name__icontains=last_name)), + id__icontains=rollno)) + ))))) + request.session['first_name'] = first_name + request.session['last_name'] = last_name + request.session['ctc'] = ctc + request.session['cname'] = cname + request.session['rollno'] = rollno + request.session['year'] = form.cleaned_data['year'] +""" + total_query1 = p1.count() + + if total_query1 > 30: + pagination_pbi = 1 + paginator = Paginator(pbirecord, 30) + page = request.GET.get('page', 1) + pbirecord = paginator.page(page) + page = int(page) + total_page = int(page + 3) + + if page<(paginator.num_pages-3): + if total_query1 > 30 and total_query1 <=60: + page_range = range(1, 3) + else: + page_range = range(1, total_page+1) + + if page >= 5: + is_disabled = 1 + page_range = range(page-2, total_page) + else: + if page >= 5: + is_disabled = 1 + page_range = range(page-2, paginator.num_pages+1) + else: + page_range = range(1, paginator.num_pages+1) + else: + pagination_pbi = 0 + else: + if request.GET.get('page') != None: + try: + if request.session['year']: + pbirecord = StudentRecord.objects.select_related('unique_id','record_id').filter( + Q(record_id__in=PlacementRecord.objects.filter( + Q(placement_type="PBI", + name__icontains=request.session['cname'], + ctc__gte=ctc, year=request.session['year'])), + unique_id__in=Student.objects.filter(( + Q(id__in=ExtraInfo.objects.filter( + Q(user__in=User.objects.filter( + Q(first_name__icontains=request.session['first_name'], + last_name__icontains=request.session['last_name'])), + id__icontains=request.session['rollno']))))))) + else: + pbirecord = StudentRecord.objects.select_related('unique_id','record_id').filter( + Q(record_id__in=PlacementRecord.objects.filter(Q(placement_type="PBI", + name__icontains=request.session['cname'], + ctc__gte=request.session['ctc'])), + unique_id__in=Student.objects.filter( + (Q(id__in=ExtraInfo.objects.filter( + Q(user__in=User.objects.filter( + Q(first_name__icontains=request.session['first_name'], + last_name__icontains=request.session['last_name'])), + id__icontains=request.session['rollno']))))))) + except: + print('except') + pbirecord = '' + + if pbirecord != '': + total_query = pbirecord.count() + else: + total_query = 0 + + if total_query > 30: + pagination_pbi = 1 + paginator = Paginator(pbirecord, 30) + page = request.GET.get('page', 1) + pbirecord = paginator.page(page) + page = int(page) + total_page = int(page + 3) + + if page<(paginator.num_pages-3): + if total_query > 30 and total_query <=60: + page_range = range(1, 3) + else: + page_range = range(1, total_page+1) + + if page >= 5: + is_disabled = 1 + page_range = range(page-2, total_page) + else: + if page >= 5: + is_disabled = 1 + page_range = range(page-2, paginator.num_pages+1) + else: + page_range = range(1, paginator.num_pages+1) + else: + pagination_pbi = 0 + else: + pbirecord = '' + if total_query1!=0: + pbi_search_record=p1 + + # results of the searched query under higher studies tab + if 'studenthigherrecordsubmit' in request.POST: + officer_statistics_past_higher_search = 1 + form = SearchHigherRecord(request.POST) + if form.is_valid(): + # getting all the variables send through form + if form.cleaned_data['stuname']: + stuname = form.cleaned_data['stuname'] + try: + first_name = stuname.split(" ")[0] + last_name = stuname.split(" ")[1] + except: + first_name = stuname + last_name = '' + else: + stuname = '' + first_name = '' + last_name = '' + if form.cleaned_data['test_type']: + test_type = form.cleaned_data['test_type'] + else: + test_type = '' + if form.cleaned_data['uname']: + uname = form.cleaned_data['uname'] + else: + uname = '' + if form.cleaned_data['test_score']: + test_score = form.cleaned_data['test_score'] + else: + test_score = 0 + if form.cleaned_data['roll']: + rollno = form.cleaned_data['roll'] + else: + rollno = '' + if form.cleaned_data['year']: + year = form.cleaned_data['year'] + # result of the query when year is given + higherrecord = StudentRecord.objects.select_related('unique_id','record_id').filter(Q(record_id__in=PlacementRecord.objects.filter + (Q(placement_type="HIGHER STUDIES", + test_type__icontains=test_type, + name__icontains=uname, year=year, + test_score__gte=test_score)), + unique_id__in=Student.objects.filter + ((Q(id__in=ExtraInfo.objects.filter + (Q(user__in=User.objects.filter + (Q(first_name__icontains=first_name, + last_name__icontains=last_name)), + id__icontains=rollno)) + ))))) + + p2 = PlacementRecord.objects.filter( + Q(placement_type="HIGHER STUDIES", name__icontains=stuname, year__icontains=year)) + + """else: + # result of the query when year is not given + higherrecord = StudentRecord.objects.select_related('unique_id','record_id').filter( + Q(record_id__in=PlacementRecord.objects.filter + (Q(placement_type="HIGHER STUDIES", + test_type__icontains=test_type, + name__icontains=uname, + test_score__gte=test_score)), + unique_id__in=Student.objects.filter + ((Q(id__in=ExtraInfo.objects.filter + (Q(user__in=User.objects.filter + (Q(first_name__icontains=first_name, + last_name__icontains=last_name)), + id__icontains=rollno)) + ))))) + request.session['first_name'] = first_name + request.session['last_name'] = last_name + request.session['test_score'] = test_score + request.session['uname'] = uname + request.session['test_type'] = test_type + request.session['rollno'] = rollno + request.session['year'] = form.cleaned_data['year']""" + + total_query2 = p2.count() + + if total_query2 > 30: + pagination_higher = 1 + paginator = Paginator(p2, 30) + page = request.GET.get('page', 1) + p2 = paginator.page(page) + page = int(page) + total_page = int(page+3) + + if page < (paginator.num_pages-3): + if total_query2 > 30 and total_query2 <= 60: + page_range = range(1, 3) + else: + page_range = range(1, total_page+1) + + if page >= 5: + is_disabled = 1 + page_range = range(page-2, total_page) + else: + if page >= 5: + is_disabled = 1 + page_range = range(page-2, paginator.num_pages+1) + else: + page_range = range(page-2, paginator.num_pages+1) + else: + pagination_higher = 0 + else: + if request.GET.get('page') != None: + try: + if request.session['year']: + higherrecord = StudentRecord.objects.select_related('unique_id','record_id').filter( + Q(record_id__in=PlacementRecord.objects.filter( + Q(placement_type="HIGHER STUDIES", + test_type__icontains=request.session['test_type'], + name__icontains=request.session['uname'], + year=request.session['year'], + test_score__gte=request.session['test_score'])), + unique_id__in=Student.objects.filter( + (Q(id__in=ExtraInfo.objects.filter( + Q(user__in=User.objects.filter( + Q(first_name__icontains=request.session['first_name'], + last_name__icontains=request.session['last_name'])), + id__icontains=request.session['rollno'])) + ))))) + else: + higherrecord = StudentRecord.objects.select_related('unique_id','record_id').filter( + Q(record_id__in=PlacementRecord.objects.filter( + Q(placement_type="HIGHER STUDIES", + test_type__icontains=request.session['test_type'], + name__icontains=request.session['uname'], + test_score__gte=request.session['test_score'])), + unique_id__in=Student.objects.filter + ((Q(id__in=ExtraInfo.objects.filter( + Q(user__in=User.objects.filter( + Q(first_name__icontains=request.session['first_name'], + last_name__icontains=request.session['last_name'])), + id__icontains=request.session['rollno'])) + ))))) + except: + higherrecord = '' + + if higherrecord != '': + total_query = higherrecord.count() + else: + total_query = 0 + + if total_query > 30: + no_pagination = 1 + paginator = Paginator(higherrecord, 30) + page = request.GET.get('page', 1) + higherrecord = paginator.page(page) + page = int(page) + total_page = int(page + 3) + + if page<(paginator.num_pages-3): + if total_query > 30 and total_query <=60: + page_range = range(1, 3) + else: + page_range = range(1, total_page+1) + + if page >= 5: + is_disabled = 1 + page_range = range(page-2, total_page) + else: + if page >= 5: + is_disabled = 1 + page_range = range(page-2, paginator.num_pages+1) + else: + page_range = range(1, paginator.num_pages+1) + else: + no_pagination = 0 + else: + higherrecord = '' + if total_query2!=0: + higher_search_record=p2 + + context = { + 'form2' : form2, + 'form3' : form3, + 'form4' : form4, + 'current' : current, + 'current1' : current1, + 'current2' : current2, + + + 'all_records': all_records, #for flashing all placement Schedule + + 'placement_search_record': placement_search_record, + 'pbi_search_record': pbi_search_record, + 'higher_search_record': higher_search_record, + + + + 'statistics_tab' : statistics_tab, + 'pbirecord' : pbirecord, + 'placementrecord' : placementrecord, + 'higherrecord' : higherrecord, + 'years' : years, + 'records' : records, + 'delete_operation' : delete_operation, + 'page_range': page_range, + 'paginator': paginator, + 'pagination_placement': pagination_placement, + 'pagination_pbi': pagination_pbi, + 'pagination_higher': pagination_higher, + 'is_disabled': is_disabled, + 'officer_statistics_past_pbi_search': officer_statistics_past_pbi_search, + 'officer_statistics_past_higher_search': officer_statistics_past_higher_search + } + + return render(request, 'placementModule/placementstatistics.html', context) + + +@login_required +def delete_placement_statistics(request): + """ + The function is used to delete the placement statistic record. + @param: + request - trivial + @variables: + record_id = stores current StudentRecord Id. + """ + if 'deleterecord' in request.POST or 'deleterecordmanaged' in request.POST: + try: + if 'deleterecord' in request.POST: + record_id = int(request.POST['deleterecord']) + elif 'deleterecordmanaged' in request.POST: + record_id = int(request.POST['deleterecordmanaged']) + + student_record = StudentRecord.objects.get(pk=record_id) + PlacementRecord.objects.get(id=student_record.record_id.id).delete() + student_record.delete() + messages.success(request, 'Placement Statistics deleted Successfully!!') + + except Exception as e: + messages.error(request, 'Problem Occurred!! Please Try Again!!') + print(e) + + + if 'deleterecordmanaged' in request.POST: + return redirect('/placement/manage_records/') + + return redirect('/placement/statistics/') + + +def cv(request, username): + # Retrieve data or whatever you need + """ + The function is used to generate the cv in the pdf format. + Embeds the data into the predefined template. + @param: + request - trivial + username - name of user whose cv is to be generated + @variables: + user = stores current user + profile = stores extrainfo of user + current = Stores all working students from HoldsDesignation for the respective degignation + achievementcheck = variable for achievementcheck in form for cv generation + educationcheck = variable for educationcheck in form for cv generation + publicationcheck = variable for publicationcheck in form for cv generation + patentcheck = variable for patentcheck in form for cv generation + internshipcheck = variable for internshipcheck in form for cv generation + projectcheck = variable for projectcheck in form for cv generation + coursecheck = variable for coursecheck in form for cv generation + skillcheck = variable for skillcheck in form for cv generation + user = get_object_or_404(User, Q(username=username)) + profile = get_object_or_404(ExtraInfo, Q(user=user)) + import datetime + now = stores current timestamp + roll = roll of the user + student = variable storing the profile data + studentplacement = variable storing the placement data + skills = variable storing the skills data + education = variable storing the education data + course = variable storing the course data + experience = variable storing the experience data + project = variable storing the project data + achievement = variable storing the achievement data + publication = variable storing the publication data + patent = variable storing the patent data + """ + user = request.user + profile = get_object_or_404(ExtraInfo, Q(user=user)) + + current = HoldsDesignation.objects.filter(Q(working=user, designation__name="student")) + if current: + if request.method == 'POST': + achievementcheck = request.POST.get('achievementcheck') + educationcheck = request.POST.get('educationcheck') + publicationcheck = request.POST.get('publicationcheck') + patentcheck = request.POST.get('patentcheck') + internshipcheck = request.POST.get('internshipcheck') + projectcheck = request.POST.get('projectcheck') + coursecheck = request.POST.get('coursecheck') + skillcheck = request.POST.get('skillcheck') + reference_list = request.POST.getlist('reference_checkbox_list') + extracurricularcheck = request.POST.get('extracurricularcheck') + conferencecheck = request.POST.get('conferencecheck') + else: + conferencecheck = '1' + achievementcheck = '1' + educationcheck = '1' + publicationcheck = '1' + patentcheck = '1' + internshipcheck = '1' + projectcheck = '1' + coursecheck = '1' + skillcheck = '1' + extracurricularcheck = '1' + + + # print(achievementcheck,' ',educationcheck,' ',publicationcheck,' ',patentcheck,' ',internshipcheck,' ',projectcheck,' \n\n\n') + user = get_object_or_404(User, Q(username=username)) + profile = get_object_or_404(ExtraInfo, Q(user=user)) + now = datetime.datetime.now() + if int(str(profile.id)[:2]) == 20: + if (now.month>4): + roll = 1+now.year-int(str(profile.id)[:4]) + else: + roll = now.year-int(str(profile.id)[:4]) + else: + if (now.month>4): + roll = 1+(now.year)-int("20"+str(profile.id)[0:2]) + else: + roll = (now.year)-int("20"+str(profile.id)[0:2]) + + student = get_object_or_404(Student, Q(id=profile.id)) + skills = Has.objects.select_related('skill_id','unique_id').filter(Q(unique_id=student)) + education = Education.objects.select_related('unique_id').filter(Q(unique_id=student)) + reference = Reference.objects.filter(id__in=reference_list) + course = Course.objects.select_related('unique_id').filter(Q(unique_id=student)) + experience = Experience.objects.select_related('unique_id').filter(Q(unique_id=student)) + project = Project.objects.select_related('unique_id').filter(Q(unique_id=student)) + achievement = Achievement.objects.select_related('unique_id').filter(Q(unique_id=student)) + extracurricular = Extracurricular.objects.select_related('unique_id').filter(Q(unique_id=student)) + conference = Conference.objects.select_related('unique_id').filter(Q(unique_id=student)) + publication = Publication.objects.select_related('unique_id').filter(Q(unique_id=student)) + patent = Patent.objects.select_related('unique_id').filter(Q(unique_id=student)) + today = datetime.date.today() + + if len(reference) == 0: + referencecheck = '0' + else: + referencecheck = '1' + + return render_to_pdf('placementModule/cv.html', {'pagesize': 'A4', 'user': user, 'references': reference, + 'profile': profile, 'projects': project, + 'skills': skills, 'educations': education, + 'courses': course, 'experiences': experience, + 'referencecheck': referencecheck, + 'achievements': achievement, + 'extracurriculars': extracurricular, + 'publications': publication, + 'patents': patent, 'roll': roll, + 'achievementcheck': achievementcheck, + 'extracurricularcheck': extracurricularcheck, + 'educationcheck': educationcheck, + 'publicationcheck': publicationcheck, + 'patentcheck': patentcheck, + 'conferencecheck': conferencecheck, + 'conferences': conference, + 'internshipcheck': internshipcheck, + 'projectcheck': projectcheck, + 'coursecheck': coursecheck, + 'skillcheck': skillcheck, + 'today':today}) + + +def render_to_pdf(template_src, context_dict): + """ + The function is used to generate the cv in the pdf format. + Embeds the data into the predefined template. + @param: + template_src - template of cv to be rendered + context_dict - data fetched from the dtatabase to be filled in the cv template + @variables: + template - stores the template + html - html rendered pdf + result - variable to store data in BytesIO + pdf - storing encoded html of pdf version + """ + template = get_template(template_src) + html = template.render(context_dict) + result = BytesIO() + pdf = pisa.pisaDocument( + BytesIO(html.encode("UTF-8")), + result, + link_callback=_pdf_link_callback, + ) + if not pdf.err: + return HttpResponse(result.getvalue(), content_type='application/pdf') + return HttpResponse('We had some errors
%s
' % escape(html)) + + +def _pdf_link_callback(uri, rel): + if uri.startswith(settings.MEDIA_URL): + path = os.path.join(settings.MEDIA_ROOT, uri.replace(settings.MEDIA_URL, '', 1)) + elif uri.startswith(settings.STATIC_URL) and getattr(settings, 'STATIC_ROOT', None): + path = os.path.join(settings.STATIC_ROOT, uri.replace(settings.STATIC_URL, '', 1)) + else: + path = uri + + if not os.path.isfile(path): + return uri + return path + + +def export_to_xls_std_records(qs): + """ + The function is used to generate the file in the xls format. + Embeds the data into the file. + """ + response = HttpResponse(content_type='application/ms-excel') + response['Content-Disposition'] = 'attachment; filename="report.xls"' + + wb = xlwt.Workbook(encoding='utf-8') + ws = wb.add_sheet('Report') + + # Sheet header, first row + row_num = 0 + + font_style = xlwt.XFStyle() + font_style.font.bold = True + + columns = ['Roll No.', 'Name', 'CPI', 'Department', 'Discipline', 'Placed', 'Debarred' ] + + for col_num in range(len(columns)): + ws.write(row_num, col_num, columns[col_num], font_style) + + # Sheet body, remaining rows + font_style = xlwt.XFStyle() + + for student in qs: + row_num += 1 + + row = [] + row.append(student.id.id) + row.append(student.id.user.first_name+' '+student.id.user.last_name) + row.append(student.cpi) + row.append(student.programme) + row.append(student.id.department.name) + if student.studentplacement.placed_type == "PLACED": + row.append('Yes') + else: + row.append('No') + if student.studentplacement.placed_type == "DEBAR": + row.append('Yes') + else: + row.append('No') + + for col_num in range(len(row)): + ws.write(row_num, col_num, row[col_num], font_style) + + wb.save(response) + return response + + +def export_to_xls_invitation_status(qs): + response = HttpResponse(content_type='application/ms-excel') + response['Content-Disposition'] = 'attachment; filename="report.xls"' + + wb = xlwt.Workbook(encoding='utf-8') + ws = wb.add_sheet('Report') + + # Sheet header, first row + row_num = 0 + + font_style = xlwt.XFStyle() + font_style.font.bold = True + + columns = ['Roll No.', 'Name', 'Company', 'CTC', 'Invitation Status'] + + for col_num in range(len(columns)): + ws.write(row_num, col_num, columns[col_num], font_style) + + # Sheet body, remaining rows + font_style = xlwt.XFStyle() + + for student in qs: + row_num += 1 + + row = [] + row.append(student.unique_id.id.id) + row.append(student.unique_id.id.user.first_name+' '+student.unique_id.id.user.last_name) + row.append(student.notify_id.company_name) + row.append(student.notify_id.ctc) + row.append(student.invitation) + + for col_num in range(len(row)): + ws.write(row_num, col_num, row[col_num], font_style) + + wb.save(response) + return response + + +def check_invitation_date(placementstatus): + """ + The function is used to run before render of student placement view for ensuring that + last date for RESPONSE is not passed + @param: + placementstatus - queryset containing placement status of particular student + @variables: + ps - individual PlacementStatus object + """ + try: + for ps in placementstatus: + if ps.invitation=='PENDING': + dt = ps.timestamp+datetime.timedelta(days=ps.no_of_days) + if dt 5 * 1024 * 1024: + raise ValidationError('Document size must be 5MB or less.') + + +def _profile_validation_errors(student): + return _flatten_profile_errors(_profile_completion_errors(student)) + + +def _serialize_profile_eligibility_summary(student): + schedules = PlacementSchedule.objects.select_related( + 'notify_id', + 'role', + ).filter( + placement_date__gte=_today(), + ).order_by('placement_date', 'id') + summary = [] + for schedule in schedules: + eligibility = _schedule_eligibility(schedule, student) + summary.append({ + 'schedule_id': schedule.id, + 'company_name': schedule.notify_id.company_name, + 'role': schedule.get_role or '', + 'placement_date': schedule.placement_date.isoformat() if schedule.placement_date else None, + 'eligible': eligibility['eligible'], + 'reasons': eligibility['reasons'], + }) + return { + 'eligible_count': sum(1 for item in summary if item['eligible']), + 'ineligible_count': sum(1 for item in summary if not item['eligible']), + 'jobs': summary[:10], + } + + +def _is_report_admin(user): + return bool( + _is_tpo_user(user) or selectors.get_designation_queryset(user, "placement chairman") + ) + + +def _add_working_days(start, days): + current = start + remaining = days + while remaining > 0: + current += datetime.timedelta(days=1) + if current.weekday() < 5: + remaining -= 1 + return current + + +def _serialize_appeal(appeal): + due_by = _add_working_days(appeal.created_at, 5) if appeal.created_at else None + return { + 'id': appeal.id, + 'student': { + 'roll_no': appeal.student.id.id, + 'name': appeal.student.id.user.get_full_name().strip() or appeal.student.id.user.username, + 'email': appeal.student.id.user.email, + }, + 'placement_status': appeal.placement_status.id, + 'company_name': appeal.placement_status.notify_id.company_name, + 'reason': appeal.reason, + 'status': appeal.status, + 'response': appeal.response, + 'created_at': appeal.created_at.isoformat() if appeal.created_at else None, + 'reviewed_at': appeal.reviewed_at.isoformat() if appeal.reviewed_at else None, + 'due_by': due_by.isoformat() if due_by else None, + 'overdue': bool(due_by and appeal.status == 'pending' and timezone.now() > due_by), + } + + +def _get_report_records(request): + records = StudentRecord.objects.select_related( + 'record_id', + 'unique_id__id__user', + 'unique_id__id__department', + ).filter( + record_id__placement_type__in=['PLACEMENT', 'PBI'], + ) + company = request.GET.get('company') + if company: + records = records.filter(record_id__name__icontains=company) + ctc_min = request.GET.get('ctc_min') + if ctc_min not in [None, '']: + records = records.filter(record_id__ctc__gte=_parse_decimal(ctc_min)) + ctc_max = request.GET.get('ctc_max') + if ctc_max not in [None, '']: + records = records.filter(record_id__ctc__lte=_parse_decimal(ctc_max)) + year = request.GET.get('year') + if year not in [None, '']: + records = records.filter(record_id__year=year) + department = request.GET.get('department') or request.GET.get('branch') + if department: + records = records.filter(unique_id__id__department__name__iexact=department) + placement_type = request.GET.get('placement_type') + if placement_type: + records = records.filter(record_id__placement_type=placement_type) + return records + + +def _serialize_student_record(student_record): + return { + 'id': student_record.record_id.id, + 'first_name': '{} {}'.format( + student_record.unique_id.id.user.first_name, + student_record.unique_id.id.user.last_name, + ).strip() or student_record.unique_id.id.user.username, + 'roll_no': student_record.unique_id.id.id, + 'placement_name': student_record.record_id.name, + 'batch': student_record.record_id.year, + 'branch': student_record.unique_id.id.department.name if student_record.unique_id.id.department else '', + 'ctc': str(student_record.record_id.ctc), + 'placement_type': student_record.record_id.placement_type, + } + + +def _build_report_payload(request): + records = _get_report_records(request) + report_type = (request.GET.get('report_type') or 'custom').strip().lower() + if report_type == 'batch': + summary = records.values('record_id__year').annotate( + count=Count('id'), + ).order_by('-record_id__year') + columns = ['batch', 'count'] + rows = [ + {'batch': item['record_id__year'], 'count': item['count']} + for item in summary + ] + elif report_type == 'company': + summary = records.values('record_id__name').annotate( + count=Count('id'), + ).order_by('record_id__name') + columns = ['company', 'count'] + rows = [ + {'company': item['record_id__name'], 'count': item['count']} + for item in summary + ] + elif report_type == 'branch': + summary = records.values('unique_id__id__department__name').annotate( + count=Count('id'), + ).order_by('unique_id__id__department__name') + columns = ['branch', 'count'] + rows = [ + { + 'branch': item['unique_id__id__department__name'] or 'Unassigned', + 'count': item['count'], + } + for item in summary + ] + else: + columns = ['student_name', 'roll_no', 'company', 'batch', 'branch', 'ctc', 'placement_type'] + rows = [ + { + 'student_name': item['first_name'], + 'roll_no': item['roll_no'], + 'company': item['placement_name'], + 'batch': item['batch'], + 'branch': item['branch'], + 'ctc': item['ctc'], + 'placement_type': item['placement_type'], + } + for item in [_serialize_student_record(record) for record in records.order_by('-record_id__year', '-record_id__id')] + ] + return { + 'report_type': report_type, + 'columns': columns, + 'rows': rows, + 'filters': { + key: request.GET.get(key) + for key in ['company', 'ctc_min', 'ctc_max', 'year', 'department', 'branch', 'placement_type'] + if request.GET.get(key) not in [None, ''] + }, + } + + +def _build_report_pdf_response(payload): + title_map = { + 'batch': 'Batch Placement Report', + 'company': 'Company Placement Report', + 'branch': 'Branch Placement Report', + 'custom': 'Custom Placement Report', + } + header_html = ''.join( + f'{escape(str(column).replace("_", " ").title())}' + for column in payload['columns'] + ) + row_html = ''.join( + '{}'.format(''.join( + f'{escape(str(row.get(column, "")))}' + for column in payload['columns'] + )) + for row in payload['rows'] + ) or 'No records found.'.format( + len(payload['columns']) or 1, + ) + filter_html = ''.join( + f'
  • {escape(key.replace("_", " ").title())}: {escape(str(value))}
  • ' + for key, value in payload['filters'].items() + ) or '
  • No filters applied.
  • ' + html = f""" + + +

    {escape(title_map.get(payload['report_type'], 'Placement Report'))}

    +

    Generated on {escape(timezone.now().strftime('%Y-%m-%d %H:%M'))}

    +

    Filters

    +
      {filter_html}
    + + {header_html} + {row_html} +
    + + + """ + result = BytesIO() + pdf = pisa.pisaDocument(BytesIO(html.encode('utf-8')), result) + if pdf.err: + return Response({'detail': 'Could not generate PDF report.'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + response = HttpResponse(result.getvalue(), content_type='application/pdf') + response['Content-Disposition'] = 'attachment; filename="placement_report.pdf"' + return response + + +def _build_report_excel_response(payload): + response = HttpResponse(content_type='application/ms-excel') + response['Content-Disposition'] = 'attachment; filename="placement_report.xls"' + workbook = xlwt.Workbook(encoding='utf-8') + sheet = workbook.add_sheet('Report') + header_style = xlwt.XFStyle() + header_style.font.bold = True + for idx, column in enumerate(payload['columns']): + sheet.write(0, idx, str(column).replace('_', ' ').title(), header_style) + for row_index, row in enumerate(payload['rows'], start=1): + for col_index, column in enumerate(payload['columns']): + sheet.write(row_index, col_index, str(row.get(column, ''))) + workbook.save(response) + return response + + +def _serialize_report_schedule(item): + return { + 'id': item.id, + 'name': item.name, + 'report_type': item.report_type, + 'frequency': item.frequency, + 'export_format': item.export_format, + 'filters': item.filters or {}, + 'recipients': [entry.strip() for entry in (item.recipients or '').split(',') if entry.strip()], + 'is_active': item.is_active, + 'last_run_at': item.last_run_at.isoformat() if item.last_run_at else None, + 'created_at': item.created_at.isoformat() if item.created_at else None, + 'updated_at': item.updated_at.isoformat() if item.updated_at else None, + } + + +def _coerce_decimal(value): + try: + return decimal.Decimal(str(value)) + except Exception: + return None + + +def _matches_condition(actual, expected, condition): + if actual is None: + return False + actual_value = str(actual).strip() + expected_value = str(expected).strip() + if condition == 'equals': + return actual_value.lower() == expected_value.lower() + if condition == 'not_equals': + return actual_value.lower() != expected_value.lower() + actual_decimal = _coerce_decimal(actual) + expected_decimal = _coerce_decimal(expected) + if actual_decimal is None or expected_decimal is None: + return False + if condition == 'gte': + return actual_decimal >= expected_decimal + if condition == 'gt': + return actual_decimal > expected_decimal + if condition == 'lte': + return actual_decimal <= expected_decimal + if condition == 'lt': + return actual_decimal < expected_decimal + return False + + +def _student_attribute_map(student): + return { + 'cpi': student.cpi, + 'batch': student.batch, + 'passoutyr': student.batch, + 'department': student.id.department.name if student.id.department else '', + 'branch': student.id.department.name if student.id.department else '', + 'programme': student.programme, + 'gender': student.id.sex, + } + + +def _schedule_eligibility(schedule, student, *, student_placement=None, restrictions=None): + student_placement = student_placement or _ensure_studentplacement(student) + reasons = [] + if student_placement.debar == 'DEBAR': + reasons.append('Student is debarred.') + + student_attributes = _student_attribute_map(student) + direct_checks = [ + ('cpi', schedule.cpi, 'gte', 'CPI requirement not met.'), + ('passoutyr', schedule.passoutyr, 'equals', 'Passout year requirement not met.'), + ('gender', schedule.gender, 'equals', 'Gender requirement not met.'), + ] + for attribute, expected, condition, message in direct_checks: + if expected not in [None, ''] and not _matches_condition(student_attributes.get(attribute), expected, condition): + reasons.append(message) + + if schedule.branch: + allowed_branches = [item.strip().lower() for item in schedule.branch.split(',') if item.strip()] + student_branch = str(student_attributes.get('branch') or '').strip().lower() + if allowed_branches and student_branch not in allowed_branches: + reasons.append('Branch requirement not met.') + + for restriction in (restrictions if restrictions is not None else PlacementRestriction.objects.all()): + actual = student_attributes.get(restriction.criteria.lower()) + if actual is None: + continue + if not _matches_condition(actual, restriction.value, restriction.condition): + reasons.append(restriction.description or '{} restriction not met.'.format(restriction.criteria)) + + return { + 'eligible': len(reasons) == 0, + 'reasons': reasons, + } + + +def _send_placement_notifications(*, actor, recipients, description): + recipients = list(recipients) + recipient_ids = [recipient.id for recipient in recipients if getattr(recipient, 'id', None)] + student_map = { + student.id.user_id: student + for student in Student.objects.filter(id__user_id__in=recipient_ids).select_related('id__user') + } + preference_map = { + preference.student_id: preference + for preference in PlacementNotificationPreference.objects.filter( + student_id__in=[student.pk for student in student_map.values()], + ) + } + + for recipient in recipients: + preference = None + student = student_map.get(recipient.id) + if student: + preference = preference_map.get(student.pk) + if preference is None: + preference = _ensure_notification_preferences(student) + preference_map[student.pk] = preference + portal_enabled = True if preference is None else preference.enable_portal + email_enabled = False if preference is None else preference.enable_email + sms_enabled = False if preference is None else preference.enable_sms + + if portal_enabled: + notify.send( + sender=actor, + recipient=recipient, + verb=description, + url='placement:placement', + module='Placement Cell', + ) + + if email_enabled and recipient.email: + send_mail( + subject='Placement Cell Notification', + message=description, + from_email=getattr(settings, 'EMAIL_HOST_USER', None) or 'noreply@fusion.local', + recipient_list=[recipient.email], + fail_silently=True, + ) + + if sms_enabled: + logger = logging.getLogger(__name__) + logger.info('SMS notification queued for %s: %s', recipient.username, description) + + +def _application_stage_label(status_value): + stage_map = { + 'pending': 'Under Review', + 'shortlisted': 'Shortlisted', + 'interview_scheduled': 'Interview Scheduled', + 'interview_completed': 'Interview Completed', + 'offer_released': 'Offer Released', + 'accept': 'Selected', + 'reject': 'Rejected', + 'withdrawn': 'Withdrawn', + } + return stage_map.get(status_value, 'Under Review') + + +def _create_application_timeline_entry(application, *, stage=None, remarks='', actor=None): + return PlacementApplicationTimeline.objects.create( + application=application, + stage=stage or _application_stage_label(application.status), + remarks=remarks or '', + actor=actor, + ) + + +def _serialize_application_timeline(application): + entries = [{ + 'id': 'applied-{}'.format(application.id), + 'stage': 'Applied', + 'remarks': 'Application submitted', + 'actor': application.student.id.user.get_full_name().strip() or application.student.id.user.username, + 'created_at': application.created_at.isoformat() if application.created_at else None, + }] + entries.extend([{ + 'id': item.id, + 'stage': item.stage, + 'remarks': item.remarks, + 'actor': item.actor.get_full_name().strip() if item.actor else '', + 'created_at': item.created_at.isoformat() if item.created_at else None, + } for item in application.timeline_entries.select_related('actor').all()]) + return entries + + +def _serialize_application_interview(item): + return { + 'id': item.id, + 'round_no': item.round_no, + 'title': item.title or 'Round {}'.format(item.round_no), + 'scheduled_at': item.scheduled_at.isoformat() if item.scheduled_at else None, + 'end_datetime': item.end_datetime.isoformat() if item.end_datetime else None, + 'mode': item.mode, + 'location': item.location, + 'meeting_link': item.meeting_link, + 'remarks': item.remarks, + 'feedback': item.remarks, + 'outcome': item.outcome, + 'is_active': item.is_active, + } + + +def _ensure_placement_record_for_selection(application): + student_record = StudentRecord.objects.filter( + unique_id=application.student, + record_id__name=application.schedule.notify_id.company_name, + record_id__year=timezone.now().year, + record_id__placement_type=application.schedule.notify_id.placement_type, + ).select_related('record_id').first() + if student_record: + return student_record.record_id + + record = PlacementRecord.objects.create( + placement_type=application.schedule.notify_id.placement_type, + name=application.schedule.notify_id.company_name, + ctc=application.schedule.notify_id.ctc, + year=timezone.now().year, + test_type=application.schedule.get_role or '', + test_score=0, + ) + StudentRecord.objects.get_or_create(record_id=record, unique_id=application.student) + return record + + +def _serialize_tpo_application_detail(application, request=None): + student_user = application.student.id.user + offer = PlacementStatus.objects.filter( + notify_id=application.schedule.notify_id, + unique_id=application.student, + ).first() + responses = PlacementApplicationResponse.objects.select_related('field').filter(application=application) + profile = application.student.id + documents = PlacementProfileDocument.objects.filter(student=application.student).order_by('-uploaded_at', '-id') + return { + 'id': application.id, + 'schedule_id': application.schedule_id, + 'status': application.status, + 'status_label': _application_stage_label(application.status), + 'remarks': application.remarks or '', + 'updated_at': application.updated_at.isoformat() if application.updated_at else None, + 'applied_at': application.created_at.isoformat() if application.created_at else None, + 'student': { + 'name': student_user.get_full_name().strip() or student_user.username, + 'roll_no': application.student.id.id, + 'email': student_user.email, + 'phone_no': str(profile.phone_no or ''), + 'address': profile.address or '', + 'about_me': profile.about_me if profile.about_me != 'NA' else '', + 'programme': application.student.programme or '', + 'branch': application.student.id.department.name if application.student.id.department else '', + 'cpi': application.student.cpi, + 'passout_year': application.student.batch, + }, + 'company': { + 'name': application.schedule.notify_id.company_name, + 'role': application.schedule.get_role or '', + 'ctc': str(application.schedule.notify_id.ctc), + 'placement_type': application.schedule.notify_id.placement_type, + }, + 'documents': [_serialize_profile_document(item, request=request) for item in documents], + 'resume': _serialize_profile_document(documents[0], request=request) if documents else None, + 'timeline': _serialize_application_timeline(application), + 'interviews': [ + _serialize_application_interview(item) + for item in application.interview_schedules.all() + ], + 'offer': { + 'id': offer.id, + 'invitation': offer.invitation, + 'timestamp': offer.timestamp.isoformat() if offer and offer.timestamp else None, + 'deadline_days': offer.no_of_days if offer else None, + } if offer else None, + 'responses': [ + { + 'field': item.field.name if item.field else 'Response', + 'value': item.value, + } + for item in responses + ], + } + + +def _serialize_schedule(schedule, user, *, has_applied=False, eligibility=None): + eligibility = eligibility or {'eligible': True, 'reasons': []} + company = schedule.company + application_fields = [{ + 'field_id': field.id, + 'name': field.name, + 'type': field.type, + 'required': field.required, + } for field in schedule.fields.all().order_by('name')] + + return { + 'id': str(schedule.id), + 'jobID': str(schedule.id), + 'company_name': schedule.notify_id.company_name, + 'location': schedule.location, + 'role_st': schedule.get_role or '', + 'placement_type': schedule.notify_id.placement_type, + 'schedule_at': schedule.schedule_at.isoformat() if schedule.schedule_at else '', + 'placement_date': schedule.placement_date.isoformat() if schedule.placement_date else '', + 'description': schedule.description or '', + 'ctc': str(schedule.notify_id.ctc), + 'check': has_applied, + 'time': schedule.time.isoformat() if schedule.time else '', + 'end_datetime': schedule.end_datetime.isoformat() if schedule.end_datetime else '', + 'attached_file_url': schedule.attached_file.url if schedule.attached_file else None, + 'eligible': eligibility['eligible'], + 'eligibility_reasons': eligibility['reasons'], + 'eligibility_criteria': [item.strip() for item in (schedule.eligibility or '').split(',') if item.strip()], + 'passout_year': schedule.passoutyr or '', + 'gender_requirement': schedule.gender or '', + 'cpi_requirement': schedule.cpi or '', + 'branch_requirement': schedule.branch or '', + 'company_details': { + 'description': company.description if company else '', + 'address': company.address if company else '', + 'website': company.website if company else '', + 'logo_url': company.logo.url if company and company.logo else None, + }, + 'application_fields': application_fields, + } + + +@api_view(['GET', 'POST']) +@permission_classes([IsAuthenticated]) +@authentication_classes([TokenAuthentication]) +def placement_api(request): + if request.method == 'GET': + selected_role = cache.get('last_selected_role_{}'.format(request.user.id)) + has_student_designation = selectors.get_designation_queryset( + request.user, "student" + ).exists() + has_tpo_designation = _is_tpo_user(request.user) + is_student_view = selected_role == "student" and has_student_designation + is_tpo_user = has_tpo_designation and not is_student_view + schedules = PlacementSchedule.objects.select_related( + 'notify_id', + 'role', + 'company', + ).prefetch_related( + 'fields', + ).order_by('-placement_date', '-id') + + # Advanced search/filter + company = request.GET.get('company') + role = request.GET.get('role') + location = request.GET.get('location') + min_package = request.GET.get('min_package') + max_package = request.GET.get('max_package') + + if company: + schedules = schedules.filter(notify_id__company_name__icontains=company) + if role: + schedules = schedules.filter(role__role__icontains=role) + if location: + schedules = schedules.filter(location__icontains=location) + if min_package: + schedules = schedules.filter(notify_id__ctc__gte=min_package) + if max_package: + schedules = schedules.filter(notify_id__ctc__lte=max_package) + + if has_student_designation and not is_tpo_user: + student = selectors.get_student_for_user(request.user) + future_aspect = _ensure_studentplacement(student).future_aspect + # Students should be able to see both placement and internship drives + # on the placement schedule page. Higher studies remains separate. + # The frontend exposes All / Active / Upcoming tabs, so students + # need the full schedule list and the client can segment by date. + if future_aspect == "HIGHER STUDIES": + schedules = schedules.filter(notify_id__placement_type=future_aspect) + else: + schedules = schedules.filter( + notify_id__placement_type__in=["PLACEMENT", "PBI"] + ) + schedules = list(schedules) + data = [] + has_applied_schedule_ids = set() + eligibility_by_schedule_id = {} + + if has_student_designation: + try: + student = selectors.get_student_for_user(request.user) + student_placement = _ensure_studentplacement(student) + restrictions = list(PlacementRestriction.objects.all()) + has_applied_schedule_ids = set( + PlacementApplication.objects.filter( + student=student, + schedule_id__in=[schedule.id for schedule in schedules], + ).exclude(status='withdrawn').values_list('schedule_id', flat=True), + ) + for schedule in schedules: + eligibility_by_schedule_id[schedule.id] = _schedule_eligibility( + schedule, + student, + student_placement=student_placement, + restrictions=restrictions, + ) + except Exception: + has_applied_schedule_ids = set() + eligibility_by_schedule_id = {} + + for schedule in schedules: + try: + data.append( + _serialize_schedule( + schedule, + request.user, + has_applied=schedule.id in has_applied_schedule_ids, + eligibility=eligibility_by_schedule_id.get( + schedule.id, + {'eligible': True, 'reasons': []}, + ), + ), + ) + except Exception: + continue + return Response(data, status=status.HTTP_200_OK) + + if not _is_tpo_user(request.user): + return Response( + {'detail': 'Only TPO users can create placement schedules.'}, + status=status.HTTP_403_FORBIDDEN, + ) + + company = None + company_id = request.data.get('company_id') + if company_id: + company = CompanyDetails.objects.filter(id=company_id).first() + company_name = request.data.get('company_name') or request.data.get('title') + if company is None and company_name: + company, _ = CompanyDetails.objects.get_or_create(company_name=company_name) + + role_name = request.data.get('role') or '' + role = selectors.get_or_create_role(role_name) if role_name else None + placement_date = _parse_date(request.data.get('placement_date')) or timezone.now().date() + if placement_date < _today(): + return Response( + {'placement_date': ['Placement date cannot be in the past.']}, + status=status.HTTP_400_BAD_REQUEST, + ) + schedule_time = _parse_time(request.data.get('schedule_at')) or timezone.now().time() + notify = NotifyStudent.objects.create( + placement_type=_normalize_placement_type(request.data.get('placement_type')), + company_name=company_name or '', + ctc=_parse_decimal(request.data.get('ctc')), + description=request.data.get('description') or '', + ) + schedule = PlacementSchedule.objects.create( + notify_id=notify, + title=request.data.get('title') or notify.company_name, + placement_date=placement_date, + end_date=_parse_date(request.data.get('end_date')), + location=request.data.get('location') or '', + description=request.data.get('description') or '', + eligibility=request.data.get('eligibility') or '', + passoutyr=request.data.get('passoutyr') or '', + gender=request.data.get('gender') or '', + cpi=str(request.data.get('cpi') or ''), + branch=request.data.get('branch') or '', + time=schedule_time, + role=role, + attached_file=request.FILES.get('resume') or request.FILES.get('attached_file'), + schedule_at=_parse_datetime(request.data.get('schedule_at')) or timezone.now(), + end_datetime=_parse_datetime(request.data.get('end_datetime')), + company=company, + ) + field_ids = _get_field_ids_from_request(request) + if field_ids: + schedule.fields.set(PlacementField.objects.filter(id__in=field_ids)) + return Response(_serialize_schedule(schedule, request.user), status=status.HTTP_201_CREATED) + + +@api_view(['GET', 'PUT', 'DELETE']) +@permission_classes([IsAuthenticated]) +@authentication_classes([TokenAuthentication]) +def placement_detail_api(request, schedule_id): + schedule = get_object_or_404( + PlacementSchedule.objects.select_related('notify_id', 'role', 'company').prefetch_related('fields'), + pk=schedule_id, + ) + + if request.method == 'GET': + return Response(_serialize_schedule(schedule, request.user), status=status.HTTP_200_OK) + + if not _is_tpo_user(request.user): + return Response( + {'detail': 'Only TPO users can modify placement schedules.'}, + status=status.HTTP_403_FORBIDDEN, + ) + + if request.method == 'DELETE': + schedule.notify_id.delete() + return Response({'message': 'Placement schedule deleted successfully.'}, status=status.HTTP_200_OK) + + notify = schedule.notify_id + notify.placement_type = _normalize_placement_type(request.data.get('placement_type') or notify.placement_type) + notify.company_name = request.data.get('company_name') or notify.company_name + notify.ctc = _parse_decimal(request.data.get('ctc'), notify.ctc) + notify.description = request.data.get('description') or notify.description + notify.save() + + placement_date = _parse_date(request.data.get('placement_date')) + if placement_date and placement_date < _today(): + return Response( + {'placement_date': ['Placement date cannot be in the past.']}, + status=status.HTTP_400_BAD_REQUEST, + ) + schedule.placement_date = placement_date or schedule.placement_date + schedule.location = request.data.get('location') or schedule.location + schedule.description = request.data.get('description') or schedule.description + schedule.schedule_at = _parse_datetime(request.data.get('schedule_at')) or schedule.schedule_at + schedule.end_datetime = _parse_datetime(request.data.get('end_date_time')) or schedule.end_datetime + role_name = request.data.get('role') + if role_name: + schedule.role = selectors.get_or_create_role(role_name) + time_value = _parse_time(request.data.get('schedule_at')) + if time_value: + schedule.time = time_value + schedule.save() + return Response({'message': 'Placement schedule updated successfully.'}, status=status.HTTP_200_OK) + + +@api_view(['GET', 'POST']) +@permission_classes([IsAuthenticated]) +@authentication_classes([TokenAuthentication]) +def placement_statistics_api(request): + if request.method != 'GET' and not _is_tpo_user(request.user): + return Response( + {'detail': 'Only TPO users can access placement statistics.'}, + status=status.HTTP_403_FORBIDDEN, + ) + + if request.method == 'GET': + records = StudentRecord.objects.select_related( + 'record_id', + 'unique_id__id__user', + 'unique_id__id__department', + ) + company = request.GET.get('company') + if company: + records = records.filter(record_id__name__icontains=company) + ctc_min = request.GET.get('ctc_min') + if ctc_min not in [None, '']: + records = records.filter(record_id__ctc__gte=_parse_decimal(ctc_min)) + ctc_max = request.GET.get('ctc_max') + if ctc_max not in [None, '']: + records = records.filter(record_id__ctc__lte=_parse_decimal(ctc_max)) + year = request.GET.get('year') + if year not in [None, '']: + records = records.filter(record_id__year=year) + department = request.GET.get('department') + if department: + records = records.filter(unique_id__id__department__name__iexact=department) + + if request.GET.get('aggregate_by') == 'department': + summary = records.values( + 'unique_id__id__department__name', + ).annotate( + count=Count('id'), + ).order_by('unique_id__id__department__name') + return Response([ + { + 'department': item['unique_id__id__department__name'] or 'Unassigned', + 'count': item['count'], + } + for item in summary + ], status=status.HTTP_200_OK) + + rows = [] + for item in records.order_by('-record_id__year', '-record_id__id'): + rows.append({ + 'id': item.record_id.id, + 'first_name': '{} {}'.format( + item.unique_id.id.user.first_name, + item.unique_id.id.user.last_name, + ).strip() or item.unique_id.id.user.username, + 'placement_name': item.record_id.name, + 'batch': item.record_id.year, + 'branch': item.unique_id.id.department.name if item.unique_id.id.department else '', + 'ctc': str(item.record_id.ctc), + }) + return Response(rows, status=status.HTTP_200_OK) + + roll_no = request.data.get('roll_no') + student = get_object_or_404(Student.objects.select_related('id__user', 'id__department'), pk=roll_no) + record = PlacementRecord.objects.create( + placement_type=_normalize_placement_type(request.data.get('placement_type')), + name=request.data.get('company_name') or '', + ctc=_parse_decimal(request.data.get('ctc')), + year=int(request.data.get('year') or timezone.now().year), + test_type=request.data.get('test_type') or '', + test_score=int(request.data.get('test_score') or 0), + ) + StudentRecord.objects.get_or_create(record_id=record, unique_id=student) + return Response({'id': record.id}, status=status.HTTP_201_CREATED) + + +@api_view(['GET']) +@permission_classes([IsAuthenticated]) +@authentication_classes([TokenAuthentication]) +def placement_reports_api(request): + if not _is_report_admin(request.user): + return Response({'detail': 'Only TPO and chairman users can access reports.'}, status=status.HTTP_403_FORBIDDEN) + payload = _build_report_payload(request) + payload['templates'] = [ + {'value': 'batch', 'label': 'Batch Summary'}, + {'value': 'company', 'label': 'Company Summary'}, + {'value': 'branch', 'label': 'Branch Summary'}, + {'value': 'custom', 'label': 'Custom Report'}, + ] + return Response(payload, status=status.HTTP_200_OK) + + +@api_view(['GET']) +@permission_classes([IsAuthenticated]) +@authentication_classes([TokenAuthentication]) +def placement_reports_export_api(request): + if not _is_report_admin(request.user): + return Response({'detail': 'Only TPO and chairman users can export reports.'}, status=status.HTTP_403_FORBIDDEN) + payload = _build_report_payload(request) + export_format = ( + request.GET.get('export_format') + or request.GET.get('download_format') + or 'excel' + ).lower() + if export_format == 'pdf': + return _build_report_pdf_response(payload) + return _build_report_excel_response(payload) + + +@api_view(['GET', 'POST']) +@permission_classes([IsAuthenticated]) +@authentication_classes([TokenAuthentication]) +def placement_report_schedules_api(request): + if not _is_report_admin(request.user): + return Response({'detail': 'Only TPO and chairman users can manage report schedules.'}, status=status.HTTP_403_FORBIDDEN) + if request.method == 'GET': + schedules = PlacementReportSchedule.objects.all() + return Response([_serialize_report_schedule(item) for item in schedules], status=status.HTTP_200_OK) + + recipients = request.data.get('recipients') or [] + if isinstance(recipients, list): + recipients = ', '.join([str(item).strip() for item in recipients if str(item).strip()]) + schedule = PlacementReportSchedule.objects.create( + name=request.data.get('name') or 'Placement Report', + report_type=request.data.get('report_type') or 'custom', + frequency=request.data.get('frequency') or 'weekly', + export_format=request.data.get('export_format') or 'excel', + filters=request.data.get('filters') or {}, + recipients=recipients, + is_active=bool(request.data.get('is_active', True)), + created_by=request.user, + ) + return Response(_serialize_report_schedule(schedule), status=status.HTTP_201_CREATED) + + +@api_view(['PUT', 'DELETE']) +@permission_classes([IsAuthenticated]) +@authentication_classes([TokenAuthentication]) +def placement_report_schedule_detail_api(request, schedule_id): + if not _is_report_admin(request.user): + return Response({'detail': 'Only TPO and chairman users can manage report schedules.'}, status=status.HTTP_403_FORBIDDEN) + schedule = get_object_or_404(PlacementReportSchedule, pk=schedule_id) + if request.method == 'DELETE': + schedule.delete() + return Response({'message': 'Report schedule deleted successfully.'}, status=status.HTTP_200_OK) + + recipients = request.data.get('recipients', schedule.recipients) + if isinstance(recipients, list): + recipients = ', '.join([str(item).strip() for item in recipients if str(item).strip()]) + schedule.name = request.data.get('name', schedule.name) + schedule.report_type = request.data.get('report_type', schedule.report_type) + schedule.frequency = request.data.get('frequency', schedule.frequency) + schedule.export_format = request.data.get('export_format', schedule.export_format) + schedule.filters = request.data.get('filters', schedule.filters) + schedule.recipients = recipients + if 'is_active' in request.data: + schedule.is_active = bool(request.data.get('is_active')) + schedule.save() + return Response(_serialize_report_schedule(schedule), status=status.HTTP_200_OK) + + +@api_view(['DELETE']) +@permission_classes([IsAuthenticated]) +@authentication_classes([TokenAuthentication]) +def delete_placement_statistics_api(request, record_id): + if not _is_tpo_user(request.user): + return Response( + {'detail': 'Only TPO users can delete placement statistics.'}, + status=status.HTTP_403_FORBIDDEN, + ) + PlacementRecord.objects.filter(pk=record_id).delete() + return Response({'message': 'Record deleted successfully.'}, status=status.HTTP_200_OK) + + +def _serialize_higher_studies_record(student_record): + student = student_record.unique_id + user = student.id.user + record = student_record.record_id + return { + 'id': record.id, + 'student_record_id': student_record.id, + 'roll_no': student.id.id, + 'student_name': '{} {}'.format( + user.first_name, + user.last_name, + ).strip() or user.username, + 'university': record.name, + 'test_type': record.test_type, + 'test_score': record.test_score, + 'year': record.year, + 'department': student.id.department.name if student.id.department else '', + } + + +@api_view(['GET', 'POST']) +@permission_classes([IsAuthenticated]) +@authentication_classes([TokenAuthentication]) +def higher_studies_api(request): + if not _is_tpo_user(request.user): + return Response( + {'detail': 'Only TPO users can access higher studies records.'}, + status=status.HTTP_403_FORBIDDEN, + ) + + if request.method == 'GET': + records = StudentRecord.objects.select_related( + 'record_id', + 'unique_id__id__user', + 'unique_id__id__department', + ).filter(record_id__placement_type='HIGHER STUDIES') + + roll_no = request.GET.get('roll_no') + if roll_no: + records = records.filter(unique_id__id__id__iexact=roll_no) + university = request.GET.get('university') + if university: + records = records.filter(record_id__name__icontains=university) + test_type = request.GET.get('test_type') + if test_type: + records = records.filter(record_id__test_type__icontains=test_type) + year = request.GET.get('year') + if year not in [None, '']: + records = records.filter(record_id__year=year) + + data = [ + _serialize_higher_studies_record(item) + for item in records.order_by('-record_id__year', '-record_id__id') + ] + return Response(data, status=status.HTTP_200_OK) + + roll_no = request.data.get('roll_no') or request.data.get('roll') + student = get_object_or_404(Student.objects.select_related('id__user', 'id__department'), pk=roll_no) + record = PlacementRecord.objects.create( + placement_type='HIGHER STUDIES', + name=request.data.get('university') or request.data.get('company_name') or request.data.get('name') or '', + ctc=0, + year=int(request.data.get('year') or timezone.now().year), + test_type=request.data.get('test_type') or '', + test_score=int(request.data.get('test_score') or 0), + ) + student_record, _ = StudentRecord.objects.get_or_create(record_id=record, unique_id=student) + return Response(_serialize_higher_studies_record(student_record), status=status.HTTP_201_CREATED) + + +@api_view(['PUT', 'DELETE']) +@permission_classes([IsAuthenticated]) +@authentication_classes([TokenAuthentication]) +def higher_studies_detail_api(request, record_id): + if not _is_tpo_user(request.user): + return Response( + {'detail': 'Only TPO users can modify higher studies records.'}, + status=status.HTTP_403_FORBIDDEN, + ) + record = get_object_or_404(PlacementRecord, pk=record_id, placement_type='HIGHER STUDIES') + + if request.method == 'DELETE': + record.delete() + return Response({'message': 'Higher studies record deleted successfully.'}, status=status.HTTP_200_OK) + + record.name = request.data.get('university') or request.data.get('company_name') or record.name + record.test_type = request.data.get('test_type') or record.test_type + if request.data.get('test_score') not in [None, '']: + record.test_score = int(request.data.get('test_score')) + if request.data.get('year') not in [None, '']: + record.year = int(request.data.get('year')) + record.save() + student_record = get_object_or_404( + StudentRecord.objects.select_related('record_id', 'unique_id__id__user', 'unique_id__id__department'), + record_id=record, + ) + return Response(_serialize_higher_studies_record(student_record), status=status.HTTP_200_OK) + + +@api_view(['GET', 'POST']) +@permission_classes([IsAuthenticated]) +@authentication_classes([TokenAuthentication]) +def registration_api(request): + if request.method == 'POST' and not _is_tpo_user(request.user): + return Response( + {'detail': 'Only TPO users can create company registrations.'}, + status=status.HTTP_403_FORBIDDEN, + ) + + if request.method == 'GET': + companies = CompanyDetails.objects.all().order_by('company_name') + data = [{ + 'id': company.id, + 'companyName': company.company_name, + 'description': company.description, + 'address': company.address, + 'website': company.website, + 'logo': company.logo.url if company.logo else None, + } for company in companies] + return Response(data, status=status.HTTP_200_OK) + + company = CompanyDetails.objects.create( + company_name=request.data.get('companyName') or request.data.get('company_name') or '', + description=request.data.get('description') or '', + address=request.data.get('address') or '', + website=request.data.get('website') or '', + logo=request.FILES.get('logo'), + ) + return Response({ + 'id': company.id, + 'companyName': company.company_name, + 'description': company.description, + 'address': company.address, + 'website': company.website, + 'logo': company.logo.url if company.logo else None, + }, status=status.HTTP_200_OK) + + +@api_view(['GET', 'POST']) +@permission_classes([IsAuthenticated]) +@authentication_classes([TokenAuthentication]) +def placement_fields_api(request): + if not _is_tpo_user(request.user): + return Response( + {'detail': 'Only TPO users can manage placement fields.'}, + status=status.HTTP_403_FORBIDDEN, + ) + + if request.method == 'GET': + fields = PlacementField.objects.all().order_by('name') + data = [{ + 'id': field.id, + 'name': field.name, + 'type': field.type, + 'required': field.required, + } for field in fields] + return Response(data, status=status.HTTP_200_OK) + + field = PlacementField.objects.create( + name=request.data.get('name') or '', + type=request.data.get('type') or 'text', + required=bool(request.data.get('required')), + ) + return Response({ + 'id': field.id, + 'name': field.name, + 'type': field.type, + 'required': field.required, + }, status=status.HTTP_200_OK) + + +@api_view(['GET']) +@permission_classes([IsAuthenticated]) +@authentication_classes([TokenAuthentication]) +def form_fields_api(request): + job_id = request.GET.get('jobId') + schedule = PlacementSchedule.objects.filter(pk=job_id).first() if job_id else None + queryset = schedule.fields.all() if schedule and schedule.fields.exists() else PlacementField.objects.all() + data = [{ + 'field_id': field.id, + 'id': field.id, + 'name': field.name, + 'type': field.type, + 'required': field.required, + } for field in queryset.order_by('name')] + return Response(data, status=status.HTTP_200_OK) + + +@api_view(['GET', 'POST', 'PUT']) +@permission_classes([IsAuthenticated]) +@authentication_classes([TokenAuthentication]) +def placement_profile_api(request): + student = selectors.get_student_for_user(request.user) + + if request.method == 'POST': + file_obj = request.FILES.get('document') + if not file_obj: + return Response( + {'document': ['Please choose a document to upload.']}, + status=status.HTTP_400_BAD_REQUEST, + ) + try: + _validate_profile_document(file_obj) + except ValidationError as exc: + return Response({'document': exc.messages}, status=status.HTTP_400_BAD_REQUEST) + document = PlacementProfileDocument.objects.create( + student=student, + name=request.data.get('name') or file_obj.name, + document=file_obj, + ) + _log_profile_action( + student, + request.user, + 'document_uploaded', + {'name': document.name}, + ) + return Response(_serialize_profile_document(document, request=request), status=status.HTTP_201_CREATED) + + if request.method == 'PUT': + current_data = _serialize_profile(student) + incoming_data = _sanitize_profile_payload(request.data) + updated_data = dict(current_data) + updated_data.update(incoming_data) + field_errors = _profile_form_errors(updated_data) + document_file = request.FILES.get('document') + if document_file: + try: + _validate_profile_document(document_file) + except ValidationError as exc: + field_errors['document'] = exc.messages + if field_errors: + return Response( + { + 'detail': 'Placement profile could not be saved.', + 'field_errors': field_errors, + 'errors': _flatten_profile_errors(field_errors), + }, + status=status.HTTP_400_BAD_REQUEST, + ) + + profile = student.id + changed_fields = {} + for key in ['first_name', 'last_name', 'email']: + previous_value = getattr(profile.user, key) or '' + new_value = updated_data[key] + if previous_value != new_value: + changed_fields[key] = {'from': previous_value, 'to': new_value} + setattr(profile.user, key, new_value) + profile.user.save() + + profile_mapping = { + 'phone_no': int(updated_data['phone_no']), + 'address': updated_data['address'], + 'about_me': updated_data['about_me'], + } + for key, new_value in profile_mapping.items(): + previous_value = getattr(profile, key) + previous_text = '' if previous_value is None else str(previous_value) + current_text = str(new_value) + if previous_text != current_text: + changed_fields[key] = {'from': previous_text, 'to': current_text} + setattr(profile, key, new_value) + profile.save() + + if changed_fields: + _log_profile_action( + student, + request.user, + 'profile_updated', + {'changed_fields': changed_fields}, + ) + + if document_file: + document = PlacementProfileDocument.objects.create( + student=student, + name=request.data.get('name') or document_file.name, + document=document_file, + ) + _log_profile_action( + student, + request.user, + 'document_uploaded', + {'name': document.name}, + ) + + if changed_fields or document_file: + _send_placement_notifications( + actor=request.user, + recipients=[request.user], + description='Your placement profile has been updated.', + ) + + preferences = _ensure_notification_preferences(student) + documents = PlacementProfileDocument.objects.filter(student=student) + logs = PlacementProfileAuditLog.objects.filter(student=student)[:25] + field_errors = _profile_completion_errors(student) + validation_errors = _flatten_profile_errors(field_errors) + return Response({ + 'is_complete': len(validation_errors) == 0, + 'profile': _serialize_profile(student), + 'eligibility_summary': _serialize_profile_eligibility_summary(student), + 'field_errors': field_errors, + 'validation_errors': validation_errors, + 'documents': [_serialize_profile_document(item, request=request) for item in documents], + 'audit_logs': [_serialize_audit_log(item) for item in logs], + 'preferences': { + 'portal': preferences.enable_portal, + 'email': preferences.enable_email, + 'sms': preferences.enable_sms, + }, + }, status=status.HTTP_200_OK) + + +@api_view(['GET', 'PUT']) +@permission_classes([IsAuthenticated]) +@authentication_classes([TokenAuthentication]) +def notification_preferences_api(request): + student = selectors.get_student_for_user(request.user) + preferences = _ensure_notification_preferences(student) + + if request.method == 'PUT': + preferences.enable_portal = bool(request.data.get('portal', preferences.enable_portal)) + preferences.enable_email = bool(request.data.get('email', preferences.enable_email)) + preferences.enable_sms = bool(request.data.get('sms', preferences.enable_sms)) + preferences.save() + _log_profile_action( + student, + request.user, + 'notification_preferences_updated', + { + 'portal': preferences.enable_portal, + 'email': preferences.enable_email, + 'sms': preferences.enable_sms, + }, + ) + + return Response({ + 'portal': preferences.enable_portal, + 'email': preferences.enable_email, + 'sms': preferences.enable_sms, + }, status=status.HTTP_200_OK) + + +@api_view(['POST']) +@permission_classes([IsAuthenticated]) +@authentication_classes([TokenAuthentication]) +def apply_for_placement_api(request): + student = selectors.get_student_for_user(request.user) + schedule = get_object_or_404(PlacementSchedule, pk=request.data.get('jobId')) + requested_action = ( + request.data.get('invitation') or + request.data.get('status') or + 'ACCEPTED' + ) + requested_action = str(requested_action).upper() + is_decline = requested_action in ['REJECT', 'REJECTED', 'DECLINE', 'DECLINED'] + student_placement = _ensure_studentplacement(student) + if student_placement.debar == 'DEBAR': + return Response( + {'detail': 'Debarred students are not eligible to apply for placement activities.'}, + status=status.HTTP_403_FORBIDDEN, + ) + profile_errors = _profile_validation_errors(student) + if profile_errors: + return Response( + {'detail': 'Placement profile is incomplete.', 'errors': profile_errors}, + status=status.HTTP_400_BAD_REQUEST, + ) + + eligibility = _schedule_eligibility(schedule, student) + if not eligibility['eligible']: + return Response( + {'detail': 'You are not eligible for this job posting.', 'errors': eligibility['reasons']}, + status=status.HTTP_403_FORBIDDEN, + ) + + if is_decline: + placement_status, _ = PlacementStatus.objects.get_or_create( + notify_id=schedule.notify_id, + unique_id=student, + ) + placement_status.invitation = 'REJECTED' + placement_status.timestamp = timezone.now() + placement_status.no_of_days = 2 + placement_status.save() + officer_recipients = User.objects.filter( + current_designation__designation__name__in=['placement officer', 'placement chairman'], + ).distinct() + _send_placement_notifications( + actor=request.user, + recipients=officer_recipients, + description='{} declined the offer for {}.'.format(student.id.id, schedule.notify_id.company_name), + ) + return Response({'message': 'Invitation declined successfully.'}, status=status.HTTP_200_OK) + + active_application_count = PlacementApplication.objects.filter( + student=student, + ).exclude(status='withdrawn').count() + application_limit = _max_active_application_limit() + warning_threshold = max(application_limit - 2, 1) + warning_message = None + if active_application_count >= application_limit: + return Response( + { + 'detail': 'You can only have {} active applications at a time.'.format( + application_limit, + ), + }, + status=status.HTTP_403_FORBIDDEN, + ) + elif active_application_count >= warning_threshold: + warning_message = 'You have {} active applications. The limit is {}.'.format( + active_application_count, + application_limit, + ) + + application, created = PlacementApplication.objects.get_or_create( + schedule=schedule, + student=student, + defaults={'status': 'pending'}, + ) + if not created: + if application.status == 'withdrawn': + return Response( + {'detail': 'This application was withdrawn and cannot be submitted again.'}, + status=status.HTTP_409_CONFLICT, + ) + return Response( + {'detail': 'You have already applied for this job.'}, + status=status.HTTP_409_CONFLICT, + ) + + responses = request.data.get('responses') or [] + if responses: + PlacementApplicationResponse.objects.filter(application=application).delete() + for item in responses: + field = PlacementField.objects.filter(id=item.get('field_id')).first() + PlacementApplicationResponse.objects.create( + application=application, + field=field, + value=str(item.get('value') or ''), + ) + _log_profile_action( + student, + request.user, + 'application_submitted', + {'job_id': schedule.id, 'company_name': schedule.notify_id.company_name}, + ) + response_payload = {'message': 'Application submitted successfully.'} + if warning_message: + response_payload['warning'] = warning_message + return Response(response_payload, status=status.HTTP_200_OK) + + +@api_view(['DELETE']) +@permission_classes([IsAuthenticated]) +@authentication_classes([TokenAuthentication]) +def withdraw_application_api(request, schedule_id): + student = selectors.get_student_for_user(request.user) + application = get_object_or_404( + PlacementApplication.objects.select_related('schedule__notify_id'), + schedule_id=schedule_id, + student=student, + ) + if application.status == 'withdrawn': + return Response({'detail': 'Application already withdrawn.'}, status=status.HTTP_409_CONFLICT) + + application.status = 'withdrawn' + application.withdrawn_at = timezone.now() + application.save(update_fields=['status', 'withdrawn_at']) + + placement_status = PlacementStatus.objects.filter( + notify_id=application.schedule.notify_id, + unique_id=student, + invitation__in=['PENDING', 'ACCEPTED'], + ).first() + if placement_status: + placement_status.invitation = 'REJECTED' + placement_status.timestamp = timezone.now() + placement_status.no_of_days = 2 + placement_status.save(update_fields=['invitation', 'timestamp', 'no_of_days']) + + officer_recipients = User.objects.filter( + current_designation__designation__name__in=['placement officer', 'placement chairman'], + ).distinct() + # Notify company if company user exists + company_users = User.objects.filter(email=application.schedule.company.company_name + '@example.com') if application.schedule.company and application.schedule.company.company_name else [] + notification_recipients = list(officer_recipients) + list(company_users) + notification_recipients.append(request.user) + _send_placement_notifications( + actor=request.user, + recipients=notification_recipients, + description='{} withdrew the application for {}.'.format( + student.id.id, + application.schedule.notify_id.company_name, + ), + ) + _log_profile_action( + student, + request.user, + 'application_withdrawn', + {'job_id': application.schedule_id, 'company_name': application.schedule.notify_id.company_name}, + ) + return Response({'message': 'Application withdrawn successfully.'}, status=status.HTTP_200_OK) + + +@api_view(['GET', 'PUT', 'DELETE']) +@permission_classes([IsAuthenticated]) +@authentication_classes([TokenAuthentication]) +def student_applications_api(request, identifier): + if request.method == 'PUT': + if not _is_tpo_user(request.user): + return Response( + {'detail': 'Only TPO users can update application status.'}, + status=status.HTTP_403_FORBIDDEN, + ) + application = get_object_or_404(PlacementApplication, pk=identifier) + if application.status in ['accept', 'reject', 'withdrawn']: + return Response( + {'detail': 'Final application status cannot be changed.'}, + status=status.HTTP_409_CONFLICT, + ) + status_value = request.data.get('status') or 'pending' + allowed_statuses = [ + 'pending', + 'shortlisted', + 'interview_scheduled', + 'interview_completed', + 'offer_released', + 'accept', + 'reject', + ] + next_status = status_value if status_value in allowed_statuses else 'pending' + remarks = request.data.get('remarks') or '' + application.status = next_status + application.remarks = remarks + application.save(update_fields=['status', 'remarks', 'updated_at']) + student_user = application.student.id.user + if application.status == 'accept': + placement_status, _ = PlacementStatus.objects.get_or_create( + notify_id=application.schedule.notify_id, + unique_id=application.student, + ) + placement_status.invitation = 'PENDING' + placement_status.placed = 'PLACED' + placement_status.timestamp = timezone.now() + placement_status.no_of_days = 2 + placement_status.save() + _ensure_placement_record_for_selection(application) + _create_application_timeline_entry( + application, + stage='Selected', + remarks=remarks or 'Congratulations! You have been selected.', + actor=request.user, + ) + _send_placement_notifications( + actor=request.user, + recipients=[student_user], + description='You received an offer for {}. Please respond within 48 hours.'.format( + application.schedule.notify_id.company_name, + ), + ) + elif application.status == 'reject': + placement_status, _ = PlacementStatus.objects.get_or_create( + notify_id=application.schedule.notify_id, + unique_id=application.student, + ) + placement_status.invitation = 'REJECTED' + placement_status.timestamp = timezone.now() + placement_status.no_of_days = 2 + placement_status.save() + _create_application_timeline_entry( + application, + stage='Rejected', + remarks=remarks or 'Your application has been rejected.', + actor=request.user, + ) + _send_placement_notifications( + actor=request.user, + recipients=[student_user], + description='Your application for {} has been rejected.'.format( + application.schedule.notify_id.company_name, + ), + ) + elif application.status == 'offer_released': + placement_status, _ = PlacementStatus.objects.get_or_create( + notify_id=application.schedule.notify_id, + unique_id=application.student, + ) + placement_status.invitation = 'PENDING' + placement_status.timestamp = timezone.now() + placement_status.no_of_days = 2 + placement_status.save() + _create_application_timeline_entry( + application, + stage='Offer Released', + remarks=remarks or 'Offer released. Please respond from your dashboard.', + actor=request.user, + ) + _send_placement_notifications( + actor=request.user, + recipients=[student_user], + description='Offer released for {}. Please check your placement dashboard.'.format( + application.schedule.notify_id.company_name, + ), + ) + else: + _create_application_timeline_entry( + application, + stage=_application_stage_label(application.status), + remarks=remarks or 'Application status updated to {}.'.format(_application_stage_label(application.status)), + actor=request.user, + ) + _send_placement_notifications( + actor=request.user, + recipients=[student_user], + description='Your application status for {} was updated to {}.'.format( + application.schedule.notify_id.company_name, + application.status, + ), + ) + return Response({'message': 'Application status updated successfully.'}, status=status.HTTP_200_OK) + + if request.method == 'DELETE': + if not _is_tpo_user(request.user): + return Response( + {'detail': 'Only TPO users can delete applications.'}, + status=status.HTTP_403_FORBIDDEN, + ) + application = get_object_or_404(PlacementApplication, pk=identifier) + student_user = application.student.id.user + company_name = application.schedule.notify_id.company_name + application.delete() + _send_placement_notifications( + actor=request.user, + recipients=[student_user], + description='Your application for {} was removed by the placement office.'.format( + company_name, + ), + ) + return Response({'message': 'Application deleted successfully.'}, status=status.HTTP_200_OK) + + if not _is_tpo_user(request.user): + return Response( + {'detail': 'Only TPO users can view applicant lists.'}, + status=status.HTTP_403_FORBIDDEN, + ) + + applications = PlacementApplication.objects.select_related( + 'student__id__user', + ).filter(schedule_id=identifier).order_by('-created_at') + students = [] + for app in applications: + students.append({ + 'id': app.id, + 'username': app.student.id.user.username, + 'name': '{} {}'.format( + app.student.id.user.first_name, + app.student.id.user.last_name, + ).strip() or app.student.id.user.username, + 'roll_no': app.student.id.id, + 'email': app.student.id.user.email, + 'cpi': app.student.cpi, + 'status': app.status, + 'applied_at': app.created_at.isoformat() if app.created_at else None, + }) + return Response({'students': students}, status=status.HTTP_200_OK) + + +@api_view(['GET']) +@permission_classes([IsAuthenticated]) +@authentication_classes([TokenAuthentication]) +def download_applications_api(request, schedule_id): + if not _is_tpo_user(request.user): + return Response( + {'detail': 'Only TPO users can export applicant data.'}, + status=status.HTTP_403_FORBIDDEN, + ) + applications = PlacementApplication.objects.select_related( + 'student__id__user', + ).filter(schedule_id=schedule_id).order_by('-created_at') + response = HttpResponse(content_type='application/ms-excel') + response['Content-Disposition'] = 'attachment; filename="applications_{}.xls"'.format(schedule_id) + workbook = xlwt.Workbook(encoding='utf-8') + worksheet = workbook.add_sheet('Applications') + headers = ['Name', 'Roll No', 'Email', 'CPI', 'Status'] + header_style = xlwt.XFStyle() + header_style.font.bold = True + for index, header in enumerate(headers): + worksheet.write(0, index, header, header_style) + row_style = xlwt.XFStyle() + for row_index, application in enumerate(applications, start=1): + worksheet.write(row_index, 0, '{} {}'.format(application.student.id.user.first_name, application.student.id.user.last_name).strip() or application.student.id.user.username, row_style) + worksheet.write(row_index, 1, application.student.id.id, row_style) + worksheet.write(row_index, 2, application.student.id.user.email, row_style) + worksheet.write(row_index, 3, application.student.cpi, row_style) + worksheet.write(row_index, 4, application.status, row_style) + workbook.save(response) + return response + + +@api_view(['POST']) +@permission_classes([IsAuthenticated]) +@authentication_classes([TokenAuthentication]) +def next_round_api(request, schedule_id): + if not _is_tpo_user(request.user): + return Response( + {'detail': 'Only TPO users can schedule next rounds.'}, + status=status.HTTP_403_FORBIDDEN, + ) + schedule = get_object_or_404(PlacementSchedule, pk=schedule_id) + start_datetime = _parse_datetime(request.data.get('start_datetime')) + end_datetime = _parse_datetime(request.data.get('end_datetime')) + + if not start_datetime: + return Response( + {'detail': 'Interview start date and time are required.'}, + status=status.HTTP_400_BAD_REQUEST, + ) + + if not end_datetime: + end_datetime = start_datetime + datetime.timedelta(hours=1) + + if end_datetime <= start_datetime: + return Response( + {'detail': 'Interview end time must be after start time.'}, + status=status.HTTP_400_BAD_REQUEST, + ) + + application_ids = request.data.get('application_ids') or [] + applications = PlacementApplication.objects.select_related('student__id__user').filter( + schedule=schedule, + ).exclude(status__in=['reject', 'withdrawn', 'accept']) + if application_ids: + applications = applications.filter(id__in=application_ids) + applications = list(applications) + if not applications: + return Response( + {'detail': 'Select at least one valid candidate to schedule the next round.'}, + status=status.HTTP_400_BAD_REQUEST, + ) + + conflicts = _collect_student_schedule_conflicts( + applications=applications, + start_dt=start_datetime, + end_dt=end_datetime, + current_schedule_id=schedule.id, + ) + if conflicts: + return Response( + { + 'detail': 'Selected interview time conflicts with student placement calendar.', + 'conflicts': conflicts, + }, + status=status.HTTP_400_BAD_REQUEST, + ) + + requested_round_no = request.data.get('round_no') + if requested_round_no in [None, '']: + existing_round_no = ( + PlacementRound.objects.filter(schedule=schedule) + .aggregate(max_round=Max('round_no')) + .get('max_round') + or 0 + ) + round_no = existing_round_no + 1 + else: + round_no = int(requested_round_no) + + feedback = request.data.get('feedback') + description = ( + feedback + if feedback not in [None, ''] + else request.data.get('description') or '' + ) + + round_obj = PlacementRound.objects.create( + schedule=schedule, + round_no=round_no, + test_date=start_datetime.date(), + start_datetime=start_datetime, + end_datetime=end_datetime, + mode=request.data.get('mode') or '', + location_link=request.data.get('location_link') or '', + description=description, + test_type=request.data.get('test_type') or '', + ) + applications_to_update = [] + for application in applications: + application.status = 'interview_scheduled' + application.remarks = description or application.remarks + applications_to_update.append(application) + PlacementInterviewSchedule.objects.create( + application=application, + round_no=round_no, + title=request.data.get('test_type') or 'Round {}'.format(round_no), + scheduled_at=start_datetime, + end_datetime=end_datetime, + mode=request.data.get('mode') or '', + location=request.data.get('location_link') or '', + meeting_link=request.data.get('location_link') or '', + remarks=description, + created_by=request.user, + ) + _create_application_timeline_entry( + application, + stage='Interview Scheduled', + remarks=description or 'Interview round scheduled.', + actor=request.user, + ) + PlacementApplication.objects.bulk_update(applications_to_update, ['status', 'remarks']) + + recipients = [application.student.id.user for application in applications] + if recipients: + _send_placement_notifications( + actor=request.user, + recipients=recipients, + description='Interview schedule updated for {}: {} on {}.'.format( + schedule.notify_id.company_name, + round_obj.test_type or 'Round {}'.format(round_obj.round_no), + round_obj.start_datetime.isoformat() if round_obj.start_datetime else 'TBA', + ), + ) + return Response( + { + 'id': round_obj.id, + 'scheduled_candidates': len(applications), + 'start_datetime': round_obj.start_datetime.isoformat() if round_obj.start_datetime else None, + 'end_datetime': round_obj.end_datetime.isoformat() if round_obj.end_datetime else None, + }, + status=status.HTTP_201_CREATED, + ) + + +@api_view(['GET']) +@permission_classes([IsAuthenticated]) +@authentication_classes([TokenAuthentication]) +def timeline_api(request, schedule_id): + rounds = list(PlacementRound.objects.filter(schedule_id=schedule_id).order_by('round_no', 'created_at')) + student_data = selectors.get_designation_queryset(request.user, "student") + application = None + if student_data: + student = selectors.get_student_for_user(request.user) + application = PlacementApplication.objects.filter(schedule_id=schedule_id, student=student).first() + + data = [] + if application: + timeline_entries = _serialize_application_timeline(application) + for index, item in enumerate(timeline_entries): + data.append({ + 'round_no': index, + 'test_name': item['stage'], + 'test_date': item['created_at'], + 'description': item['remarks'], + }) + if application.status == 'reject': + return Response({'next_data': data}, status=status.HTTP_200_OK) + if application.status == 'withdrawn': + return Response({'next_data': data}, status=status.HTTP_200_OK) + + interviews = PlacementInterviewSchedule.objects.filter(application=application).order_by('scheduled_at', 'id') + if interviews.exists(): + data.extend([{ + 'round_no': max(item.round_no, len(data)), + 'test_name': item.title or 'Round {}'.format(item.round_no), + 'test_date': item.scheduled_at.isoformat() if item.scheduled_at else None, + 'start_datetime': item.scheduled_at.isoformat() if item.scheduled_at else None, + 'end_datetime': item.end_datetime.isoformat() if item.end_datetime else None, + 'mode': item.mode, + 'location_link': item.meeting_link or item.location, + 'description': item.remarks, + 'feedback': item.remarks, + 'outcome': item.outcome, + } for item in interviews]) + return Response({'next_data': data}, status=status.HTTP_200_OK) + + if not rounds: + if not data: + data.append({ + 'round_no': 0, + 'test_name': 'Application', + 'test_date': None, + 'description': 'To be updated', + }) + return Response({'next_data': data}, status=status.HTTP_200_OK) + + data.extend([{ + 'round_no': max(item.round_no, len(data)), + 'test_name': item.test_type or 'Round {}'.format(item.round_no), + 'test_date': item.start_datetime.isoformat() if item.start_datetime else (item.test_date.isoformat() if item.test_date else None), + 'start_datetime': item.start_datetime.isoformat() if item.start_datetime else None, + 'end_datetime': item.end_datetime.isoformat() if item.end_datetime else None, + 'mode': item.mode, + 'location_link': item.location_link, + 'description': item.description, + 'feedback': item.description, + } for item in rounds]) + return Response({'next_data': data}, status=status.HTTP_200_OK) + + +@api_view(['GET']) +@permission_classes([IsAuthenticated]) +@authentication_classes([TokenAuthentication]) +def my_applications_api(request): + student = selectors.get_student_for_user(request.user) + applications = PlacementApplication.objects.select_related( + 'schedule__notify_id', + 'schedule__role', + ).prefetch_related( + Prefetch( + 'interview_schedules', + queryset=PlacementInterviewSchedule.objects.order_by('scheduled_at', 'id'), + to_attr='prefetched_interviews', + ), + ).filter(student=student).order_by('-created_at') + offer_map = { + item.notify_id_id: item + for item in PlacementStatus.objects.filter( + unique_id=student, + notify_id_id__in=[application.schedule.notify_id_id for application in applications], + ) + } + rows = [] + for application in applications: + interviews = list(getattr(application, 'prefetched_interviews', [])) + next_round = interviews[-1] if interviews else None + offer = offer_map.get(application.schedule.notify_id_id) + rows.append({ + 'application_id': application.id, + 'schedule_id': application.schedule_id, + 'company_name': application.schedule.notify_id.company_name, + 'role': application.schedule.get_role, + 'placement_type': application.schedule.notify_id.placement_type, + 'status': application.status, + 'status_label': application.status.replace('_', ' ').title(), + 'applied_at': application.created_at.isoformat() if application.created_at else None, + 'offer_id': offer.id if offer else None, + 'offer_status': offer.invitation if offer else None, + 'rounds': [ + { + 'id': item.id, + 'round_no': item.round_no, + 'title': item.title or 'Round {}'.format(item.round_no), + 'date': item.scheduled_at.isoformat() if item.scheduled_at else None, + 'description': item.remarks, + 'feedback': item.remarks, + 'outcome': item.outcome, + 'mode': item.mode, + 'location': item.meeting_link or item.location, + } + for item in interviews + ], + 'next_interview': { + 'round_no': next_round.round_no, + 'title': next_round.title or 'Round {}'.format(next_round.round_no), + 'date': next_round.scheduled_at.isoformat() if next_round.scheduled_at else None, + 'description': next_round.remarks, + 'feedback': next_round.remarks, + 'outcome': next_round.outcome, + } if next_round else None, + 'can_withdraw': application.status not in ['withdrawn', 'reject', 'accept'], + 'can_raise_appeal': application.status == 'reject', + }) + return Response({'applications': rows}, status=status.HTTP_200_OK) + + +@api_view(['GET', 'PUT']) +@permission_classes([IsAuthenticated]) +@authentication_classes([TokenAuthentication]) +def application_detail_api(request, application_id): + application = get_object_or_404( + PlacementApplication.objects.select_related( + 'student__id__user', + 'student__id__department', + 'schedule__notify_id', + 'schedule__role', + ).prefetch_related( + 'timeline_entries__actor', + 'interview_schedules', + ), + pk=application_id, + ) + + if request.method == 'GET': + if _is_tpo_user(request.user): + return Response(_serialize_tpo_application_detail(application, request=request), status=status.HTTP_200_OK) + + student = selectors.get_student_for_user(request.user) + if application.student_id != student.id: + return Response({'detail': 'Application not found.'}, status=status.HTTP_404_NOT_FOUND) + return Response(_serialize_tpo_application_detail(application, request=request), status=status.HTTP_200_OK) + + if not _is_tpo_user(request.user): + return Response({'detail': 'Only TPO users can update applicants.'}, status=status.HTTP_403_FORBIDDEN) + + if application.status in ['withdrawn']: + return Response({'detail': 'Withdrawn applications cannot be updated.'}, status=status.HTTP_409_CONFLICT) + + next_status = request.data.get('status') or application.status + remarks = request.data.get('remarks') or '' + allowed_statuses = [ + 'pending', + 'shortlisted', + 'interview_scheduled', + 'interview_completed', + 'offer_released', + 'accept', + 'reject', + ] + if next_status not in allowed_statuses: + return Response({'status': ['Invalid application status.']}, status=status.HTTP_400_BAD_REQUEST) + + application.status = next_status + application.remarks = remarks + application.save(update_fields=['status', 'remarks', 'updated_at']) + + student_user = application.student.id.user + stage_label = _application_stage_label(next_status) + _create_application_timeline_entry( + application, + stage=stage_label, + remarks=remarks or stage_label, + actor=request.user, + ) + + if next_status == 'offer_released': + placement_status, _ = PlacementStatus.objects.get_or_create( + notify_id=application.schedule.notify_id, + unique_id=application.student, + ) + placement_status.invitation = 'PENDING' + placement_status.timestamp = timezone.now() + placement_status.no_of_days = 2 + placement_status.save() + _send_placement_notifications( + actor=request.user, + recipients=[student_user], + description='You received an offer for {}. Please respond within 48 hours.'.format( + application.schedule.notify_id.company_name, + ), + ) + elif next_status == 'accept': + placement_status, _ = PlacementStatus.objects.get_or_create( + notify_id=application.schedule.notify_id, + unique_id=application.student, + ) + placement_status.invitation = 'PENDING' + placement_status.placed = 'PLACED' + placement_status.timestamp = timezone.now() + placement_status.no_of_days = 2 + placement_status.save() + _ensure_placement_record_for_selection(application) + _send_placement_notifications( + actor=request.user, + recipients=[student_user], + description='Congratulations! You have been selected for {}. Please review the offer in your dashboard.'.format( + application.schedule.notify_id.company_name, + ), + ) + elif next_status == 'reject': + _send_placement_notifications( + actor=request.user, + recipients=[student_user], + description='Your application for {} has been rejected.'.format( + application.schedule.notify_id.company_name, + ), + ) + else: + _send_placement_notifications( + actor=request.user, + recipients=[student_user], + description='Your application status for {} is now {}.'.format( + application.schedule.notify_id.company_name, + stage_label, + ), + ) + + application.refresh_from_db() + return Response(_serialize_tpo_application_detail(application, request=request), status=status.HTTP_200_OK) + + +@api_view(['POST']) +@permission_classes([IsAuthenticated]) +@authentication_classes([TokenAuthentication]) +def application_interview_schedule_api(request, application_id): + if not _is_tpo_user(request.user): + return Response({'detail': 'Only TPO users can manage interviews.'}, status=status.HTTP_403_FORBIDDEN) + + application = get_object_or_404( + PlacementApplication.objects.select_related('student__id__user', 'schedule__notify_id'), + pk=application_id, + ) + scheduled_at = _parse_datetime(request.data.get('scheduled_at')) + if not scheduled_at: + return Response({'scheduled_at': ['Interview date and time are required.']}, status=status.HTTP_400_BAD_REQUEST) + + end_datetime = _parse_datetime(request.data.get('end_datetime')) + if not end_datetime: + end_datetime = scheduled_at + datetime.timedelta(hours=1) + + requested_round_no = request.data.get('round_no') + if requested_round_no in [None, '']: + existing_round_no = ( + PlacementInterviewSchedule.objects.filter(application=application) + .aggregate(max_round=Max('round_no')) + .get('max_round') + or 0 + ) + round_no = existing_round_no + 1 + else: + round_no = int(requested_round_no) + + feedback = request.data.get('feedback') + remarks = ( + feedback + if feedback not in [None, ''] + else request.data.get('remarks') or '' + ) + + interview = PlacementInterviewSchedule.objects.create( + application=application, + round_no=round_no, + title=request.data.get('title') or '', + scheduled_at=scheduled_at, + end_datetime=end_datetime, + mode=request.data.get('mode') or '', + location=request.data.get('location') or '', + meeting_link=request.data.get('meeting_link') or '', + remarks=remarks, + outcome=request.data.get('outcome') or 'pending', + is_active=bool(request.data.get('is_active', True)), + created_by=request.user, + ) + application.status = 'interview_scheduled' + application.remarks = remarks or application.remarks + application.save(update_fields=['status', 'remarks', 'updated_at']) + _create_application_timeline_entry( + application, + stage='Interview Scheduled', + remarks=remarks or 'Interview round scheduled.', + actor=request.user, + ) + _send_placement_notifications( + actor=request.user, + recipients=[application.student.id.user], + description='Interview scheduled for {} on {}.'.format( + application.schedule.notify_id.company_name, + interview.scheduled_at.isoformat(), + ), + ) + return Response(_serialize_application_interview(interview), status=status.HTTP_201_CREATED) + + +@api_view(['GET']) +@permission_classes([IsAuthenticated]) +@authentication_classes([TokenAuthentication]) +def my_offers_api(request): + student = selectors.get_student_for_user(request.user) + visible_offer_statuses = {'offer_released', 'accept'} + offers = PlacementStatus.objects.select_related('notify_id').filter( + unique_id=student, + ).order_by('-timestamp', '-id') + notify_ids = [offer.notify_id_id for offer in offers] + schedules = PlacementSchedule.objects.select_related('role', 'notify_id').filter( + notify_id_id__in=notify_ids, + ).order_by('notify_id_id', '-id') + schedule_map = {} + for schedule in schedules: + if schedule.notify_id_id not in schedule_map: + schedule_map[schedule.notify_id_id] = schedule + + applications = PlacementApplication.objects.filter( + student=student, + schedule__notify_id_id__in=notify_ids, + ).select_related('schedule__notify_id').order_by('schedule__notify_id_id', '-created_at') + application_map = {} + for application in applications: + notify_id = application.schedule.notify_id_id + if notify_id not in application_map: + application_map[notify_id] = application + + rows = [] + for offer in offers: + schedule = schedule_map.get(offer.notify_id_id) + application = application_map.get(offer.notify_id_id) + if application is None or application.status not in visible_offer_statuses: + continue + deadline = offer.response_date if offer.timestamp else None + rows.append({ + 'id': offer.id, + 'schedule_id': schedule.id if schedule else None, + 'company_name': offer.notify_id.company_name, + 'role': schedule.get_role if schedule else '', + 'ctc': str(offer.notify_id.ctc), + 'status': offer.invitation, + 'response_deadline': deadline.isoformat() if deadline else None, + 'expired': bool(deadline and timezone.now() > deadline), + }) + return Response({'offers': rows}, status=status.HTTP_200_OK) + + +@api_view(['GET']) +@permission_classes([IsAuthenticated]) +@authentication_classes([TokenAuthentication]) +def calendar_api(request): + if not request.user.is_authenticated: + return Response( + {'detail': 'Authentication credentials were not provided.'}, + status=status.HTTP_403_FORBIDDEN, + ) + rounds = PlacementRound.objects.select_related('schedule__notify_id').all().order_by('test_date') + schedule_data = [{ + 'id': item.schedule.id, + 'company_name': item.schedule.notify_id.company_name, + 'round': item.round_no, + 'date': item.start_datetime.isoformat() if item.start_datetime else (item.test_date.isoformat() if item.test_date else item.schedule.placement_date.isoformat()), + 'end_datetime': item.end_datetime.isoformat() if item.end_datetime else None, + 'description': item.description, + 'type': item.test_type, + 'mode': item.mode, + 'location_link': item.location_link, + } for item in rounds] + if not schedule_data: + schedules = PlacementSchedule.objects.select_related('notify_id').all() + schedule_data = [{ + 'id': item.id, + 'company_name': item.notify_id.company_name, + 'round': 0, + 'date': item.placement_date.isoformat(), + 'description': item.description, + 'type': item.notify_id.placement_type, + } for item in schedules] + return Response({'schedule_data': schedule_data}, status=status.HTTP_200_OK) + + +@api_view(['POST']) +@permission_classes([IsAuthenticated]) +@authentication_classes([TokenAuthentication]) +def generate_cv_api(request): + username = request.user.username + profile = get_object_or_404(ExtraInfo, Q(user=request.user)) + student = get_object_or_404(Student, Q(id=profile.id)) + now = datetime.datetime.now() + if int(str(profile.id)[:2]) == 20: + roll = (1 + now.year - int(str(profile.id)[:4])) if now.month > 4 else (now.year - int(str(profile.id)[:4])) + else: + roll = (1 + now.year - int("20" + str(profile.id)[0:2])) if now.month > 4 else (now.year - int("20" + str(profile.id)[0:2])) + + def _flag(name): + return '1' if request.data.get(name, True) else '0' + + reference = Reference.objects.filter(unique_id=student) + profile_picture_url = profile.profile_picture.url if profile.profile_picture else '' + profile_picture_path = profile.profile_picture.path if profile.profile_picture else '' + context = { + 'pagesize': 'A4', + 'user': request.user, + 'references': reference, + 'profile': profile, + 'profile_picture': profile_picture_url, + 'profile_picture_path': profile_picture_path, + 'projects': Project.objects.filter(unique_id=student), + 'skills': Has.objects.select_related('skill_id').filter(unique_id=student), + 'educations': Education.objects.filter(unique_id=student), + 'courses': Course.objects.filter(unique_id=student), + 'experiences': Experience.objects.filter(unique_id=student), + 'referencecheck': '1' if reference.exists() and request.data.get('references', True) else '0', + 'achievements': Achievement.objects.filter(unique_id=student), + 'extracurriculars': Extracurricular.objects.filter(unique_id=student), + 'publications': Publication.objects.filter(unique_id=student), + 'patents': Patent.objects.filter(unique_id=student), + 'roll': roll, + 'achievementcheck': _flag('achievements'), + 'extracurricularcheck': _flag('extracurriculars'), + 'educationcheck': _flag('education'), + 'publicationcheck': _flag('publications'), + 'patentcheck': _flag('patents'), + 'conferencecheck': _flag('conferences'), + 'conferences': Conference.objects.filter(unique_id=student), + 'internshipcheck': _flag('experience'), + 'projectcheck': _flag('projects'), + 'coursecheck': _flag('courses'), + 'skillcheck': _flag('skills'), + 'today': datetime.date.today(), + } + pdf_response = render_to_pdf('placementModule/cv.html', context) + if isinstance(pdf_response, HttpResponse): + selected_sections = sorted( + key for key, enabled in request.data.items() if str(enabled).lower() in ['true', '1', 'yes', 'on'] + ) if hasattr(request.data, 'items') else [] + PlacementProfileAuditLog.objects.create( + student=student, + actor=request.user, + action='resume_downloaded', + details={ + 'filename': 'student_cv.pdf', + 'selected_sections': selected_sections, + }, + ) + pdf_response['Content-Disposition'] = 'attachment; filename="student_cv.pdf"' + return pdf_response + + +@api_view(['GET']) +@permission_classes([IsAuthenticated]) +@authentication_classes([TokenAuthentication]) +def debarred_students_api(request): + if not _is_tpo_user(request.user): + return Response( + {'detail': 'Only TPO users can access debarred students.'}, + status=status.HTTP_403_FORBIDDEN, + ) + rows = [] + records = StudentPlacement.objects.select_related('unique_id__id__user').filter(debar='DEBAR') + for item in records: + rows.append({ + 'id': item.unique_id.id.id, + 'roll_no': item.unique_id.id.id, + 'name': '{} {}'.format(item.unique_id.id.user.first_name, item.unique_id.id.user.last_name).strip() or item.unique_id.id.user.username, + 'description': item.debar_reason or '', + }) + return Response(rows, status=status.HTTP_200_OK) + + +@api_view(['GET', 'POST', 'DELETE']) +@permission_classes([IsAuthenticated]) +@authentication_classes([TokenAuthentication]) +def debarred_status_api(request, roll_no): + if not _is_tpo_user(request.user): + return Response( + {'detail': 'Only TPO users can manage debarred status.'}, + status=status.HTTP_403_FORBIDDEN, + ) + student = get_object_or_404(Student.objects.select_related('id__user', 'id__department'), pk=roll_no) + student_placement = _ensure_studentplacement(student) + + if request.method == 'GET': + return Response({ + 'name': '{} {}'.format(student.id.user.first_name, student.id.user.last_name).strip() or student.id.user.username, + 'programme': student.programme, + 'year': student.batch, + 'department': student.id.department.name if student.id.department else '', + 'email': student.id.user.email, + 'description': student_placement.debar_reason or '', + }, status=status.HTTP_200_OK) + + if request.method == 'DELETE': + student_placement.debar = 'NOT DEBAR' + student_placement.debar_reason = '' + student_placement.save() + _send_placement_notifications( + actor=request.user, + recipients=[student.id.user], + description='Your placement debarment has been removed.', + ) + return Response({'message': 'Student un-debarred successfully.'}, status=status.HTTP_200_OK) + + student_placement.debar = 'DEBAR' + student_placement.debar_reason = request.data.get('reason') or '' + student_placement.save() + _send_placement_notifications( + actor=request.user, + recipients=[student.id.user], + description='You have been debarred from placement activities. {}'.format(student_placement.debar_reason).strip(), + ) + return Response({'message': 'Student debarred successfully.'}, status=status.HTTP_200_OK) + + +def _serialize_restriction(restriction): + return { + 'id': restriction.id, + 'criteria': restriction.criteria, + 'condition': restriction.condition, + 'value': restriction.value, + 'description': restriction.description, + } + + +def _serialize_policy(policy): + return { + 'id': policy.id, + 'title': policy.title, + 'description': policy.description, + 'created_by': policy.created_by.get_full_name().strip() or policy.created_by.username if policy.created_by else '', + 'created_at': policy.created_at.isoformat() if policy.created_at else None, + 'updated_at': policy.updated_at.isoformat() if policy.updated_at else None, + } + + +@api_view(['GET', 'POST']) +@permission_classes([IsAuthenticated]) +@authentication_classes([TokenAuthentication]) +def restrictions_api(request): + if not _is_tpo_user(request.user): + return Response( + {'detail': 'Only TPO users can manage placement restrictions.'}, + status=status.HTTP_403_FORBIDDEN, + ) + if request.method == 'GET': + data = [_serialize_restriction(item) for item in PlacementRestriction.objects.all().order_by('-id')] + return Response(data, status=status.HTTP_200_OK) + + restriction = PlacementRestriction.objects.create( + criteria=request.data.get('criteria') or '', + condition=request.data.get('condition') or '', + value=request.data.get('value') or '', + description=request.data.get('description') or '', + ) + return Response(_serialize_restriction(restriction), status=status.HTTP_201_CREATED) + + +@api_view(['PUT', 'DELETE']) +@permission_classes([IsAuthenticated]) +@authentication_classes([TokenAuthentication]) +def restriction_detail_api(request, restriction_id): + if not _is_tpo_user(request.user): + return Response( + {'detail': 'Only TPO users can manage placement restrictions.'}, + status=status.HTTP_403_FORBIDDEN, + ) + restriction = get_object_or_404(PlacementRestriction, pk=restriction_id) + + if request.method == 'DELETE': + restriction.delete() + return Response({'message': 'Restriction deleted successfully.'}, status=status.HTTP_200_OK) + + restriction.criteria = request.data.get('criteria') or restriction.criteria + restriction.condition = request.data.get('condition') or restriction.condition + restriction.value = request.data.get('value') or restriction.value + restriction.description = request.data.get('description') or '' + restriction.save() + return Response(_serialize_restriction(restriction), status=status.HTTP_200_OK) + + +@api_view(['GET', 'POST']) +@permission_classes([IsAuthenticated]) +@authentication_classes([TokenAuthentication]) +def placement_policies_api(request): + if not selectors.get_designation_queryset(request.user, "placement chairman").exists(): + return Response( + {'detail': 'Only placement chairman users can manage placement policies.'}, + status=status.HTTP_403_FORBIDDEN, + ) + + if request.method == 'GET': + data = [_serialize_policy(item) for item in PlacementPolicy.objects.all()] + return Response(data, status=status.HTTP_200_OK) + + title = (request.data.get('title') or '').strip() + description = (request.data.get('description') or '').strip() + + errors = {} + if not title: + errors['title'] = ['This field is required.'] + if not description: + errors['description'] = ['This field is required.'] + if errors: + return Response(errors, status=status.HTTP_400_BAD_REQUEST) + + policy = PlacementPolicy.objects.create( + title=title, + description=description, + created_by=request.user, + ) + return Response(_serialize_policy(policy), status=status.HTTP_201_CREATED) + + +@api_view(['PUT']) +@permission_classes([IsAuthenticated]) +@authentication_classes([TokenAuthentication]) +def placement_policy_detail_api(request, policy_id): + if not selectors.get_designation_queryset(request.user, "placement chairman").exists(): + return Response( + {'detail': 'Only placement chairman users can manage placement policies.'}, + status=status.HTTP_403_FORBIDDEN, + ) + + policy = get_object_or_404(PlacementPolicy, pk=policy_id) + title = (request.data.get('title') or '').strip() + description = (request.data.get('description') or '').strip() + + errors = {} + if not title: + errors['title'] = ['This field is required.'] + if not description: + errors['description'] = ['This field is required.'] + if errors: + return Response(errors, status=status.HTTP_400_BAD_REQUEST) + + policy.title = title + policy.description = description + policy.save(update_fields=['title', 'description', 'updated_at']) + return Response(_serialize_policy(policy), status=status.HTTP_200_OK) + + +def _ensure_alumni_designation(user): + designation, _ = Designation.objects.get_or_create( + name='alumni', + defaults={'full_name': 'Alumni', 'type': 'administrative'}, + ) + HoldsDesignation.objects.get_or_create( + user=user, + working=user, + designation=designation, + ) + + +def _is_tpo_user(user): + return selectors.is_tpo(user) + + +def _max_active_application_limit(): + try: + return max(int(getattr(settings, 'PLACEMENT_MAX_ACTIVE_APPLICATIONS', 10)), 1) + except (TypeError, ValueError): + return 10 + + +def _serialize_alumni_profile(profile): + extra = ExtraInfo.objects.filter(user=profile.user).select_related('department').first() + return { + 'id': profile.id, + 'username': profile.user.username, + 'full_name': profile.user.get_full_name().strip() or profile.user.username, + 'email': profile.user.email, + 'graduation_year': profile.graduation_year, + 'degree': profile.degree, + 'current_company': profile.current_company, + 'current_designation': profile.current_designation, + 'linkedin_url': profile.linkedin_url, + 'verification_document': profile.verification_document.url if profile.verification_document else None, + 'verification_notes': profile.verification_notes, + 'status': profile.status, + 'topics': [item.strip() for item in (profile.topics or '').split(',') if item.strip()], + 'availability': profile.availability, + 'bio': profile.bio, + 'mentorship_enabled': profile.mentorship_enabled, + 'department': extra.department.name if extra and extra.department else '', + 'approved_at': profile.approved_at.isoformat() if profile.approved_at else None, + } + + +def _serialize_referral(referral): + return { + 'id': referral.id, + 'title': referral.title, + 'company': referral.company, + 'location': referral.location, + 'application_url': referral.application_url, + 'description': referral.description, + 'expires_at': referral.expires_at.isoformat() if referral.expires_at else None, + 'created_at': referral.created_at.isoformat() if referral.created_at else None, + 'alumni': _serialize_alumni_profile(referral.alumni), + } + + +def _serialize_connection(connection): + return { + 'id': connection.id, + 'status': connection.status, + 'message': connection.message, + 'created_at': connection.created_at.isoformat() if connection.created_at else None, + 'responded_at': connection.responded_at.isoformat() if connection.responded_at else None, + 'student': { + 'roll_no': connection.student.id.id, + 'name': connection.student.id.user.get_full_name().strip() or connection.student.id.user.username, + 'email': connection.student.id.user.email, + }, + 'alumni': _serialize_alumni_profile(connection.alumni), + } + + +def _serialize_session(session): + return { + 'id': session.id, + 'topic': session.topic, + 'agenda': session.agenda, + 'scheduled_at': session.scheduled_at.isoformat() if session.scheduled_at else None, + 'mode': session.mode, + 'meeting_link': session.meeting_link, + 'student_message': session.student_message, + 'alumni_message': session.alumni_message, + 'status': session.status, + 'student': { + 'roll_no': session.student.id.id, + 'name': session.student.id.user.get_full_name().strip() or session.student.id.user.username, + 'email': session.student.id.user.email, + }, + 'alumni': _serialize_alumni_profile(session.alumni), + } + + +@api_view(['GET', 'POST', 'PUT']) +@permission_classes([IsAuthenticated]) +@authentication_classes([TokenAuthentication]) +def alumni_profile_api(request): + profile = AlumniProfile.objects.filter(user=request.user).first() + + if request.method == 'GET': + return Response({ + 'profile': _serialize_alumni_profile(profile) if profile else None, + 'can_access': bool(profile and profile.status == 'approved'), + 'is_tpo': _is_tpo_user(request.user), + }, status=status.HTTP_200_OK) + + data = request.data + if profile is None: + graduation_year = data.get('graduation_year') + if not graduation_year: + return Response({'graduation_year': ['This field is required.']}, status=status.HTTP_400_BAD_REQUEST) + profile = AlumniProfile.objects.create( + user=request.user, + graduation_year=int(graduation_year), + degree=data.get('degree') or '', + current_company=data.get('current_company') or '', + current_designation=data.get('current_designation') or '', + linkedin_url=data.get('linkedin_url') or '', + verification_document=request.FILES.get('verification_document'), + bio=data.get('bio') or '', + topics=data.get('topics') or '', + availability=data.get('availability') or '', + mentorship_enabled=str(data.get('mentorship_enabled', '')).lower() in ['true', '1', 'yes', 'on'], + verification_notes=data.get('verification_notes') or '', + status='pending', + ) + officer_recipients = User.objects.filter( + current_designation__designation__name__in=['placement officer', 'placement chairman'], + ).distinct() + _send_placement_notifications( + actor=request.user, + recipients=officer_recipients, + description='New alumni verification request submitted by {}.'.format(request.user.username), + ) + return Response(_serialize_alumni_profile(profile), status=status.HTTP_201_CREATED) + + if profile.status == 'approved' or _is_tpo_user(request.user): + profile.degree = data.get('degree', profile.degree) + profile.current_company = data.get('current_company', profile.current_company) + profile.current_designation = data.get('current_designation', profile.current_designation) + profile.linkedin_url = data.get('linkedin_url', profile.linkedin_url) + profile.bio = data.get('bio', profile.bio) + profile.topics = data.get('topics', profile.topics) + profile.availability = data.get('availability', profile.availability) + if 'mentorship_enabled' in data: + profile.mentorship_enabled = str(data.get('mentorship_enabled')).lower() in ['true', '1', 'yes', 'on'] + if request.FILES.get('verification_document'): + profile.verification_document = request.FILES.get('verification_document') + profile.status = 'pending' + profile.save() + return Response(_serialize_alumni_profile(profile), status=status.HTTP_200_OK) + + return Response( + {'detail': 'Your alumni registration is awaiting approval.'}, + status=status.HTTP_403_FORBIDDEN, + ) + + +@api_view(['GET']) +@permission_classes([IsAuthenticated]) +@authentication_classes([TokenAuthentication]) +def alumni_directory_api(request): + profiles = AlumniProfile.objects.filter(status='approved').order_by('-approved_at', '-id') + if request.GET.get('mentors_only') in ['true', '1']: + profiles = profiles.filter(mentorship_enabled=True) + query = (request.GET.get('query') or '').strip() + if query: + profiles = profiles.filter( + Q(user__username__icontains=query) | + Q(user__first_name__icontains=query) | + Q(user__last_name__icontains=query) | + Q(current_company__icontains=query) | + Q(topics__icontains=query) + ) + return Response([_serialize_alumni_profile(item) for item in profiles], status=status.HTTP_200_OK) + + +@api_view(['GET']) +@permission_classes([IsAuthenticated]) +@authentication_classes([TokenAuthentication]) +def alumni_verification_list_api(request): + if not _is_tpo_user(request.user): + return Response({'detail': 'Only TPO users can access this queue.'}, status=status.HTTP_403_FORBIDDEN) + profiles = AlumniProfile.objects.select_related('user').all().order_by('status', '-created_at') + return Response([_serialize_alumni_profile(item) for item in profiles], status=status.HTTP_200_OK) + + +@api_view(['PUT']) +@permission_classes([IsAuthenticated]) +@authentication_classes([TokenAuthentication]) +def alumni_verification_detail_api(request, profile_id): + if not _is_tpo_user(request.user): + return Response({'detail': 'Only TPO users can verify alumni.'}, status=status.HTTP_403_FORBIDDEN) + profile = get_object_or_404(AlumniProfile, pk=profile_id) + decision = str(request.data.get('status') or '').lower() + if decision not in ['approved', 'rejected', 'pending']: + return Response({'status': ['Invalid verification status.']}, status=status.HTTP_400_BAD_REQUEST) + profile.status = decision + profile.verification_notes = request.data.get('verification_notes', profile.verification_notes) + profile.approved_by = request.user if decision == 'approved' else None + profile.approved_at = timezone.now() if decision == 'approved' else None + profile.save() + if decision == 'approved': + _ensure_alumni_designation(profile.user) + _send_placement_notifications( + actor=request.user, + recipients=[profile.user], + description='Your alumni verification request has been {}.'.format(decision), + ) + return Response(_serialize_alumni_profile(profile), status=status.HTTP_200_OK) + + +@api_view(['GET', 'POST']) +@permission_classes([IsAuthenticated]) +@authentication_classes([TokenAuthentication]) +def alumni_referrals_api(request): + if request.method == 'GET': + queryset = AlumniReferral.objects.select_related('alumni__user').all() + queryset = queryset.filter(Q(expires_at__isnull=True) | Q(expires_at__gte=_today())) + return Response([_serialize_referral(item) for item in queryset], status=status.HTTP_200_OK) + + profile = get_object_or_404(AlumniProfile, user=request.user) + if profile.status != 'approved': + return Response({'detail': 'Approved alumni access is required.'}, status=status.HTTP_403_FORBIDDEN) + referral = AlumniReferral.objects.create( + alumni=profile, + title=request.data.get('title') or '', + company=request.data.get('company') or '', + location=request.data.get('location') or '', + application_url=request.data.get('application_url') or '', + description=request.data.get('description') or '', + expires_at=_parse_date(request.data.get('expires_at')), + ) + recipients = User.objects.filter(current_designation__designation__name='student').distinct() + _send_placement_notifications( + actor=request.user, + recipients=recipients, + description='New alumni job referral posted: {} at {}.'.format(referral.title, referral.company), + ) + return Response(_serialize_referral(referral), status=status.HTTP_201_CREATED) + + +@api_view(['GET', 'POST']) +@permission_classes([IsAuthenticated]) +@authentication_classes([TokenAuthentication]) +def alumni_connections_api(request): + if request.method == 'GET': + if selectors.is_student(request.user): + student = selectors.get_student_for_user(request.user) + queryset = AlumniConnection.objects.select_related('alumni__user', 'student__id__user').filter(student=student) + else: + profile = get_object_or_404(AlumniProfile, user=request.user) + queryset = AlumniConnection.objects.select_related('alumni__user', 'student__id__user').filter(alumni=profile) + return Response([_serialize_connection(item) for item in queryset], status=status.HTTP_200_OK) + + if not selectors.is_student(request.user): + return Response({'detail': 'Only students can initiate alumni connections.'}, status=status.HTTP_403_FORBIDDEN) + student = selectors.get_student_for_user(request.user) + alumni = get_object_or_404(AlumniProfile, pk=request.data.get('alumni_id'), status='approved') + connection, created = AlumniConnection.objects.get_or_create( + alumni=alumni, + student=student, + defaults={'message': request.data.get('message') or ''}, + ) + if not created: + return Response({'detail': 'A connection request already exists.'}, status=status.HTTP_409_CONFLICT) + _send_placement_notifications( + actor=request.user, + recipients=[alumni.user], + description='{} requested to connect with you.'.format(student.id.id), + ) + return Response(_serialize_connection(connection), status=status.HTTP_201_CREATED) + + +@api_view(['PUT']) +@permission_classes([IsAuthenticated]) +@authentication_classes([TokenAuthentication]) +def alumni_connection_detail_api(request, connection_id): + connection = get_object_or_404(AlumniConnection.objects.select_related('alumni__user', 'student__id__user'), pk=connection_id) + if connection.alumni.user != request.user and not _is_tpo_user(request.user): + return Response({'detail': 'You cannot update this connection.'}, status=status.HTTP_403_FORBIDDEN) + next_status = str(request.data.get('status') or '').lower() + if next_status not in ['connected', 'rejected', 'pending']: + return Response({'status': ['Invalid connection status.']}, status=status.HTTP_400_BAD_REQUEST) + connection.status = next_status + connection.responded_by = request.user + connection.responded_at = timezone.now() + connection.save() + _send_placement_notifications( + actor=request.user, + recipients=[connection.student.id.user], + description='Your alumni connection request has been {}.'.format(next_status), + ) + return Response(_serialize_connection(connection), status=status.HTTP_200_OK) + + +@api_view(['GET', 'POST']) +@permission_classes([IsAuthenticated]) +@authentication_classes([TokenAuthentication]) +def alumni_sessions_api(request): + if request.method == 'GET': + if selectors.is_student(request.user): + student = selectors.get_student_for_user(request.user) + queryset = AlumniMentorshipSession.objects.select_related('alumni__user', 'student__id__user').filter(student=student) + else: + profile = get_object_or_404(AlumniProfile, user=request.user) + queryset = AlumniMentorshipSession.objects.select_related('alumni__user', 'student__id__user').filter(alumni=profile) + return Response([_serialize_session(item) for item in queryset], status=status.HTTP_200_OK) + + if not selectors.is_student(request.user): + return Response({'detail': 'Only students can request mentorship sessions.'}, status=status.HTTP_403_FORBIDDEN) + student = selectors.get_student_for_user(request.user) + alumni = get_object_or_404(AlumniProfile, pk=request.data.get('alumni_id'), status='approved', mentorship_enabled=True) + session = AlumniMentorshipSession.objects.create( + alumni=alumni, + student=student, + topic=request.data.get('topic') or '', + agenda=request.data.get('agenda') or '', + scheduled_at=_parse_datetime(request.data.get('scheduled_at')) or timezone.now(), + mode=request.data.get('mode') or 'online', + student_message=request.data.get('student_message') or '', + ) + _send_placement_notifications( + actor=request.user, + recipients=[alumni.user], + description='New mentorship session request from {} on {}.'.format(student.id.id, session.topic), + ) + return Response(_serialize_session(session), status=status.HTTP_201_CREATED) + + +@api_view(['PUT']) +@permission_classes([IsAuthenticated]) +@authentication_classes([TokenAuthentication]) +def alumni_session_detail_api(request, session_id): + session = get_object_or_404(AlumniMentorshipSession.objects.select_related('alumni__user', 'student__id__user'), pk=session_id) + if session.alumni.user != request.user and session.student.id.user != request.user and not _is_tpo_user(request.user): + return Response({'detail': 'You cannot update this session.'}, status=status.HTTP_403_FORBIDDEN) + if session.alumni.user == request.user or _is_tpo_user(request.user): + session.status = request.data.get('status', session.status) + session.alumni_message = request.data.get('alumni_message', session.alumni_message) + session.meeting_link = request.data.get('meeting_link', session.meeting_link) + session.mode = request.data.get('mode', session.mode) + parsed_dt = _parse_datetime(request.data.get('scheduled_at')) + if parsed_dt: + session.scheduled_at = parsed_dt + if session.student.id.user == request.user: + session.student_message = request.data.get('student_message', session.student_message) + session.save() + recipients = [session.alumni.user, session.student.id.user] + _send_placement_notifications( + actor=request.user, + recipients=recipients, + description='Mentorship session "{}" has been updated.'.format(session.topic), + ) + return Response(_serialize_session(session), status=status.HTTP_200_OK) + + +@api_view(['POST']) +@permission_classes([IsAuthenticated]) +@authentication_classes([TokenAuthentication]) +def send_notification_api(request): + send_to = request.data.get('sendTo') + recipient = request.data.get('recipient') + description = request.data.get('description') or request.data.get('type') or 'Placement Cell notification' + + if send_to == 'All': + recipients = User.objects.filter(extrainfo__user_type='student') + else: + target_user = User.objects.filter(username=recipient).first() + if target_user is None: + target_user = User.objects.filter(extrainfo__id=recipient).first() + if target_user is None: + return Response( + {'recipient': ['No user found for the supplied recipient.']}, + status=status.HTTP_404_NOT_FOUND, + ) + recipients = [target_user] + + _send_placement_notifications( + actor=request.user, + recipients=recipients, + description=description, + ) + + return Response({'message': 'Notification sent successfully.'}, status=status.HTTP_200_OK) + diff --git a/FusionIIIT/applications/placement_cell/forms.py b/FusionIIIT/applications/placement_cell/forms.py index aea81b795..54fb122ba 100644 --- a/FusionIIIT/applications/placement_cell/forms.py +++ b/FusionIIIT/applications/placement_cell/forms.py @@ -410,7 +410,7 @@ class SendInvite(forms.Form): company - name of company """ company = forms.ModelChoiceField(required=True, queryset=NotifyStudent.objects.all(), label="company") - rollno = forms.CharField(label="rollno", widget=forms.TextInput(attrs={'min': 0}), required=False) + rollno = forms.IntegerField(label="rollno", widget=forms.NumberInput(attrs={'min': 0}), required=False) programme = forms.ChoiceField(choices = Con.PROGRAMME, required=False, label="programme", widget=forms.Select(attrs={'style': "height:45px", 'onchange': "changeDeptForSend()", @@ -483,7 +483,6 @@ def clean_company_name(self): return company_name - def current_year(): return date.today().year @@ -546,87 +545,6 @@ class SearchPbiRecord(forms.Form): label="cname", required=False) - -class SendInvitation(forms.Form): - """ - The form is used to send invite to students about upcoming placement or pbi events. - @variables: - company - name of company - """ - company = forms.ModelChoiceField(required=True, queryset=NotifyStudent.objects.all(), label="company") - rollno = forms.IntegerField(label="rollno", widget=forms.NumberInput(attrs={'min': 0}), required=False) - programme = forms.ChoiceField(choices = Con.PROGRAMME, required=False, - label="programme", widget=forms.Select(attrs={'style': "height:45px", - 'onchange': "changeDeptForSend()", - 'id': "id_programme_send"})) - - dep_btech = forms.MultipleChoiceField(choices = Constants.BTECH_DEP, required=False, label="department", - widget=forms.CheckboxSelectMultiple) - dep_bdes = forms.MultipleChoiceField(choices = Constants.BDES_DEP, required=False, label="department", - widget=forms.CheckboxSelectMultiple) - dep_mtech = forms.MultipleChoiceField(choices = Constants.MTECH_DEP, required=False, label="department", - widget=forms.CheckboxSelectMultiple) - dep_mdes = forms.MultipleChoiceField(choices = Constants.MDES_DEP, required=False, label="department", - widget=forms.CheckboxSelectMultiple) - dep_phd = forms.MultipleChoiceField(choices = Constants.PHD_DEP, required=False, label="department", - widget=forms.CheckboxSelectMultiple) - cpi = forms.DecimalField(label="cpi", required=False) - no_of_days = forms.CharField(required=True, widget=forms.NumberInput(attrs={ 'min':0, - 'max':30, - 'max_length': 10, - 'class': 'form-control'})) - - -class AddPlacementSchedule(forms.Form): - """ - The form is used to placement or pbi schedule. - @variables: - time - time of placement activity - ctc - salary - company_name - name of company - placement_type - placement type (placement/pbi) - location - location of company - description - description of company - placement_date - date of placement activity - """ - time = forms.TimeField(label='time', widget=forms.widgets.TimeInput(attrs={'type': "time", - 'value':"00:00", - 'min':"0:00", - 'max':"24:00"})) - ctc = forms.DecimalField(label="ctc", widget=forms.NumberInput(attrs={ 'min':0, 'step': 0.25}) ) - company_name = forms.CharField(widget=forms.TextInput(attrs={'max_length': 100, - 'class': 'field', - 'list': 'company_dropdown1', - 'id': 'company_input'}), - label="company_name") - placement_type = forms.ChoiceField(widget=forms.Select(attrs={'style': "height:45px"}), label="placement_type", - choices=Constants.PLACEMENT_TYPE) - location = forms.CharField(widget=forms.TextInput(attrs={'max_length': 100, - 'class': 'field'}), - label="location") - description = forms.CharField(widget=forms.Textarea(attrs={'max_length': 1000, - 'class': 'form-control'}), - label="description", required=False) - attached_file = forms.FileField(required=False) - placement_date = forms.DateField(label='placement_date', widget=forms.DateInput(attrs={'class':'datepicker'})) - - def clean_ctc(self): - ctc = self.cleaned_data['ctc'] - # print('form validation \n\n\n\n', ctc) - if ctc <= 0: - raise forms.ValidationError("CTC must be positive value") - - return ctc - - def clean_company_name(self): - company_name = self.cleaned_data['company_name'] - # print('form validation \n\n\n\n', ctc) - if NotifyStudent.objects.filter(company_name=company_name): - raise forms.ValidationError("company_name must be unique") - - return company_name - - class SearchHigherRecord(forms.Form): """ The form is used to search from higher study record based on various parameters . @@ -671,7 +589,7 @@ class ManagePlacementRecord(forms.Form): stuname = forms.CharField(widget=forms.TextInput(attrs={'max_length': 100, 'class': 'field'}), label="stuname", required=False) - roll = forms.CharField(widget=forms.TextInput(attrs={ 'min':0, + roll = forms.IntegerField(widget=forms.NumberInput(attrs={ 'min':0, 'max_length': 10, 'class': 'form-control'}), label="roll", required=False) diff --git a/FusionIIIT/applications/placement_cell/migrations/0001_initial.py b/FusionIIIT/applications/placement_cell/migrations/0001_initial.py index 712c60a56..257c98609 100644 --- a/FusionIIIT/applications/placement_cell/migrations/0001_initial.py +++ b/FusionIIIT/applications/placement_cell/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 3.1.5 on 2024-07-16 15:44 +# Generated by Django 3.1.5 on 2023-03-15 18:53 import datetime from django.db import migrations, models diff --git a/FusionIIIT/applications/placement_cell/migrations/0002_frontend_api_models.py b/FusionIIIT/applications/placement_cell/migrations/0002_frontend_api_models.py new file mode 100644 index 000000000..a3aae8c4a --- /dev/null +++ b/FusionIIIT/applications/placement_cell/migrations/0002_frontend_api_models.py @@ -0,0 +1,139 @@ +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ('placement_cell', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='companydetails', + name='address', + field=models.TextField(blank=True, default='', max_length=1000), + ), + migrations.AddField( + model_name='companydetails', + name='description', + field=models.TextField(blank=True, default='', max_length=1000), + ), + migrations.AddField( + model_name='companydetails', + name='logo', + field=models.ImageField(blank=True, null=True, upload_to='documents/placement/company_logos'), + ), + migrations.AddField( + model_name='companydetails', + name='website', + field=models.CharField(blank=True, default='', max_length=255), + ), + migrations.CreateModel( + name='PlacementField', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=100, unique=True)), + ('type', models.CharField(choices=[('text', 'Text'), ('number', 'Number'), ('decimal', 'Decimal'), ('date', 'Date'), ('time', 'Time')], default='text', max_length=20)), + ('required', models.BooleanField(default=False)), + ], + ), + migrations.AddField( + model_name='placementschedule', + name='branch', + field=models.CharField(blank=True, default='', max_length=100), + ), + migrations.AddField( + model_name='placementschedule', + name='company', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='placement_cell.CompanyDetails'), + ), + migrations.AddField( + model_name='placementschedule', + name='cpi', + field=models.CharField(blank=True, default='', max_length=20), + ), + migrations.AddField( + model_name='placementschedule', + name='eligibility', + field=models.TextField(blank=True, default='', max_length=1000), + ), + migrations.AddField( + model_name='placementschedule', + name='end_date', + field=models.DateField(blank=True, null=True, verbose_name='Date'), + ), + migrations.AddField( + model_name='placementschedule', + name='end_datetime', + field=models.DateTimeField(blank=True, null=True), + ), + migrations.AddField( + model_name='placementschedule', + name='gender', + field=models.CharField(blank=True, default='', max_length=20), + ), + migrations.AddField( + model_name='placementschedule', + name='passoutyr', + field=models.CharField(blank=True, default='', max_length=20), + ), + migrations.AddField( + model_name='placementschedule', + name='fields', + field=models.ManyToManyField(blank=True, to='placement_cell.PlacementField'), + ), + migrations.AddField( + model_name='studentplacement', + name='debar_reason', + field=models.TextField(blank=True, default='', max_length=1000), + ), + migrations.CreateModel( + name='PlacementRestriction', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('criteria', models.CharField(max_length=50)), + ('condition', models.CharField(max_length=50)), + ('value', models.CharField(max_length=255)), + ('description', models.TextField(blank=True, default='', max_length=1000)), + ], + ), + migrations.CreateModel( + name='PlacementRound', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('round_no', models.IntegerField(default=0)), + ('test_date', models.DateField(blank=True, null=True)), + ('description', models.TextField(blank=True, default='', max_length=1000)), + ('test_type', models.CharField(blank=True, default='', max_length=100)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('schedule', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='placement_cell.PlacementSchedule')), + ], + options={ + 'ordering': ('round_no', 'created_at'), + }, + ), + migrations.CreateModel( + name='PlacementApplication', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('status', models.CharField(choices=[('pending', 'Pending'), ('accept', 'Accept'), ('reject', 'Reject')], default='pending', max_length=20)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('schedule', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='placement_cell.PlacementSchedule')), + ('student', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='academic_information.Student')), + ], + options={ + 'unique_together': {('schedule', 'student')}, + }, + ), + migrations.CreateModel( + name='PlacementApplicationResponse', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('value', models.TextField(blank=True, default='', max_length=5000)), + ('application', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='placement_cell.PlacementApplication')), + ('field', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='placement_cell.PlacementField')), + ], + ), + ] diff --git a/FusionIIIT/applications/placement_cell/migrations/0003_assignment7_requirement_fixes.py b/FusionIIIT/applications/placement_cell/migrations/0003_assignment7_requirement_fixes.py new file mode 100644 index 000000000..7551ab48b --- /dev/null +++ b/FusionIIIT/applications/placement_cell/migrations/0003_assignment7_requirement_fixes.py @@ -0,0 +1,28 @@ +from django.db import migrations, models +import django.core.validators + + +class Migration(migrations.Migration): + + dependencies = [ + ('placement_cell', '0002_frontend_api_models'), + ] + + operations = [ + migrations.AlterField( + model_name='education', + name='grade', + field=models.CharField(default='', max_length=3), + ), + migrations.AlterField( + model_name='has', + name='skill_rating', + field=models.IntegerField( + default=80, + validators=[ + django.core.validators.MinValueValidator(0), + django.core.validators.MaxValueValidator(100), + ], + ), + ), + ] diff --git a/FusionIIIT/applications/placement_cell/migrations/0004_higher_studies_chairman_visit_dates.py b/FusionIIIT/applications/placement_cell/migrations/0004_higher_studies_chairman_visit_dates.py new file mode 100644 index 000000000..0d3d6f000 --- /dev/null +++ b/FusionIIIT/applications/placement_cell/migrations/0004_higher_studies_chairman_visit_dates.py @@ -0,0 +1,23 @@ +import datetime + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('placement_cell', '0003_assignment7_requirement_fixes'), + ] + + operations = [ + migrations.AddField( + model_name='chairmanvisit', + name='start_date', + field=models.DateField(default=datetime.date.today, verbose_name='Start Date'), + ), + migrations.AddField( + model_name='chairmanvisit', + name='end_date', + field=models.DateField(blank=True, null=True, verbose_name='End Date'), + ), + ] diff --git a/FusionIIIT/applications/placement_cell/migrations/0005_auto_20260418_0906.py b/FusionIIIT/applications/placement_cell/migrations/0005_auto_20260418_0906.py new file mode 100644 index 000000000..6fe05cd3b --- /dev/null +++ b/FusionIIIT/applications/placement_cell/migrations/0005_auto_20260418_0906.py @@ -0,0 +1,65 @@ +# Generated by Django 3.1.5 on 2026-04-18 09:06 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('academic_information', '0001_initial'), + ('placement_cell', '0004_higher_studies_chairman_visit_dates'), + ] + + operations = [ + migrations.AddField( + model_name='placementapplication', + name='withdrawn_at', + field=models.DateTimeField(blank=True, null=True), + ), + migrations.AlterField( + model_name='placementapplication', + name='status', + field=models.CharField(choices=[('pending', 'Pending'), ('accept', 'Accept'), ('reject', 'Reject'), ('withdrawn', 'Withdrawn')], default='pending', max_length=20), + ), + migrations.CreateModel( + name='PlacementProfileDocument', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(default='Supporting Document', max_length=100)), + ('document', models.FileField(upload_to='documents/placement/profile_documents')), + ('uploaded_at', models.DateTimeField(auto_now_add=True)), + ('student', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='academic_information.student')), + ], + options={ + 'ordering': ('-uploaded_at', '-id'), + }, + ), + migrations.CreateModel( + name='PlacementProfileAuditLog', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('action', models.CharField(max_length=100)), + ('details', models.JSONField(blank=True, default=dict)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('actor', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)), + ('student', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='academic_information.student')), + ], + options={ + 'ordering': ('-created_at', '-id'), + }, + ), + migrations.CreateModel( + name='PlacementNotificationPreference', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('enable_portal', models.BooleanField(default=True)), + ('enable_email', models.BooleanField(default=True)), + ('enable_sms', models.BooleanField(default=False)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('student', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='academic_information.student')), + ], + ), + ] diff --git a/FusionIIIT/applications/placement_cell/migrations/0006_alumni_features.py b/FusionIIIT/applications/placement_cell/migrations/0006_alumni_features.py new file mode 100644 index 000000000..67034b272 --- /dev/null +++ b/FusionIIIT/applications/placement_cell/migrations/0006_alumni_features.py @@ -0,0 +1,97 @@ +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('academic_information', '0001_initial'), + ('placement_cell', '0005_auto_20260418_0906'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='AlumniProfile', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('graduation_year', models.IntegerField()), + ('degree', models.CharField(blank=True, default='', max_length=100)), + ('current_company', models.CharField(blank=True, default='', max_length=150)), + ('current_designation', models.CharField(blank=True, default='', max_length=150)), + ('linkedin_url', models.URLField(blank=True, default='')), + ('verification_document', models.FileField(blank=True, null=True, upload_to='documents/placement/alumni_verification')), + ('verification_notes', models.TextField(blank=True, default='', max_length=1000)), + ('status', models.CharField(choices=[('pending', 'Pending'), ('approved', 'Approved'), ('rejected', 'Rejected')], default='pending', max_length=20)), + ('topics', models.TextField(blank=True, default='', max_length=1000)), + ('availability', models.CharField(blank=True, default='', max_length=200)), + ('bio', models.TextField(blank=True, default='', max_length=1500)), + ('mentorship_enabled', models.BooleanField(default=False)), + ('approved_at', models.DateTimeField(blank=True, null=True)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('approved_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='approved_alumni_profiles', to=settings.AUTH_USER_MODEL)), + ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + options={ + 'ordering': ('-updated_at', '-id'), + }, + ), + migrations.CreateModel( + name='AlumniReferral', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=150)), + ('company', models.CharField(max_length=150)), + ('location', models.CharField(blank=True, default='', max_length=150)), + ('application_url', models.URLField(blank=True, default='')), + ('description', models.TextField(max_length=2000)), + ('expires_at', models.DateField(blank=True, null=True)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('alumni', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='referrals', to='placement_cell.alumniprofile')), + ], + options={ + 'ordering': ('-created_at', '-id'), + }, + ), + migrations.CreateModel( + name='AlumniMentorshipSession', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('topic', models.CharField(max_length=150)), + ('agenda', models.TextField(blank=True, default='', max_length=1500)), + ('scheduled_at', models.DateTimeField()), + ('mode', models.CharField(blank=True, default='online', max_length=50)), + ('meeting_link', models.CharField(blank=True, default='', max_length=300)), + ('student_message', models.TextField(blank=True, default='', max_length=1500)), + ('alumni_message', models.TextField(blank=True, default='', max_length=1500)), + ('status', models.CharField(choices=[('requested', 'Requested'), ('scheduled', 'Scheduled'), ('completed', 'Completed'), ('cancelled', 'Cancelled')], default='requested', max_length=20)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('alumni', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='sessions', to='placement_cell.alumniprofile')), + ('student', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='alumni_sessions', to='academic_information.student')), + ], + options={ + 'ordering': ('scheduled_at', '-id'), + }, + ), + migrations.CreateModel( + name='AlumniConnection', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('status', models.CharField(choices=[('pending', 'Pending'), ('connected', 'Connected'), ('rejected', 'Rejected')], default='pending', max_length=20)), + ('message', models.TextField(blank=True, default='', max_length=1000)), + ('responded_at', models.DateTimeField(blank=True, null=True)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('alumni', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='connections', to='placement_cell.alumniprofile')), + ('responded_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='alumni_connection_responses', to=settings.AUTH_USER_MODEL)), + ('student', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='alumni_connections', to='academic_information.student')), + ], + options={ + 'ordering': ('-created_at', '-id'), + 'unique_together': {('alumni', 'student')}, + }, + ), + ] diff --git a/FusionIIIT/applications/placement_cell/migrations/0007_reporting_features.py b/FusionIIIT/applications/placement_cell/migrations/0007_reporting_features.py new file mode 100644 index 000000000..f4f292e97 --- /dev/null +++ b/FusionIIIT/applications/placement_cell/migrations/0007_reporting_features.py @@ -0,0 +1,33 @@ +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ("placement_cell", "0006_alumni_features"), + ] + + operations = [ + migrations.CreateModel( + name="PlacementReportSchedule", + fields=[ + ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ("name", models.CharField(max_length=120)), + ("report_type", models.CharField(choices=[("batch", "Batch Summary"), ("company", "Company Summary"), ("branch", "Branch Summary"), ("custom", "Custom Report")], default="custom", max_length=20)), + ("frequency", models.CharField(choices=[("daily", "Daily"), ("weekly", "Weekly"), ("monthly", "Monthly")], default="weekly", max_length=20)), + ("export_format", models.CharField(choices=[("excel", "Excel"), ("pdf", "PDF")], default="excel", max_length=20)), + ("filters", models.JSONField(blank=True, default=dict)), + ("recipients", models.TextField(blank=True, default="", max_length=500)), + ("is_active", models.BooleanField(default=True)), + ("last_run_at", models.DateTimeField(blank=True, null=True)), + ("created_at", models.DateTimeField(auto_now_add=True)), + ("updated_at", models.DateTimeField(auto_now=True)), + ("created_by", models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)), + ], + options={ + "ordering": ("-updated_at", "-id"), + }, + ), + ] diff --git a/FusionIIIT/applications/placement_cell/migrations/0008_round_datetime_conflicts.py b/FusionIIIT/applications/placement_cell/migrations/0008_round_datetime_conflicts.py new file mode 100644 index 000000000..d2ba78473 --- /dev/null +++ b/FusionIIIT/applications/placement_cell/migrations/0008_round_datetime_conflicts.py @@ -0,0 +1,31 @@ +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("placement_cell", "0007_reporting_features"), + ] + + operations = [ + migrations.AddField( + model_name="placementround", + name="end_datetime", + field=models.DateTimeField(blank=True, null=True), + ), + migrations.AddField( + model_name="placementround", + name="location_link", + field=models.CharField(blank=True, default="", max_length=255), + ), + migrations.AddField( + model_name="placementround", + name="mode", + field=models.CharField(blank=True, default="", max_length=30), + ), + migrations.AddField( + model_name="placementround", + name="start_datetime", + field=models.DateTimeField(blank=True, null=True), + ), + ] diff --git a/FusionIIIT/applications/placement_cell/migrations/0009_application_detail_features.py b/FusionIIIT/applications/placement_cell/migrations/0009_application_detail_features.py new file mode 100644 index 000000000..182ac6289 --- /dev/null +++ b/FusionIIIT/applications/placement_cell/migrations/0009_application_detail_features.py @@ -0,0 +1,75 @@ +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ("placement_cell", "0008_round_datetime_conflicts"), + ] + + operations = [ + migrations.AddField( + model_name="placementapplication", + name="remarks", + field=models.TextField(blank=True, default="", max_length=1000), + ), + migrations.AddField( + model_name="placementapplication", + name="updated_at", + field=models.DateTimeField(auto_now=True, null=True), + preserve_default=False, + ), + migrations.AlterField( + model_name="placementapplication", + name="status", + field=models.CharField( + choices=[ + ("pending", "Pending"), + ("shortlisted", "Shortlisted"), + ("interview_scheduled", "Interview Scheduled"), + ("interview_completed", "Interview Completed"), + ("offer_released", "Offer Released"), + ("accept", "Accept"), + ("reject", "Reject"), + ("withdrawn", "Withdrawn"), + ], + default="pending", + max_length=20, + ), + ), + migrations.CreateModel( + name="PlacementApplicationTimeline", + fields=[ + ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ("stage", models.CharField(default="", max_length=100)), + ("remarks", models.TextField(blank=True, default="", max_length=1000)), + ("created_at", models.DateTimeField(auto_now_add=True)), + ("actor", models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)), + ("application", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name="timeline_entries", to="placement_cell.PlacementApplication")), + ], + options={"ordering": ("created_at", "id")}, + ), + migrations.CreateModel( + name="PlacementInterviewSchedule", + fields=[ + ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ("round_no", models.IntegerField(default=1)), + ("title", models.CharField(blank=True, default="", max_length=100)), + ("scheduled_at", models.DateTimeField()), + ("end_datetime", models.DateTimeField(blank=True, null=True)), + ("mode", models.CharField(blank=True, default="", max_length=30)), + ("location", models.CharField(blank=True, default="", max_length=255)), + ("meeting_link", models.CharField(blank=True, default="", max_length=255)), + ("remarks", models.TextField(blank=True, default="", max_length=1000)), + ("outcome", models.CharField(choices=[("pending", "Pending"), ("passed", "Passed"), ("failed", "Failed"), ("selected", "Selected")], default="pending", max_length=20)), + ("is_active", models.BooleanField(default=True)), + ("created_at", models.DateTimeField(auto_now_add=True)), + ("updated_at", models.DateTimeField(auto_now=True)), + ("application", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name="interview_schedules", to="placement_cell.PlacementApplication")), + ("created_by", models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)), + ], + options={"ordering": ("-scheduled_at", "-id")}, + ), + ] diff --git a/FusionIIIT/applications/placement_cell/migrations/0010_placement_appeal.py b/FusionIIIT/applications/placement_cell/migrations/0010_placement_appeal.py new file mode 100644 index 000000000..136d334fb --- /dev/null +++ b/FusionIIIT/applications/placement_cell/migrations/0010_placement_appeal.py @@ -0,0 +1,28 @@ +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ("placement_cell", "0009_application_detail_features"), + ] + + operations = [ + migrations.CreateModel( + name="PlacementAppeal", + fields=[ + ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ("reason", models.TextField(max_length=2000)), + ("status", models.CharField(choices=[("pending", "Pending"), ("reviewed", "Reviewed"), ("accepted", "Accepted"), ("rejected", "Rejected")], default="pending", max_length=20)), + ("response", models.TextField(blank=True, default="", max_length=2000)), + ("created_at", models.DateTimeField(auto_now_add=True)), + ("reviewed_at", models.DateTimeField(blank=True, null=True)), + ("placement_status", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="placement_cell.PlacementStatus")), + ("student", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="academic_information.Student")), + ], + options={ + "unique_together": {("student", "placement_status")}, + }, + ), + ] diff --git a/FusionIIIT/applications/placement_cell/migrations/0011_performance_indexes.py b/FusionIIIT/applications/placement_cell/migrations/0011_performance_indexes.py new file mode 100644 index 000000000..cc2c2d8ee --- /dev/null +++ b/FusionIIIT/applications/placement_cell/migrations/0011_performance_indexes.py @@ -0,0 +1,83 @@ +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("placement_cell", "0010_placement_appeal"), + ] + + operations = [ + migrations.AddIndex( + model_name="notifystudent", + index=models.Index(fields=["placement_type"], name="placement_c_placeme_1c6ff8_idx"), + ), + migrations.AddIndex( + model_name="notifystudent", + index=models.Index(fields=["company_name"], name="placement_c_company_0204a2_idx"), + ), + migrations.AddIndex( + model_name="placementstatus", + index=models.Index(fields=["unique_id", "invitation"], name="placement_c_unique__068844_idx"), + ), + migrations.AddIndex( + model_name="placementstatus", + index=models.Index(fields=["unique_id", "timestamp"], name="placement_c_unique__5767b0_idx"), + ), + migrations.AddIndex( + model_name="studentrecord", + index=models.Index(fields=["unique_id", "record_id"], name="placement_c_unique__964ebc_idx"), + ), + migrations.AddIndex( + model_name="placementschedule", + index=models.Index(fields=["placement_date"], name="placement_c_placeme_190366_idx"), + ), + migrations.AddIndex( + model_name="placementschedule", + index=models.Index(fields=["schedule_at"], name="placement_c_schedul_f76a20_idx"), + ), + migrations.AddIndex( + model_name="placementschedule", + index=models.Index(fields=["notify_id", "placement_date"], name="placement_c_notify__957faa_idx"), + ), + migrations.AddIndex( + model_name="placementapplication", + index=models.Index(fields=["student", "created_at"], name="placement_c_student_5ef23d_idx"), + ), + migrations.AddIndex( + model_name="placementapplication", + index=models.Index(fields=["schedule", "created_at"], name="placement_c_schedul_6db3a0_idx"), + ), + migrations.AddIndex( + model_name="placementapplication", + index=models.Index(fields=["student", "status"], name="placement_c_student_10ef00_idx"), + ), + migrations.AddIndex( + model_name="placementapplication", + index=models.Index(fields=["schedule", "status"], name="placement_c_schedul_93bec9_idx"), + ), + migrations.AddIndex( + model_name="placementround", + index=models.Index(fields=["schedule", "round_no"], name="placement_c_schedul_6c9b4d_idx"), + ), + migrations.AddIndex( + model_name="placementround", + index=models.Index(fields=["schedule", "start_datetime"], name="placement_c_schedul_3be9db_idx"), + ), + migrations.AddIndex( + model_name="placementapplicationtimeline", + index=models.Index(fields=["application", "created_at"], name="placement_c_applica_6646ff_idx"), + ), + migrations.AddIndex( + model_name="placementinterviewschedule", + index=models.Index(fields=["application", "scheduled_at"], name="placement_c_applica_45d617_idx"), + ), + migrations.AddIndex( + model_name="placementinterviewschedule", + index=models.Index(fields=["application", "round_no"], name="placement_c_applica_209fbb_idx"), + ), + migrations.AddIndex( + model_name="placementprofiledocument", + index=models.Index(fields=["student", "uploaded_at"], name="placement_c_student_27f93a_idx"), + ), + ] diff --git a/FusionIIIT/applications/placement_cell/migrations/0012_placementpolicy.py b/FusionIIIT/applications/placement_cell/migrations/0012_placementpolicy.py new file mode 100644 index 000000000..50028ecba --- /dev/null +++ b/FusionIIIT/applications/placement_cell/migrations/0012_placementpolicy.py @@ -0,0 +1,27 @@ +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('placement_cell', '0011_performance_indexes'), + ] + + operations = [ + migrations.CreateModel( + name='PlacementPolicy', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=150)), + ('description', models.TextField(max_length=3000)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)), + ], + options={ + 'ordering': ('-updated_at', '-id'), + }, + ), + ] diff --git a/FusionIIIT/applications/placement_cell/models.py b/FusionIIIT/applications/placement_cell/models.py index 53f8d8ea4..09b685c4d 100644 --- a/FusionIIIT/applications/placement_cell/models.py +++ b/FusionIIIT/applications/placement_cell/models.py @@ -1,13 +1,29 @@ -import datetime -from django.db import models -from django.utils import timezone -from django.utils.translation import gettext as _ - -from applications.academic_information.models import Student +# imports +import datetime + +from django.conf import settings +from django.core.exceptions import ValidationError +from django.core.validators import MaxValueValidator, MinValueValidator +from django.contrib.auth import get_user_model +from django.db import models +from django.utils import timezone +from django.utils.translation import gettext as _ + +from applications.academic_information.models import Student + +User = get_user_model() # Class definations: +def validate_start_before_end(*, start_date, end_date, start_field='sdate', end_field='edate'): + if start_date and end_date and start_date >= end_date: + raise ValidationError({ + start_field: _('Start date must be earlier than end date.'), + end_field: _('End date must be later than start date.'), + }) + + class Constants: RESUME_TYPE = ( ('ONGOING', 'Ongoing'), @@ -54,7 +70,6 @@ class Constants: ('CSE', 'CSE'), ('ME','ME'), ('ECE','ECE'), - ('SM','SM'), ) BDES_DEP = ( @@ -92,6 +107,10 @@ class Project(models.Model): sdate = models.DateField(_("Date"), default=datetime.date.today) edate = models.DateField(null=True, blank=True) + def clean(self): + super().clean() + validate_start_before_end(start_date=self.sdate, end_date=self.edate) + def __str__(self): return '{} - {}'.format(self.unique_id.id, self.project_name) @@ -106,7 +125,10 @@ def __str__(self): class Has(models.Model): skill_id = models.ForeignKey(Skill, on_delete=models.CASCADE) unique_id = models.ForeignKey(Student, on_delete=models.CASCADE) - skill_rating = models.IntegerField(default=80) + skill_rating = models.IntegerField( + default=80, + validators=[MinValueValidator(0), MaxValueValidator(100)], + ) class Meta: unique_together = (('skill_id', 'unique_id'),) @@ -118,36 +140,15 @@ def __str__(self): class Education(models.Model): unique_id = models.ForeignKey(Student, on_delete=models.CASCADE) degree = models.CharField(max_length=40, default='') - grade = models.CharField(max_length=10, default='') + grade = models.CharField(max_length=3, default='') institute = models.TextField(max_length=250, default='') stream = models.CharField(max_length=150, default='', null=True, blank=True) sdate = models.DateField(_("Date"), default=datetime.date.today) edate = models.DateField(null=True, blank=True) def clean(self): - - sdate = self.cleaned_data.get("startdate") - stime = self.cleaned_data.get("starttime") - print(sdate, "sdate") - today = datetime.datetime.now() - datetime.timedelta(1) - print(today, "today") - k1 = stime.hour - k2 = stime.minute - k3 = stime.second - x = time(k1, k2, k3) - date = datetime.datetime.combine(sdate, x) - edate = self.cleaned_data.get("enddate") - etime = self.cleaned_data.get("endtime") - k1 = etime.hour - k2 = etime.minute - k3 = etime.second - end_date = datetime.datetime.combine(edate, datetime.time(k1, k2, k3)) - print(date, end_date) - if(date < today): - raise forms.ValidationError("Invalid quiz Start Date") - elif(date > end_date): - raise forms.ValidationError("Start Date but me before End Date") - return self.cleaned_data + super().clean() + validate_start_before_end(start_date=self.sdate, end_date=self.edate) class Experience(models.Model): @@ -161,6 +162,10 @@ class Experience(models.Model): sdate = models.DateField(_("Date"), default=datetime.date.today) edate = models.DateField(null=True, blank=True) + def clean(self): + super().clean() + validate_start_before_end(start_date=self.sdate, end_date=self.edate) + def __str__(self): return '{} - {}'.format(self.unique_id.id, self.company) @@ -173,6 +178,10 @@ class Course(models.Model): sdate = models.DateField(_("Date"), default=datetime.date.today) edate = models.DateField(null=True, blank=True) + def clean(self): + super().clean() + validate_start_before_end(start_date=self.sdate, end_date=self.edate) + def __str__(self): return '{} - {}'.format(self.unique_id.id, self.course_name) @@ -278,7 +287,7 @@ def __str__(self): return self.message -class NotifyStudent(models.Model): +class NotifyStudent(models.Model): placement_type = models.CharField(max_length=20, choices=Constants.PLACEMENT_TYPE, default='PLACEMENT') company_name = models.CharField(max_length=100, default='') @@ -286,8 +295,14 @@ class NotifyStudent(models.Model): description = models.TextField(max_length=1000, default='', null=True, blank=True) timestamp = models.DateTimeField(auto_now=True) - def __str__(self): - return '{} - {}'.format(self.company_name, self.placement_type) + def __str__(self): + return '{} - {}'.format(self.company_name, self.placement_type) + + class Meta: + indexes = [ + models.Index(fields=['placement_type']), + models.Index(fields=['company_name']), + ] @property def get_placement_schedule_object(self): @@ -300,25 +315,54 @@ class Role(models.Model): def __str__(self): return self.role -class CompanyDetails(models.Model): +class CompanyDetails(models.Model): company_name = models.CharField(max_length=100, blank=True, null=True) + description = models.TextField(max_length=1000, default='', blank=True) + address = models.TextField(max_length=1000, default='', blank=True) + website = models.CharField(max_length=255, default='', blank=True) + logo = models.ImageField( + upload_to='documents/placement/company_logos', + null=True, + blank=True, + ) + + def __str__(self): + return self.company_name + + +class PlacementField(models.Model): + FIELD_TYPES = ( + ('text', 'Text'), + ('number', 'Number'), + ('decimal', 'Decimal'), + ('date', 'Date'), + ('time', 'Time'), + ) + + name = models.CharField(max_length=100, unique=True) + type = models.CharField(max_length=20, choices=FIELD_TYPES, default='text') + required = models.BooleanField(default=False) def __str__(self): - return self.company_name + return self.name -class PlacementStatus(models.Model): +class PlacementStatus(models.Model): notify_id = models.ForeignKey(NotifyStudent, on_delete=models.CASCADE) unique_id = models.ForeignKey(Student, on_delete=models.CASCADE) - invitation = models.CharField(max_length=20, choices=Constants.INVITATION_TYPE, - default='PENDING') - placed = models.CharField(max_length=20, choices=Constants.PLACED_TYPE, - default='NOT PLACED') - timestamp = models.DateTimeField(auto_now=True) - no_of_days = models.IntegerField(default=10, null=True, blank=True) - - class Meta: - unique_together = (('notify_id', 'unique_id'),) + invitation = models.CharField(max_length=20, choices=Constants.INVITATION_TYPE, + default='PENDING') + placed = models.CharField(max_length=20, choices=Constants.PLACED_TYPE, + default='NOT PLACED') + timestamp = models.DateTimeField(auto_now=True) + no_of_days = models.IntegerField(default=10, null=True, blank=True) + + class Meta: + unique_together = (('notify_id', 'unique_id'),) + indexes = [ + models.Index(fields=['unique_id', 'invitation']), + models.Index(fields=['unique_id', 'timestamp']), + ] @property def response_date(self): @@ -341,12 +385,15 @@ def __str__(self): return '{} - {}'.format(self.name, self.year) -class StudentRecord(models.Model): +class StudentRecord(models.Model): record_id = models.ForeignKey(PlacementRecord, on_delete=models.CASCADE) unique_id = models.ForeignKey(Student, on_delete=models.CASCADE) - class Meta: - unique_together = (('record_id', 'unique_id'),) + class Meta: + unique_together = (('record_id', 'unique_id'),) + indexes = [ + models.Index(fields=['unique_id', 'record_id']), + ] def __str__(self): return '{} - {}'.format(self.unique_id.id, self.record_id.name) @@ -356,26 +403,59 @@ class ChairmanVisit(models.Model): company_name = models.CharField(max_length=100, default='') location = models.CharField(max_length=100, default='') visiting_date = models.DateField(_("Date"), default=datetime.date.today) + start_date = models.DateField(_("Start Date"), default=datetime.date.today) + end_date = models.DateField(_("End Date"), null=True, blank=True) description = models.TextField(max_length=1000, default='', null=True, blank=True) timestamp = models.DateTimeField(auto_now=True) + def clean(self): + super().clean() + validate_start_before_end( + start_date=self.start_date, + end_date=self.end_date, + start_field='start_date', + end_field='end_date', + ) + + def save(self, *args, **kwargs): + if self.start_date and not self.visiting_date: + self.visiting_date = self.start_date + self.full_clean() + super().save(*args, **kwargs) + def __str__(self): return self.company_name -class PlacementSchedule(models.Model): +class PlacementSchedule(models.Model): notify_id = models.ForeignKey(NotifyStudent, on_delete=models.CASCADE) title = models.CharField(max_length=100, default='') placement_date = models.DateField(_("Date"), default=datetime.date.today) + end_date = models.DateField(_("Date"), null=True, blank=True) location = models.CharField(max_length=100, default='') description = models.TextField(max_length=500, default='', null=True, blank=True) + eligibility = models.TextField(max_length=1000, default='', blank=True) + passoutyr = models.CharField(max_length=20, default='', blank=True) + gender = models.CharField(max_length=20, default='', blank=True) + cpi = models.CharField(max_length=20, default='', blank=True) + branch = models.CharField(max_length=100, default='', blank=True) time = models.TimeField() role = models.ForeignKey(Role, on_delete=models.CASCADE, null=True, blank=True) - attached_file = models.FileField(upload_to='documents/placement/schedule', null=True, blank=True) - schedule_at = models.DateTimeField(auto_now_add=False, auto_now=False, default=timezone.now, blank=True, null=True) - - def __str__(self): - return '{} - {}'.format(self.notify_id.company_name, self.placement_date) + attached_file = models.FileField(upload_to='documents/placement/schedule', null=True, blank=True) + schedule_at = models.DateTimeField(auto_now_add=False, auto_now=False, default=timezone.now, blank=True, null=True) + end_datetime = models.DateTimeField(blank=True, null=True) + company = models.ForeignKey(CompanyDetails, on_delete=models.SET_NULL, null=True, blank=True) + fields = models.ManyToManyField(PlacementField, blank=True) + + def __str__(self): + return '{} - {}'.format(self.notify_id.company_name, self.placement_date) + + class Meta: + indexes = [ + models.Index(fields=['placement_date']), + models.Index(fields=['schedule_at']), + models.Index(fields=['notify_id', 'placement_date']), + ] @property def get_role(self): @@ -388,6 +468,7 @@ def get_role(self): class StudentPlacement(models.Model): unique_id = models.OneToOneField(Student, primary_key=True, on_delete=models.CASCADE) debar = models.CharField(max_length=20, choices=Constants.DEBAR_TYPE, default='NOT DEBAR') + debar_reason = models.TextField(max_length=1000, default='', blank=True) future_aspect = models.CharField(max_length=20, choices=Constants.PLACEMENT_TYPE, default='PLACEMENT') placed_type = models.CharField(max_length=20, choices=Constants.PLACED_TYPE, @@ -399,3 +480,362 @@ class StudentPlacement(models.Model): def __str__(self): return self.unique_id.id.id + + +class PlacementApplication(models.Model): + STATUS_CHOICES = ( + ('pending', 'Pending'), + ('shortlisted', 'Shortlisted'), + ('interview_scheduled', 'Interview Scheduled'), + ('interview_completed', 'Interview Completed'), + ('offer_released', 'Offer Released'), + ('accept', 'Accept'), + ('reject', 'Reject'), + ('withdrawn', 'Withdrawn'), + ) + + schedule = models.ForeignKey(PlacementSchedule, on_delete=models.CASCADE) + student = models.ForeignKey(Student, on_delete=models.CASCADE) + status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='pending') + remarks = models.TextField(max_length=1000, default='', blank=True) + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) + withdrawn_at = models.DateTimeField(null=True, blank=True) + + class Meta: + unique_together = (('schedule', 'student'),) + indexes = [ + models.Index(fields=['student', 'created_at']), + models.Index(fields=['schedule', 'created_at']), + models.Index(fields=['student', 'status']), + models.Index(fields=['schedule', 'status']), + ] + + def __str__(self): + return '{} - {}'.format(self.student.id.id, self.schedule.id) + + +class PlacementApplicationResponse(models.Model): + application = models.ForeignKey(PlacementApplication, on_delete=models.CASCADE) + field = models.ForeignKey(PlacementField, on_delete=models.CASCADE, null=True, blank=True) + value = models.TextField(max_length=5000, default='', blank=True) + + def __str__(self): + return '{} - {}'.format(self.application.id, self.field_id) + + +class PlacementRound(models.Model): + schedule = models.ForeignKey(PlacementSchedule, on_delete=models.CASCADE) + round_no = models.IntegerField(default=0) + test_date = models.DateField(null=True, blank=True) + start_datetime = models.DateTimeField(null=True, blank=True) + end_datetime = models.DateTimeField(null=True, blank=True) + mode = models.CharField(max_length=30, default='', blank=True) + location_link = models.CharField(max_length=255, default='', blank=True) + description = models.TextField(max_length=1000, default='', blank=True) + test_type = models.CharField(max_length=100, default='', blank=True) + created_at = models.DateTimeField(auto_now_add=True) + + class Meta: + ordering = ('round_no', 'created_at') + indexes = [ + models.Index(fields=['schedule', 'round_no']), + models.Index(fields=['schedule', 'start_datetime']), + ] + + def __str__(self): + return '{} - {}'.format(self.schedule.id, self.round_no) + + +class PlacementApplicationTimeline(models.Model): + application = models.ForeignKey(PlacementApplication, on_delete=models.CASCADE, related_name='timeline_entries') + stage = models.CharField(max_length=100, default='') + remarks = models.TextField(max_length=1000, default='', blank=True) + actor = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=True) + created_at = models.DateTimeField(auto_now_add=True) + + class Meta: + ordering = ('created_at', 'id') + indexes = [ + models.Index(fields=['application', 'created_at']), + ] + + def __str__(self): + return '{} - {}'.format(self.application.id, self.stage) + + +class PlacementInterviewSchedule(models.Model): + OUTCOME_CHOICES = ( + ('pending', 'Pending'), + ('passed', 'Passed'), + ('failed', 'Failed'), + ('selected', 'Selected'), + ) + + application = models.ForeignKey(PlacementApplication, on_delete=models.CASCADE, related_name='interview_schedules') + round_no = models.IntegerField(default=1) + title = models.CharField(max_length=100, default='', blank=True) + scheduled_at = models.DateTimeField() + end_datetime = models.DateTimeField(null=True, blank=True) + mode = models.CharField(max_length=30, default='', blank=True) + location = models.CharField(max_length=255, default='', blank=True) + meeting_link = models.CharField(max_length=255, default='', blank=True) + remarks = models.TextField(max_length=1000, default='', blank=True) + outcome = models.CharField(max_length=20, choices=OUTCOME_CHOICES, default='pending') + is_active = models.BooleanField(default=True) + created_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=True) + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) + + class Meta: + ordering = ('-scheduled_at', '-id') + indexes = [ + models.Index(fields=['application', 'scheduled_at']), + models.Index(fields=['application', 'round_no']), + ] + + def __str__(self): + return '{} - {}'.format(self.application.id, self.title or self.round_no) + + +class PlacementRestriction(models.Model): + criteria = models.CharField(max_length=50) + condition = models.CharField(max_length=50) + value = models.CharField(max_length=255) + description = models.TextField(max_length=1000, default='', blank=True) + + def __str__(self): + return '{} - {}'.format(self.criteria, self.condition) + + +class PlacementPolicy(models.Model): + title = models.CharField(max_length=150) + description = models.TextField(max_length=3000) + created_by = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True) + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) + + class Meta: + ordering = ('-updated_at', '-id') + + def __str__(self): + return self.title + + +class PlacementProfileDocument(models.Model): + student = models.ForeignKey(Student, on_delete=models.CASCADE) + name = models.CharField(max_length=100, default='Supporting Document') + document = models.FileField(upload_to='documents/placement/profile_documents') + uploaded_at = models.DateTimeField(auto_now_add=True) + + class Meta: + ordering = ('-uploaded_at', '-id') + indexes = [ + models.Index(fields=['student', 'uploaded_at']), + ] + + def __str__(self): + return '{} - {}'.format(self.student.id.id, self.name) + + +class PlacementProfileAuditLog(models.Model): + student = models.ForeignKey(Student, on_delete=models.CASCADE) + actor = models.ForeignKey('auth.User', on_delete=models.SET_NULL, null=True, blank=True) + action = models.CharField(max_length=100) + details = models.JSONField(default=dict, blank=True) + created_at = models.DateTimeField(auto_now_add=True) + + class Meta: + ordering = ('-created_at', '-id') + + def __str__(self): + return '{} - {}'.format(self.student.id.id, self.action) + + +class PlacementNotificationPreference(models.Model): + student = models.OneToOneField(Student, on_delete=models.CASCADE) + enable_portal = models.BooleanField(default=True) + enable_email = models.BooleanField(default=True) + enable_sms = models.BooleanField(default=False) + updated_at = models.DateTimeField(auto_now=True) + + def __str__(self): + return '{}'.format(self.student.id.id) + + +class PlacementAppeal(models.Model): + STATUS_CHOICES = ( + ('pending', 'Pending'), + ('reviewed', 'Reviewed'), + ('accepted', 'Accepted'), + ('rejected', 'Rejected'), + ) + + student = models.ForeignKey(Student, on_delete=models.CASCADE) + placement_status = models.ForeignKey(PlacementStatus, on_delete=models.CASCADE) + reason = models.TextField(max_length=2000) + status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='pending') + response = models.TextField(max_length=2000, blank=True, default='') + created_at = models.DateTimeField(auto_now_add=True) + reviewed_at = models.DateTimeField(null=True, blank=True) + + class Meta: + unique_together = (('student', 'placement_status'),) + + def __str__(self): + return f"Appeal by {self.student.id} for {self.placement_status.id}" + + +class PlacementReportSchedule(models.Model): + FREQUENCY_CHOICES = ( + ('daily', 'Daily'), + ('weekly', 'Weekly'), + ('monthly', 'Monthly'), + ) + + REPORT_TYPE_CHOICES = ( + ('batch', 'Batch Summary'), + ('company', 'Company Summary'), + ('branch', 'Branch Summary'), + ('custom', 'Custom Report'), + ) + + FORMAT_CHOICES = ( + ('excel', 'Excel'), + ('pdf', 'PDF'), + ) + + name = models.CharField(max_length=120) + report_type = models.CharField(max_length=20, choices=REPORT_TYPE_CHOICES, default='custom') + frequency = models.CharField(max_length=20, choices=FREQUENCY_CHOICES, default='weekly') + export_format = models.CharField(max_length=20, choices=FORMAT_CHOICES, default='excel') + filters = models.JSONField(default=dict, blank=True) + recipients = models.TextField(max_length=500, default='', blank=True) + is_active = models.BooleanField(default=True) + created_by = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True) + last_run_at = models.DateTimeField(null=True, blank=True) + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) + + class Meta: + ordering = ('-updated_at', '-id') + + def __str__(self): + return self.name + + +class AlumniProfile(models.Model): + STATUS_CHOICES = ( + ('pending', 'Pending'), + ('approved', 'Approved'), + ('rejected', 'Rejected'), + ) + + user = models.OneToOneField(User, on_delete=models.CASCADE) + graduation_year = models.IntegerField() + degree = models.CharField(max_length=100, default='', blank=True) + current_company = models.CharField(max_length=150, default='', blank=True) + current_designation = models.CharField(max_length=150, default='', blank=True) + linkedin_url = models.URLField(blank=True, default='') + verification_document = models.FileField( + upload_to='documents/placement/alumni_verification', + null=True, + blank=True, + ) + verification_notes = models.TextField(max_length=1000, default='', blank=True) + status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='pending') + topics = models.TextField(max_length=1000, default='', blank=True) + availability = models.CharField(max_length=200, default='', blank=True) + bio = models.TextField(max_length=1500, default='', blank=True) + mentorship_enabled = models.BooleanField(default=False) + approved_by = models.ForeignKey( + User, + on_delete=models.SET_NULL, + null=True, + blank=True, + related_name='approved_alumni_profiles', + ) + approved_at = models.DateTimeField(null=True, blank=True) + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) + + class Meta: + ordering = ('-updated_at', '-id') + + def __str__(self): + return '{} - {}'.format(self.user.username, self.status) + + +class AlumniMentorshipSession(models.Model): + STATUS_CHOICES = ( + ('requested', 'Requested'), + ('scheduled', 'Scheduled'), + ('completed', 'Completed'), + ('cancelled', 'Cancelled'), + ) + + alumni = models.ForeignKey(AlumniProfile, on_delete=models.CASCADE, related_name='sessions') + student = models.ForeignKey(Student, on_delete=models.CASCADE, related_name='alumni_sessions') + topic = models.CharField(max_length=150) + agenda = models.TextField(max_length=1500, default='', blank=True) + scheduled_at = models.DateTimeField() + mode = models.CharField(max_length=50, default='online', blank=True) + meeting_link = models.CharField(max_length=300, default='', blank=True) + student_message = models.TextField(max_length=1500, default='', blank=True) + alumni_message = models.TextField(max_length=1500, default='', blank=True) + status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='requested') + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) + + class Meta: + ordering = ('scheduled_at', '-id') + + def __str__(self): + return '{} - {}'.format(self.alumni.user.username, self.student.id.id) + + +class AlumniReferral(models.Model): + alumni = models.ForeignKey(AlumniProfile, on_delete=models.CASCADE, related_name='referrals') + title = models.CharField(max_length=150) + company = models.CharField(max_length=150) + location = models.CharField(max_length=150, default='', blank=True) + application_url = models.URLField(blank=True, default='') + description = models.TextField(max_length=2000) + expires_at = models.DateField(null=True, blank=True) + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) + + class Meta: + ordering = ('-created_at', '-id') + + def __str__(self): + return '{} - {}'.format(self.company, self.title) + + +class AlumniConnection(models.Model): + STATUS_CHOICES = ( + ('pending', 'Pending'), + ('connected', 'Connected'), + ('rejected', 'Rejected'), + ) + + alumni = models.ForeignKey(AlumniProfile, on_delete=models.CASCADE, related_name='connections') + student = models.ForeignKey(Student, on_delete=models.CASCADE, related_name='alumni_connections') + status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='pending') + message = models.TextField(max_length=1000, default='', blank=True) + responded_by = models.ForeignKey( + User, + on_delete=models.SET_NULL, + null=True, + blank=True, + related_name='alumni_connection_responses', + ) + responded_at = models.DateTimeField(null=True, blank=True) + created_at = models.DateTimeField(auto_now_add=True) + + class Meta: + unique_together = (('alumni', 'student'),) + ordering = ('-created_at', '-id') + + def __str__(self): + return '{} - {}'.format(self.alumni.user.username, self.student.id.id) diff --git a/FusionIIIT/applications/placement_cell/selectors.py b/FusionIIIT/applications/placement_cell/selectors.py new file mode 100644 index 000000000..ff89cb118 --- /dev/null +++ b/FusionIIIT/applications/placement_cell/selectors.py @@ -0,0 +1,134 @@ +from datetime import date + +from django.contrib.auth.models import User +from django.core.serializers import serialize +from django.db.models import Q + +from applications.academic_information.models import Student +from applications.globals.models import ExtraInfo, HoldsDesignation + +from .models import ( + AlumniProfile, + ChairmanVisit, + CompanyDetails, + NotifyStudent, + PlacementRecord, + PlacementSchedule, + PlacementStatus, + Reference, + Role, +) + + +def get_profile_for_user(user): + return ExtraInfo.objects.get(user=user) + + +def get_student_for_user(user): + profile = get_profile_for_user(user) + return Student.objects.get(id=profile.id) + + +def get_designation_queryset(user, designation_name): + return HoldsDesignation.objects.filter( + Q(working=user, designation__name=designation_name) + ) + + +def get_reference_list_json(student): + reference_objects = Reference.objects.select_related("unique_id").filter( + unique_id=student + ) + return serialize("json", list(reference_objects)) + + +def get_company_names_by_prefix(current_value): + return list( + CompanyDetails.objects.filter( + Q(company_name__startswith=current_value) + ).values_list("company_name", flat=True) + ) + + +def get_role_names_by_prefix(current_value): + return list( + Role.objects.filter(Q(role__startswith=current_value)).values_list( + "role", flat=True + ) + ) + + +def get_upcoming_schedule_notify_ids(): + return PlacementSchedule.objects.select_related("notify_id").filter( + Q(placement_date__gte=date.today()) + ).values_list("notify_id", flat=True) + + +def get_student_upcoming_placement_status(student): + return PlacementStatus.objects.select_related("unique_id", "notify_id").filter( + Q(unique_id=student, notify_id__in=get_upcoming_schedule_notify_ids()) + ).order_by("-timestamp") + + +def get_all_schedules(): + return PlacementSchedule.objects.select_related("notify_id").all() + + +def get_schedule_by_pk(schedule_id): + return PlacementSchedule.objects.select_related("notify_id").get(pk=schedule_id) + + +def get_placement_status_by_pk(status_id): + return PlacementStatus.objects.select_related("unique_id", "notify_id").get( + pk=status_id + ) + + +def get_or_create_company_detail(company_name): + company_detail = CompanyDetails.objects.filter(company_name=company_name).first() + if company_detail is None: + company_detail = CompanyDetails.objects.create(company_name=company_name) + return company_detail + + +def get_or_create_role(role_name): + role = Role.objects.filter(role=role_name).first() + if role is None: + role = Role.objects.create(role=role_name) + return role + + +def get_all_notify_students(): + return NotifyStudent.objects.all() + + +def get_all_roles(): + return Role.objects.all() + + +def get_all_chairman_visits(): + return ChairmanVisit.objects.all() + + +def delete_placement_record_by_id(record_id): + return PlacementRecord.objects.filter(id=record_id).delete() + + +def get_user_by_username(username): + return User.objects.get(username=username) + + +def get_alumni_profile_for_user(user): + return AlumniProfile.objects.filter(user=user).first() + + +def is_student(user): + return get_designation_queryset(user, "student").exists() + + +def is_alumni(user): + return get_designation_queryset(user, "alumni").exists() + + +def is_tpo(user): + return get_designation_queryset(user, "placement officer").exists() or get_designation_queryset(user, "placement chairman").exists() diff --git a/FusionIIIT/applications/placement_cell/services.py b/FusionIIIT/applications/placement_cell/services.py new file mode 100644 index 000000000..b05c12b1a --- /dev/null +++ b/FusionIIIT/applications/placement_cell/services.py @@ -0,0 +1,121 @@ +from django.core.exceptions import ValidationError +from django.utils import timezone + +from . import selectors +from .models import ( + ChairmanVisit, + NotifyStudent, + PlacementRecord, + PlacementSchedule, + PlacementStatus, +) + + +def _today(): + return timezone.now().date() + + +def update_invitation_status(status_id, invitation): + return ( + PlacementStatus.objects.select_related("unique_id", "notify_id") + .filter(pk=status_id, invitation="PENDING") + .update(invitation=invitation, timestamp=timezone.now()) + ) + + +def delete_invitation_status(status_id): + selectors.get_placement_status_by_pk(status_id).delete() + + +def delete_schedule(schedule_id): + placement_schedule = selectors.get_schedule_by_pk(schedule_id) + NotifyStudent.objects.get(pk=placement_schedule.notify_id.id).delete() + placement_schedule.delete() + + +def create_schedule_and_notification( + *, + placement_type, + company_name, + ctc, + description, + placement_date, + location, + time, + role_name, + attached_file=None, + timestamp=None, +): + company = selectors.get_or_create_company_detail(company_name) + role = selectors.get_or_create_role(role_name) + placement_date_value = placement_date + if hasattr(placement_date_value, "date"): + placement_date_value = placement_date_value.date() + if placement_date_value and placement_date_value < _today(): + raise ValidationError("Placement date cannot be in the past.") + + notify = NotifyStudent.objects.create( + placement_type=placement_type, + company_name=company_name, + description=description, + ctc=ctc, + timestamp=timestamp or timezone.now(), + ) + schedule = PlacementSchedule.objects.create( + notify_id=notify, + title=company_name, + description=description, + placement_date=placement_date, + attached_file=attached_file, + role=role, + location=location, + time=time, + company=company, + ) + notify.save() + schedule.save() + return notify, schedule + + +def create_placement_record( + *, + placement_type, + student_name, + ctc, + year, + test_type, + test_score, +): + record = PlacementRecord.objects.create( + placement_type=placement_type, + name=student_name, + ctc=ctc, + year=year, + test_type=test_type, + test_score=test_score, + ) + record.save() + return record + + +def create_chairman_visit( + *, + company_name, + location, + visiting_date, + description, + timestamp, + start_date=None, + end_date=None, +): + record = ChairmanVisit.objects.create( + company_name=company_name, + location=location, + visiting_date=visiting_date, + start_date=start_date or visiting_date, + end_date=end_date, + description=description, + timestamp=timestamp, + ) + record.save() + return record diff --git a/FusionIIIT/applications/placement_cell/tests.py b/FusionIIIT/applications/placement_cell/tests.py deleted file mode 100644 index e9137c85e..000000000 --- a/FusionIIIT/applications/placement_cell/tests.py +++ /dev/null @@ -1,3 +0,0 @@ -# from django.test import TestCase - -# Create your tests here. diff --git a/FusionIIIT/applications/placement_cell/tests/__init__.py b/FusionIIIT/applications/placement_cell/tests/__init__.py new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/FusionIIIT/applications/placement_cell/tests/__init__.py @@ -0,0 +1 @@ + diff --git a/FusionIIIT/applications/placement_cell/tests/conftest.py b/FusionIIIT/applications/placement_cell/tests/conftest.py new file mode 100644 index 000000000..4cee5c703 --- /dev/null +++ b/FusionIIIT/applications/placement_cell/tests/conftest.py @@ -0,0 +1,215 @@ +import datetime +from decimal import Decimal +from pathlib import Path + +import yaml +from django.contrib.auth.models import User +from django.core.files.uploadedfile import SimpleUploadedFile +from django.test import TestCase +from django.utils import timezone +from rest_framework.test import APIClient + +from applications.academic_information.models import Student +from applications.globals.models import DepartmentInfo, Designation, ExtraInfo, HoldsDesignation +from applications.placement_cell.models import ( + Education, + Has, + NotifyStudent, + PlacementProfileDocument, + PlacementSchedule, + Project, + Skill, +) + + +class PlacementCellSpecBase(TestCase): + specs_dir = Path(__file__).resolve().parent / "specs" + + @classmethod + def load_spec(cls, filename, item_key, item_id): + with (cls.specs_dir / filename).open("r", encoding="utf-8") as spec_file: + payload = yaml.safe_load(spec_file) or {} + for item in payload.get(item_key, []): + if item.get("id") == item_id: + return item + raise AssertionError("Specification {}:{} not found".format(filename, item_id)) + + def setUp(self): + self.department_cse = DepartmentInfo.objects.create(name="CSE") + self.department_ece = DepartmentInfo.objects.create(name="ECE") + self.student_designation = Designation.objects.create(name="student", full_name="Student") + self.officer = User.objects.create_user( + username="officer", + password="password", + email="officer@example.com", + ) + self.student_user = self._create_student_user( + roll_no="2023001", + username="student1", + department=self.department_cse, + ) + self.other_student_user = self._create_student_user( + roll_no="2023002", + username="student2", + department=self.department_ece, + ) + + def api_get(self, path, *, user=None, data=None): + client = APIClient() + if user is not None: + client.force_authenticate(user=user) + return client.get(path, data=data or {}, format="json") + + def api_post(self, path, *, user=None, data=None, format="json"): + client = APIClient() + if user is not None: + client.force_authenticate(user=user) + return client.post(path, data=data or {}, format=format) + + def api_put(self, path, *, user=None, data=None, format="json"): + client = APIClient() + if user is not None: + client.force_authenticate(user=user) + return client.put(path, data=data or {}, format=format) + + def api_delete(self, path, *, user=None, data=None): + client = APIClient() + if user is not None: + client.force_authenticate(user=user) + return client.delete(path, data=data or {}, format="json") + + def _create_student_user(self, *, roll_no, username, department): + user = User.objects.create_user( + username=username, + password="password", + email="{}@example.com".format(username), + first_name=username, + ) + extra = ExtraInfo.objects.get(user=user) + extra.user_type = "student" + extra.department = department + extra.save(update_fields=["user_type", "department"]) + Student.objects.create( + id=extra, + programme="B.Tech", + batch=2026, + cpi=8.5, + category="GEN", + ) + HoldsDesignation.objects.create( + user=user, + working=user, + designation=self.student_designation, + ) + return user + + def _create_officer_designation(self, name="placement officer"): + designation, _ = Designation.objects.get_or_create(name=name, defaults={"full_name": name.title()}) + HoldsDesignation.objects.get_or_create( + user=self.officer, + working=self.officer, + designation=designation, + ) + return designation + + def _create_officer_designation_for_user(self, user, name="placement officer"): + designation, _ = Designation.objects.get_or_create(name=name, defaults={"full_name": name.title()}) + extra, _ = ExtraInfo.objects.get_or_create( + user=user, + defaults={ + "id": user.username, + "user_type": "staff", + "department": self.department_cse, + }, + ) + extra.user_type = "staff" + extra.department = self.department_cse + extra.save(update_fields=["user_type", "department"]) + HoldsDesignation.objects.get_or_create( + user=user, + working=user, + designation=designation, + ) + return designation + + def _get_student(self, user): + return Student.objects.get(id__user=user) + + def _create_schedule( + self, + *, + company_name, + placement_type="PLACEMENT", + placement_date=None, + role=None, + location="Campus", + ctc="10.00", + ): + notify = NotifyStudent.objects.create( + placement_type=placement_type, + company_name=company_name, + ctc=Decimal(ctc), + description="Campus drive", + ) + return PlacementSchedule.objects.create( + notify_id=notify, + title=company_name, + placement_date=placement_date or (timezone.now().date() + datetime.timedelta(days=2)), + location=location, + description="Campus drive", + time=datetime.time(10, 0), + schedule_at=timezone.now(), + role=role, + ) + + def _make_profile_complete(self, user): + student = self._get_student(user) + response = self.api_put( + "/placement/api/profile/", + user=user, + data={ + "first_name": "Student", + "last_name": "One", + "email": "{}@example.com".format(user.username), + "phone_no": "9876543210", + "address": "Hostel A", + "about_me": "Ready for placements", + }, + format="multipart", + ) + self.assertEqual(response.status_code, 200) + Education.objects.get_or_create( + unique_id=student, + degree="B.Tech", + grade="90", + institute="IIITDMJ", + sdate=datetime.date(2020, 1, 1), + edate=datetime.date(2021, 1, 1), + ) + skill, _ = Skill.objects.get_or_create(skill="Python") + Has.objects.get_or_create( + skill_id=skill, + unique_id=student, + defaults={"skill_rating": 80}, + ) + Project.objects.get_or_create( + unique_id=student, + project_name="Capstone", + defaults={ + "project_status": "COMPLETED", + "sdate": datetime.date(2020, 1, 1), + "edate": datetime.date(2020, 2, 1), + }, + ) + PlacementProfileDocument.objects.get_or_create( + student=student, + name="Resume", + defaults={ + "document": SimpleUploadedFile( + "resume.pdf", + b"pdf-content", + content_type="application/pdf", + ) + }, + ) + return student diff --git a/FusionIIIT/applications/placement_cell/tests/reports/.gitkeep b/FusionIIIT/applications/placement_cell/tests/reports/.gitkeep new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/FusionIIIT/applications/placement_cell/tests/reports/.gitkeep @@ -0,0 +1 @@ + diff --git a/FusionIIIT/applications/placement_cell/tests/reports/Placement_Cell_Test_Report_Submission.rtf b/FusionIIIT/applications/placement_cell/tests/reports/Placement_Cell_Test_Report_Submission.rtf new file mode 100644 index 000000000..75b297dfd --- /dev/null +++ b/FusionIIIT/applications/placement_cell/tests/reports/Placement_Cell_Test_Report_Submission.rtf @@ -0,0 +1,130 @@ +{\rtf1\ansi\deff0 +{\fonttbl{\f0 Calibri;}{\f1 Cambria;}} +\paperw12240\paperh15840\margl1440\margr1440\margt1440\margb1440 +\fs24 +\pard\qc\b\f1\fs32 Placement Cell Module\par +\pard\qc\b\f1\fs28 Submission Report\par +\pard\sa160\sb160\b0\f0\fs24 +Course Artifact: Testing and Quality Evaluation Report\par +Module: Placement Cell\par +Execution Date: April 22, 2026\par +Project: Fusion\par +Test Basis: Authored specifications in placement\_cell/tests/specs, automated tests in applications/placement\_cell/tests, and workbook evidence prepared for the module.\par + +\pard\sa200\sb120\b\f1\fs28 1. Introduction\par +\pard\sa120\sb120\b0\f0\fs24 +This report presents the testing outcome for the Placement Cell module of the Fusion system. The purpose of the report is to summarize test adequacy, document the outcome of automated test execution, identify the most important failures and defects, and provide a final evaluation of the module from a deployment-readiness perspective.\par +The report is based on the Placement Cell specification assets already prepared for the assignment:\par +\pard\li360\sa80\sb80\f0\fs24 \'95 22 documented use cases\par +\'95 40 documented business rules\par +\'95 10 documented workflows\par +\'95 166 workbook test design entries\par +\pard\sa120\sb120\b0\f0\fs24 +An automated execution of the Placement Cell test suite was also performed to validate the implemented behavior against the documented expectations.\par + +\pard\sa200\sb120\b\f1\fs28 2. Test Adequacy Summary\par +\pard\sa120\sb120\b0\f0\fs24 +The module shows good overall test adequacy in terms of specification coverage. The available tests cover the major functional areas expected in a Placement Cell platform.\par +\pard\li360\sa80\sb80\f0\fs24 \'95 profile creation and profile update validation\par +\'95 document upload validation\par +\'95 job browsing and job application behavior\par +\'95 duplicate-application prevention\par +\'95 offer visibility and tracking\par +\'95 interview scheduling workflows\par +\'95 alumni registration and alumni engagement features\par +\'95 statistics and reporting APIs\par +\'95 rule-based and workflow-based traceability from YAML specifications\par +\pard\sa120\sb120\b0\f0\fs24 +This level of coverage is a positive indicator because it demonstrates that the module has been tested not only through direct API checks, but also against structured use cases, business rules, and end-to-end workflows.\par +However, adequacy in design does not fully translate into adequacy in execution. Several critical failures were found in business-sensitive features such as schedule creation, company-management flow, interview scheduling, placement finalization, and resume generation.\par +\b Adequate in planned test coverage, but only partially adequate in executed behavior.\b0\par + +\pard\sa200\sb120\b\f1\fs28 3. Automated Test Execution Report\par +\pard\sa120\sb120\b0\f0\fs24 +The Placement Cell automated suite was executed using the Django test command:\par +\pard\li360\sa80\sb80\f0\fs24 manage.py test applications.placement\_cell.tests --verbosity 2\par +\pard\sa120\sb120\b0\f0\fs24 +The test environment initialized successfully, migrations completed successfully, and the execution finished normally. The failures observed were application-level failures.\par +\b Execution Summary\b0\par +\pard\li360\sa80\sb80\f0\fs24 \'95 Total executed: 161\par +\'95 Passed: 138\par +\'95 Failed: 19\par +\'95 Errors: 4\par +\'95 Duration: about 9.3s\par +\pard\sa120\sb120\b0\f0\fs24 +The pass count shows that a large part of the module is already functional. At the same time, the 19 failures and 4 errors are too significant to ignore because they affect core placement workflows and management actions.\par +\b Completed with failures\b0\par + +\pard\sa200\sb120\b\f1\fs28 4. Key Failures Found\par +\pard\sa120\sb120\b\f0\fs24 4.1 Authorization failures in TPO management flows\par +\pard\sa120\sb120\b0\f0\fs24 +Multiple tests related to job posting, schedule creation, and company-management behavior failed because the API returned 403 Forbidden before functional validation could proceed. This affected business-rule tests and workflow tests that expected 200, 201, or 400 responses.\par +\pard\sa120\sb120\b\f0\fs24 4.2 Student placement listing contract mismatch\par +\pard\sa120\sb120\b0\f0\fs24 +The student-facing placement listing returned more schedule records than the test expected. This indicates a mismatch between implementation behavior and the intended API contract.\par +\pard\sa120\sb120\b\f0\fs24 4.3 Application-limit workflow inconsistency\par +\pard\sa120\sb120\b0\f0\fs24 +The active-application limit tests failed because the returned status codes did not match the expected outcomes. This suggests either incorrect validation order or a contract mismatch between implementation and tests.\par +\pard\sa120\sb120\b\f0\fs24 4.4 Interview scheduling failure\par +\pard\sa120\sb120\b0\f0\fs24 +The interview scheduling API returned 400 instead of successfully creating an interview entry, affecting a key recruitment transition.\par +\pard\sa120\sb120\b\f0\fs24 4.5 Profile editing payload mismatch\par +\pard\sa120\sb120\b0\f0\fs24 +The profile payload used for editing did not contain the expected skills structure in one of the tests. This may lead to frontend issues during profile editing.\par + +\pard\sa200\sb120\b\f1\fs28 5. Major Defects Identified\par +\pard\sa120\sb120\b\f0\fs24 Defect 1: Placement finalization crashes during reporting update\par +\pard\sa120\sb120\b0\f0\fs24 +Observed issue: NameError because StudentRecord is not defined.\par +Impact: Finalized application status cannot be safely converted into placement reporting or statistics records.\par +Severity: Critical\par +\pard\sa120\sb120\b\f0\fs24 Defect 2: Resume generation fails for non-numeric identifiers\par +\pard\sa120\sb120\b0\f0\fs24 +Observed issue: ValueError caused by numeric parsing assumptions.\par +Impact: Resume generation fails for certain identifier formats.\par +Severity: Critical\par +\pard\sa120\sb120\b\f0\fs24 Defect 3: Notification contract mismatch\par +\pard\sa120\sb120\b0\f0\fs24 +Observed issue: Notification verification fails because the expected metadata shape does not match the actual Notification model fields.\par +Impact: Notification validation is unreliable.\par +Severity: Major\par +\pard\sa120\sb120\b\f0\fs24 Defect 4: Profile payload missing editable skills data\par +\pard\sa120\sb120\b0\f0\fs24 +Observed issue: KeyError for skills.\par +Impact: Frontend edit workflows may fail or render incomplete data.\par +Severity: Major\par +\pard\sa120\sb120\b\f0\fs24 Defect 5: TPO authorization or role-selection behavior is not aligned with expected management access\par +\pard\sa120\sb120\b0\f0\fs24 +Observed issue: Multiple creation and workflow tests fail with 403 Forbidden.\par +Impact: Job posting, company-management, and workflow progression are blocked.\par +Severity: Critical\par + +\pard\sa200\sb120\b\f1\fs28 6. Changes Required Before Deployment\par +\pard\sa120\sb120\b\f0\fs24 6.1 Fix authorization for management endpoints\par +\pard\sa120\sb120\b0\f0\fs24 +The role and authorization checks for TPO and related placement-management users must be reviewed carefully. Deployment should proceed only after TPO users can create schedules, manage company records, and complete expected workflow actions successfully.\par +\pard\sa120\sb120\b\f0\fs24 6.2 Correct placement finalization and statistics integration\par +\pard\sa120\sb120\b0\f0\fs24 +The logic that creates or updates placement reporting records must be fixed so that selected applications can be finalized without runtime errors.\par +\pard\sa120\sb120\b\f0\fs24 6.3 Refactor CV generation to remove brittle identifier logic\par +\pard\sa120\sb120\b0\f0\fs24 +Resume generation should not depend on string slicing assumptions for profile identifiers. Roll-year or academic details should be taken from stable student data.\par +\pard\sa120\sb120\b\f0\fs24 6.4 Align API contracts with test and frontend expectations\par +\pard\sa120\sb120\b0\f0\fs24 +The response payloads for profile editing, placement listing, and notifications should be made consistent across backend implementation, automated tests, frontend usage, and documented specifications.\par +\pard\sa120\sb120\b\f0\fs24 6.5 Revalidate workflow-critical features\par +\pard\sa120\sb120\b0\f0\fs24 +The following flows should be re-tested after fixes: schedule creation, company registration, interview scheduling, application-limit enforcement, placement finalization, and resume generation.\par +\pard\sa120\sb120\b\f0\fs24 6.6 Strengthen deployment gate checks\par +\pard\sa120\sb120\b0\f0\fs24 +Before release, all critical and major defects should be resolved, the Placement Cell automated suite should pass for core workflows, and workbook evidence should be synchronized with automated evidence.\par + +\pard\sa200\sb120\b\f1\fs28 7. Final Module Evaluation\par +\pard\sa120\sb120\b0\f0\fs24 +The Placement Cell module has strong structural coverage and a substantial amount of implemented functionality. Many important features are already working correctly. However, the current version of the module cannot be recommended for deployment because the failed and errored tests affect core operational features.\par +\b Module status: Partially acceptable for submission, not acceptable for deployment in the current state.\b0\par +\pard\sa120\sb120\b0\f0\fs24 +The module is suitable for academic submission because test documentation exists, automated execution evidence exists, major risks have been identified clearly, and the testing process is traceable to specifications.\par +However, the module is not deployment-ready because critical defects still remain in TPO schedule and company-management authorization, placement finalization and reporting, interview scheduling, resume generation, and response contract consistency.\par +The recommended next step is to fix the deployment-blocking defects, run the full automated Placement Cell suite again, update the workbook where required, and only then consider the module ready for release.\par +} diff --git a/FusionIIIT/applications/placement_cell/tests/runner.py b/FusionIIIT/applications/placement_cell/tests/runner.py new file mode 100644 index 000000000..b7350ee27 --- /dev/null +++ b/FusionIIIT/applications/placement_cell/tests/runner.py @@ -0,0 +1,130 @@ +import csv +from pathlib import Path + +import yaml + + +BASE_DIR = Path(__file__).resolve().parent +SPECS_DIR = BASE_DIR / "specs" +REPORTS_DIR = BASE_DIR / "reports" + + +def _load_yaml(filename): + path = SPECS_DIR / filename + if not path.exists(): + return {} + with path.open("r", encoding="utf-8") as handle: + return yaml.safe_load(handle) or {} + + +def _write_csv(path, headers, rows): + REPORTS_DIR.mkdir(parents=True, exist_ok=True) + with path.open("w", newline="", encoding="utf-8") as handle: + writer = csv.DictWriter(handle, fieldnames=headers) + writer.writeheader() + for row in rows: + writer.writerow({header: row.get(header, "") for header in headers}) + + +def _collect_design_rows(items, item_id_key, scenario_keys): + rows = [] + for item in items: + item_id = item.get("id", "") + item_name = item.get("name", "") + endpoint = item.get("endpoint", "") + method = item.get("method", "") + description = item.get("description", "") + scenarios = item.get("scenarios", {}) + for scenario_key in scenario_keys: + scenario = scenarios.get(scenario_key, {}) + if not scenario: + continue + rows.append( + { + "test_id": scenario.get("_test_id", ""), + item_id_key: scenario.get("_{}_id".format(item_id_key.split("_")[0]), item_id), + "name": item_name, + "scenario": scenario.get("_scenario", ""), + "expected_result": scenario.get("_expected_result", ""), + "endpoint": endpoint, + "method": method, + "description": description, + } + ) + return rows + + +def generate_reports(): + use_cases = _load_yaml("use_cases.yaml").get("use_cases", []) + business_rules = _load_yaml("business_rules.yaml").get("business_rules", []) + workflows = _load_yaml("workflows.yaml").get("workflows", []) + + uc_rows = _collect_design_rows( + use_cases, + "uc_id", + ["happy_path", "alternate_path", "exception_path"], + ) + br_rows = _collect_design_rows( + business_rules, + "br_id", + ["valid", "invalid"], + ) + wf_rows = _collect_design_rows( + workflows, + "wf_id", + ["end_to_end", "negative"], + ) + + summary_rows = [ + {"artifact": "Use Cases", "count": len(uc_rows)}, + {"artifact": "Business Rules", "count": len(br_rows)}, + {"artifact": "Workflows", "count": len(wf_rows)}, + {"artifact": "Total Tests Designed", "count": len(uc_rows) + len(br_rows) + len(wf_rows)}, + ] + + _write_csv( + REPORTS_DIR / "Module_Test_Summary.csv", + ["artifact", "count"], + summary_rows, + ) + _write_csv( + REPORTS_DIR / "UC_Test_Design.csv", + ["test_id", "uc_id", "name", "scenario", "expected_result", "endpoint", "method", "description"], + uc_rows, + ) + _write_csv( + REPORTS_DIR / "BR_Test_Design.csv", + ["test_id", "br_id", "name", "scenario", "expected_result", "endpoint", "method", "description"], + br_rows, + ) + _write_csv( + REPORTS_DIR / "WF_Test_Design.csv", + ["test_id", "wf_id", "name", "scenario", "expected_result", "endpoint", "method", "description"], + wf_rows, + ) + _write_csv( + REPORTS_DIR / "Test_Execution_Log.csv", + ["test_id", "status", "executed_at", "notes"], + [], + ) + _write_csv( + REPORTS_DIR / "Defect_Log.csv", + ["defect_id", "test_id", "severity", "summary", "status"], + [], + ) + _write_csv( + REPORTS_DIR / "Artifact_Evaluation.csv", + ["artifact", "status", "remarks"], + [ + {"artifact": "use_cases.yaml", "status": "present", "remarks": ""}, + {"artifact": "business_rules.yaml", "status": "present", "remarks": ""}, + {"artifact": "workflows.yaml", "status": "present", "remarks": ""}, + {"artifact": "test_use_cases.py", "status": "present", "remarks": ""}, + {"artifact": "test_business_rules.py", "status": "present", "remarks": ""}, + {"artifact": "test_workflows.py", "status": "present", "remarks": ""}, + ], + ) + + +if __name__ == "__main__": + generate_reports() diff --git a/FusionIIIT/applications/placement_cell/tests/specs/business_rules.yaml b/FusionIIIT/applications/placement_cell/tests/specs/business_rules.yaml new file mode 100644 index 000000000..4452c2e60 --- /dev/null +++ b/FusionIIIT/applications/placement_cell/tests/specs/business_rules.yaml @@ -0,0 +1,720 @@ +business_rules: + - id: BR01 + source_id: BR-JM-SCHEDULE-DATE + name: Placement Date Must Not Be In The Past + endpoint: /placement/api/placement/ + method: POST + description: Placement schedules must be created only for current or future dates. + scenarios: + valid: + _test_id: BR01_V_01 + _br_id: BR01 + _scenario: Valid + _expected_result: Future-dated placement schedule is created successfully. + invalid: + _test_id: BR01_I_01 + _br_id: BR01 + _scenario: Invalid + _expected_result: Past-dated placement schedule is rejected. + + - id: BR02 + source_id: BR-SP-001 + name: Placement Profile Requires Mandatory Fields + endpoint: /placement/api/profile/ + method: PUT + description: Student profile must include valid mandatory contact and identity details. + scenarios: + valid: + _test_id: BR02_V_01 + _br_id: BR02 + _scenario: Valid + _expected_result: Valid profile payload is accepted. + invalid: + _test_id: BR02_I_01 + _br_id: BR02 + _scenario: Invalid + _expected_result: Invalid or blank mandatory fields are rejected. + + - id: BR03 + source_id: BR-JM-006 + name: Duplicate Applications Are Not Allowed + endpoint: /placement/api/apply-for-placement/ + method: POST + description: A student cannot submit more than one active application for the same job. + scenarios: + valid: + _test_id: BR03_V_01 + _br_id: BR03 + _scenario: Valid + _expected_result: First application submission succeeds. + invalid: + _test_id: BR03_I_01 + _br_id: BR03 + _scenario: Invalid + _expected_result: Second application submission for the same job is rejected. + + - id: BR04 + source_id: BR-SP-004 + name: Profile Update Frequency + endpoint: /placement/api/profile/ + method: PUT + description: Profile updates follow governance rules for restricted or controlled update windows. + scenarios: + valid: + _test_id: BR04_V_01 + _br_id: BR04 + _scenario: Valid + _expected_result: Profile update within allowed conditions is accepted. + invalid: + _test_id: BR04_I_01 + _br_id: BR04 + _scenario: Invalid + _expected_result: Restricted profile modification attempt is rejected. + + - id: BR05 + source_id: BR-SP-005 + name: Skills and Certifications + endpoint: /placement/api/profile/ + method: GET, PUT + description: Skills and certification data must be represented in a verifiable and usable manner. + scenarios: + valid: + _test_id: BR05_V_01 + _br_id: BR05 + _scenario: Valid + _expected_result: Verified skill and certification details are retained correctly. + invalid: + _test_id: BR05_I_01 + _br_id: BR05 + _scenario: Invalid + _expected_result: Missing or invalid proof leaves the record rejected or flagged. + + - id: BR06 + source_id: BR-JM-001 + name: Job Posting Approval + endpoint: /placement/api/placement/ + method: POST, PUT + description: Job postings must be reviewed and accepted according to placement management rules. + scenarios: + valid: + _test_id: BR06_V_01 + _br_id: BR06 + _scenario: Valid + _expected_result: Approved posting is created and can proceed in the workflow. + invalid: + _test_id: BR06_I_01 + _br_id: BR06 + _scenario: Invalid + _expected_result: Posting that fails approval constraints is rejected or not published. + + - id: BR07 + source_id: BR-JM-002 + name: Eligibility Criteria Enforcement + endpoint: /placement/api/apply-for-placement/ + method: POST + description: Students may apply only when they satisfy the job eligibility criteria. + scenarios: + valid: + _test_id: BR07_V_01 + _br_id: BR07 + _scenario: Valid + _expected_result: Eligible student can submit an application successfully. + invalid: + _test_id: BR07_I_01 + _br_id: BR07 + _scenario: Invalid + _expected_result: Ineligible student receives a rejection with reasons. + + - id: BR08 + source_id: BR-JM-003 + name: Application Deadline Management + endpoint: /placement/api/apply-for-placement/ + method: POST + description: Job applications are allowed only while the relevant deadline window is open. + scenarios: + valid: + _test_id: BR08_V_01 + _br_id: BR08 + _scenario: Valid + _expected_result: Application submitted before the deadline is accepted. + invalid: + _test_id: BR08_I_01 + _br_id: BR08 + _scenario: Invalid + _expected_result: Application submitted after the deadline is rejected. + + - id: BR09 + source_id: BR-JM-004 + name: Duplicate Application Prevention + endpoint: /placement/api/apply-for-placement/ + method: POST + description: The system must prevent duplicate applications for the same schedule by the same student. + scenarios: + valid: + _test_id: BR09_V_01 + _br_id: BR09 + _scenario: Valid + _expected_result: First-time application succeeds. + invalid: + _test_id: BR09_I_01 + _br_id: BR09 + _scenario: Invalid + _expected_result: Repeated application for the same job is rejected. + + - id: BR10 + source_id: BR-JM-005 + name: Application Information Integrity + endpoint: /placement/api/application-detail/{application_id}/ + method: GET, PUT + description: Application records must remain accurate, traceable, and internally consistent. + scenarios: + valid: + _test_id: BR10_V_01 + _br_id: BR10 + _scenario: Valid + _expected_result: Valid application status update preserves a consistent application record. + invalid: + _test_id: BR10_I_01 + _br_id: BR10 + _scenario: Invalid + _expected_result: Invalid application mutation is rejected to preserve integrity. + + - id: BR11 + source_id: BR-JM-006 + name: Company-Student Application Limits + endpoint: /placement/api/apply-for-placement/ + method: POST + description: The system enforces configured limits on active student applications. + scenarios: + valid: + _test_id: BR11_V_01 + _br_id: BR11 + _scenario: Valid + _expected_result: Application is accepted while the active application count is within limit. + invalid: + _test_id: BR11_I_01 + _br_id: BR11 + _scenario: Invalid + _expected_result: Application is rejected when the active application limit is exceeded. + + - id: BR12 + source_id: BR-JM-007 + name: Job Posting Content Standards + endpoint: /placement/api/placement/ + method: POST, PUT + description: Job postings must contain valid descriptive and structural information. + scenarios: + valid: + _test_id: BR12_V_01 + _br_id: BR12 + _scenario: Valid + _expected_result: Posting with required content and structure is accepted. + invalid: + _test_id: BR12_I_01 + _br_id: BR12 + _scenario: Invalid + _expected_result: Posting with malformed or incomplete content is rejected. + + - id: BR13 + source_id: BR-IP-001 + name: Interview Scheduling Constraints + endpoint: /placement/api/application-detail/{application_id}/interview/ + method: POST + description: Interview scheduling must satisfy timing and conflict constraints. + scenarios: + valid: + _test_id: BR13_V_01 + _br_id: BR13 + _scenario: Valid + _expected_result: Valid interview slot is scheduled successfully. + invalid: + _test_id: BR13_I_01 + _br_id: BR13 + _scenario: Invalid + _expected_result: Conflicting or malformed interview schedule is rejected. + + - id: BR14 + source_id: BR-IP-002 + name: Shortlisting Criteria + endpoint: /placement/api/application-detail/{application_id}/ + method: PUT + description: Shortlisting decisions must follow the defined review and selection logic. + scenarios: + valid: + _test_id: BR14_V_01 + _br_id: BR14 + _scenario: Valid + _expected_result: Application status update consistent with shortlisting rules is accepted. + invalid: + _test_id: BR14_I_01 + _br_id: BR14 + _scenario: Invalid + _expected_result: Improper shortlisting action is rejected or blocked. + + - id: BR15 + source_id: BR-IP-003 + name: Interview Process Documentation + endpoint: /placement/api/my-applications/ + method: GET + description: The interview process must remain visible and traceable to students and administrators. + scenarios: + valid: + _test_id: BR15_V_01 + _br_id: BR15 + _scenario: Valid + _expected_result: Interview rounds and status history are exposed consistently in the application timeline. + invalid: + _test_id: BR15_I_01 + _br_id: BR15 + _scenario: Invalid + _expected_result: Missing or inconsistent interview documentation is treated as a workflow failure. + + - id: BR16 + source_id: BR-IP-004 + name: Multiple Offer Management + endpoint: /placement/api/offer/{offer_id}/respond/ + method: POST + description: The offer process enforces institute constraints around multiple concurrent offers. + scenarios: + valid: + _test_id: BR16_V_01 + _br_id: BR16 + _scenario: Valid + _expected_result: Student accepts a valid pending offer when no blocking accepted offer exists. + invalid: + _test_id: BR16_I_01 + _br_id: BR16 + _scenario: Invalid + _expected_result: Student is blocked from accepting another offer when policy forbids it. + + - id: BR17 + source_id: BR-IP-005 + name: Interview Feedback and Appeals + endpoint: /placement/api/placement-appeals/ + method: GET, POST, PUT + description: Students and TPO can record and process interview-related appeals and follow-up decisions. + scenarios: + valid: + _test_id: BR17_V_01 + _br_id: BR17 + _scenario: Valid + _expected_result: Valid appeal submission or decision is accepted and tracked. + invalid: + _test_id: BR17_I_01 + _br_id: BR17 + _scenario: Invalid + _expected_result: Unauthorized or malformed appeal action is rejected. + + - id: BR18 + source_id: BR-NT-001 + name: Notification Delivery Requirements + endpoint: /placement/api/send-notification/ + method: POST + description: Notifications must be delivered using approved targeting and channel rules. + scenarios: + valid: + _test_id: BR18_V_01 + _br_id: BR18 + _scenario: Valid + _expected_result: Notification request with valid target selection is processed successfully. + invalid: + _test_id: BR18_I_01 + _br_id: BR18 + _scenario: Invalid + _expected_result: Notification request with invalid recipient data is rejected. + + - id: BR19 + source_id: BR-NT-002 + name: Notification Content Standards + endpoint: /placement/api/send-notification/ + method: POST + description: Notification payloads must follow message-content quality and formatting requirements. + scenarios: + valid: + _test_id: BR19_V_01 + _br_id: BR19 + _scenario: Valid + _expected_result: Well-formed notification content is accepted for delivery. + invalid: + _test_id: BR19_I_01 + _br_id: BR19 + _scenario: Invalid + _expected_result: Empty or malformed notification content is rejected. + + - id: BR20 + source_id: BR-NT-003 + name: Communication Hierarchy + endpoint: /placement/api/send-notification/ + method: POST + description: Communication actions must respect placement governance and authorized sender roles. + scenarios: + valid: + _test_id: BR20_V_01 + _br_id: BR20 + _scenario: Valid + _expected_result: Authorized user sends an allowed notification successfully. + invalid: + _test_id: BR20_I_01 + _br_id: BR20 + _scenario: Invalid + _expected_result: Unauthorized notification action is denied. + + - id: BR21 + source_id: BR-NT-004 + name: Emergency Communication Protocol + endpoint: /placement/api/send-notification/ + method: POST + description: Critical notification flows must support urgent communication behavior. + scenarios: + valid: + _test_id: BR21_V_01 + _br_id: BR21 + _scenario: Valid + _expected_result: Emergency notification request is accepted with valid targeting information. + invalid: + _test_id: BR21_I_01 + _br_id: BR21 + _scenario: Invalid + _expected_result: Emergency notification request missing required information is rejected. + + - id: BR22 + source_id: BR-NT-005 + name: Privacy and Confidentiality + endpoint: /placement/api/send-notification/ + method: POST + description: Notifications and placement data handling must respect privacy boundaries and confidentiality. + scenarios: + valid: + _test_id: BR22_V_01 + _br_id: BR22 + _scenario: Valid + _expected_result: Authorized user accesses only permitted notification data and actions. + invalid: + _test_id: BR22_I_01 + _br_id: BR22 + _scenario: Invalid + _expected_result: Disallowed access or disclosure attempt is rejected. + + - id: BR23 + source_id: BR-AC-001 + name: Role-Based Access Control + endpoint: /placement/api/reports/ + method: GET + description: Endpoints must enforce permissions based on the current user role. + scenarios: + valid: + _test_id: BR23_V_01 + _br_id: BR23 + _scenario: Valid + _expected_result: Authorized role accesses the protected resource successfully. + invalid: + _test_id: BR23_I_01 + _br_id: BR23 + _scenario: Invalid + _expected_result: Unauthorized role is denied access to the protected resource. + + - id: BR24 + source_id: BR-AC-002 + name: Authentication and Session Management + endpoint: /placement/api/profile/ + method: GET + description: Placement APIs must require valid authenticated user context. + scenarios: + valid: + _test_id: BR24_V_01 + _br_id: BR24 + _scenario: Valid + _expected_result: Authenticated request succeeds. + invalid: + _test_id: BR24_I_01 + _br_id: BR24 + _scenario: Invalid + _expected_result: Unauthenticated request is rejected. + + - id: BR25 + source_id: BR-AC-003 + name: Data Access Restrictions + endpoint: /placement/api/statistics/ + method: GET + description: Data access must be restricted to permitted scopes and users. + scenarios: + valid: + _test_id: BR25_V_01 + _br_id: BR25 + _scenario: Valid + _expected_result: Permitted user accesses allowed data successfully. + invalid: + _test_id: BR25_I_01 + _br_id: BR25 + _scenario: Invalid + _expected_result: Disallowed data-access attempt is blocked. + + - id: BR26 + source_id: BR-AC-004 + name: System Administration Rules + endpoint: /placement/api/report-schedules/ + method: GET, POST, PUT + description: Administrative placement operations must follow privileged access rules. + scenarios: + valid: + _test_id: BR26_V_01 + _br_id: BR26 + _scenario: Valid + _expected_result: Administrator-level operation succeeds for an authorized user. + invalid: + _test_id: BR26_I_01 + _br_id: BR26 + _scenario: Invalid + _expected_result: Non-admin user is denied administrative operation access. + + - id: BR27 + source_id: BR-AC-005 + name: External Integration Security + endpoint: /placement/api/registration/ + method: POST + description: External-facing integrations such as company registration must follow secure handling rules. + scenarios: + valid: + _test_id: BR27_V_01 + _br_id: BR27 + _scenario: Valid + _expected_result: Secure registration request is accepted. + invalid: + _test_id: BR27_I_01 + _br_id: BR27 + _scenario: Invalid + _expected_result: Insecure or malformed external request is rejected. + + - id: BR28 + source_id: BR-DM-001 + name: Data Quality and Validation + endpoint: /placement/api/profile/ + method: PUT + description: Placement data must satisfy validation rules before it is persisted. + scenarios: + valid: + _test_id: BR28_V_01 + _br_id: BR28 + _scenario: Valid + _expected_result: Valid data payload is stored successfully. + invalid: + _test_id: BR28_I_01 + _br_id: BR28 + _scenario: Invalid + _expected_result: Invalid data payload is rejected with errors. + + - id: BR29 + source_id: BR-DM-002 + name: Data Retention and Archival + endpoint: /placement/api/reports/ + method: GET + description: Placement data should remain available according to retention and archival policy. + scenarios: + valid: + _test_id: BR29_V_01 + _br_id: BR29 + _scenario: Valid + _expected_result: Retained report data remains available within policy scope. + invalid: + _test_id: BR29_I_01 + _br_id: BR29 + _scenario: Invalid + _expected_result: Access to data outside retention policy is unavailable or rejected. + + - id: BR30 + source_id: BR-DM-003 + name: Reporting and Analytics + endpoint: /placement/api/reports/ + method: GET + description: Reporting APIs must generate consistent analytics structures for placement data. + scenarios: + valid: + _test_id: BR30_V_01 + _br_id: BR30 + _scenario: Valid + _expected_result: Requested report payload is generated successfully. + invalid: + _test_id: BR30_I_01 + _br_id: BR30 + _scenario: Invalid + _expected_result: Invalid report request is rejected or returns no generated report. + + - id: BR31 + source_id: BR-DM-004 + name: Data Privacy and Compliance + endpoint: /placement/api/alumni/profile/ + method: GET, PUT + description: Personal placement-related data must be handled according to privacy and compliance rules. + scenarios: + valid: + _test_id: BR31_V_01 + _br_id: BR31 + _scenario: Valid + _expected_result: Authorized user accesses or updates personal data within allowed scope. + invalid: + _test_id: BR31_I_01 + _br_id: BR31 + _scenario: Invalid + _expected_result: Non-compliant data access or update attempt is rejected. + + - id: BR32 + source_id: BR-DM-005 + name: Statistical Analysis and Insights + endpoint: /placement/api/statistics/ + method: GET + description: Statistical endpoints must produce meaningful and filterable placement insights. + scenarios: + valid: + _test_id: BR32_V_01 + _br_id: BR32 + _scenario: Valid + _expected_result: Statistics endpoint returns structured insight data for valid filters. + invalid: + _test_id: BR32_I_01 + _br_id: BR32 + _scenario: Invalid + _expected_result: Invalid filter combination or unavailable dataset is handled safely. + + - id: BR33 + source_id: BR-SI-001 + name: Academic System Integration + endpoint: /placement/api/profile/ + method: GET + description: Placement data should remain consistent with linked academic information. + scenarios: + valid: + _test_id: BR33_V_01 + _br_id: BR33 + _scenario: Valid + _expected_result: Academic-linked placement data remains synchronized and usable. + invalid: + _test_id: BR33_I_01 + _br_id: BR33 + _scenario: Invalid + _expected_result: Academic inconsistency is detected or blocks dependent placement action. + + - id: BR34 + source_id: BR-SI-002 + name: Communication System Integration + endpoint: /placement/api/send-notification/ + method: POST + description: Placement notifications integrate with supported communication channels. + scenarios: + valid: + _test_id: BR34_V_01 + _br_id: BR34 + _scenario: Valid + _expected_result: Notification request is accepted for configured channels. + invalid: + _test_id: BR34_I_01 + _br_id: BR34 + _scenario: Invalid + _expected_result: Channel or recipient mismatch causes rejection or failure response. + + - id: BR35 + source_id: BR-SI-003 + name: External Platform Integration + endpoint: /placement/api/registration/ + method: GET, POST + description: Placement data exchange with external platforms must remain consistent and controlled. + scenarios: + valid: + _test_id: BR35_V_01 + _br_id: BR35 + _scenario: Valid + _expected_result: External platform–related data is accepted in the supported integration flow. + invalid: + _test_id: BR35_I_01 + _br_id: BR35 + _scenario: Invalid + _expected_result: Unsupported integration request is rejected. + + - id: BR36 + source_id: BR-SI-004 + name: Workflow Automation Rules + endpoint: /placement/api/application-detail/{application_id}/ + method: PUT + description: Placement workflows may trigger automated state updates and notifications under allowed rules. + scenarios: + valid: + _test_id: BR36_V_01 + _br_id: BR36 + _scenario: Valid + _expected_result: Valid state transition triggers the expected automated follow-up behavior. + invalid: + _test_id: BR36_I_01 + _br_id: BR36 + _scenario: Invalid + _expected_result: Disallowed automated transition is rejected. + + - id: BR37 + source_id: BR-SI-005 + name: System Performance Rules + endpoint: /placement/api/placement/ + method: GET + description: The system must respond reliably under normal placement usage conditions. + scenarios: + valid: + _test_id: BR37_V_01 + _br_id: BR37 + _scenario: Valid + _expected_result: Supported request completes successfully within expected operational conditions. + invalid: + _test_id: BR37_I_01 + _br_id: BR37 + _scenario: Invalid + _expected_result: Degraded or unavailable behavior is surfaced as a controlled failure. + + - id: BR38 + source_id: BR-EX-001 + name: TPO Override Authority + endpoint: /placement/api/placement-appeals/ + method: GET, POST, PUT + description: TPO override actions must be documented and limited to authorized review flows. + scenarios: + valid: + _test_id: BR38_V_01 + _br_id: BR38 + _scenario: Valid + _expected_result: Authorized override decision is recorded successfully. + invalid: + _test_id: BR38_I_01 + _br_id: BR38 + _scenario: Invalid + _expected_result: Unauthorized or unsupported override request is rejected. + + - id: BR39 + source_id: BR-EX-002 + name: Emergency Procedures + endpoint: /placement/api/send-notification/ + method: POST + description: Emergency handling must support alternative communication or recovery actions. + scenarios: + valid: + _test_id: BR39_V_01 + _br_id: BR39 + _scenario: Valid + _expected_result: Emergency procedure request is accepted for a supported scenario. + invalid: + _test_id: BR39_I_01 + _br_id: BR39 + _scenario: Invalid + _expected_result: Unsupported emergency procedure invocation is rejected. + + - id: BR40 + source_id: BR-EX-003 + name: Policy Violation Handling + endpoint: /placement/api/placement-appeals/ + method: GET, POST, PUT + description: Policy violations and related review actions must follow a documented control path. + scenarios: + valid: + _test_id: BR40_V_01 + _br_id: BR40 + _scenario: Valid + _expected_result: Valid policy-violation review action is recorded successfully. + invalid: + _test_id: BR40_I_01 + _br_id: BR40 + _scenario: Invalid + _expected_result: Invalid policy-violation handling action is rejected. diff --git a/FusionIIIT/applications/placement_cell/tests/specs/use_cases.yaml b/FusionIIIT/applications/placement_cell/tests/specs/use_cases.yaml new file mode 100644 index 000000000..1dcf61362 --- /dev/null +++ b/FusionIIIT/applications/placement_cell/tests/specs/use_cases.yaml @@ -0,0 +1,506 @@ +use_cases: + - id: UC01 + source_id: PC-UC-S001 + name: Profile Management + endpoint: /placement/api/profile/ + method: GET, PUT + description: Students create and update their comprehensive placement profile including personal, academic, and skills details. + scenarios: + happy_path: + _test_id: UC01_HP_01 + _uc_id: UC01 + _scenario: Happy Path + _expected_result: Student saves a valid placement profile and sees an updated profile summary. + alternate_path: + _test_id: UC01_AP_01 + _uc_id: UC01 + _scenario: Alternate Path + _expected_result: Student retrieves profile completeness, eligibility summary, documents, and audit history. + exception_path: + _test_id: UC01_EP_01 + _uc_id: UC01 + _scenario: Exception Path + _expected_result: Invalid profile details are rejected with field-level validation errors. + + - id: UC02 + source_id: PC-UC-S002 + name: Browse and Search Jobs + endpoint: /placement/api/placement/ + method: GET + description: Students browse available job postings and search for opportunities based on preferences and eligibility. + scenarios: + happy_path: + _test_id: UC02_HP_01 + _uc_id: UC02 + _scenario: Happy Path + _expected_result: Student sees active upcoming job opportunities. + alternate_path: + _test_id: UC02_AP_01 + _uc_id: UC02 + _scenario: Alternate Path + _expected_result: Filtered search returns only matching placements. + exception_path: + _test_id: UC02_EP_01 + _uc_id: UC02 + _scenario: Exception Path + _expected_result: No active jobs results in an empty list with no server error. + + - id: UC03 + source_id: PC-UC-S003 + name: Apply for Jobs + endpoint: /placement/api/apply-for-placement/ + method: POST + description: Students submit applications for eligible job postings with required information and supporting profile data. + scenarios: + happy_path: + _test_id: UC03_HP_01 + _uc_id: UC03 + _scenario: Happy Path + _expected_result: Eligible student with a complete profile submits an application successfully. + alternate_path: + _test_id: UC03_AP_01 + _uc_id: UC03 + _scenario: Alternate Path + _expected_result: Ineligible student is blocked with eligibility reasons. + exception_path: + _test_id: UC03_EP_01 + _uc_id: UC03 + _scenario: Exception Path + _expected_result: Duplicate application attempt is rejected with a conflict response. + + - id: UC04 + source_id: PC-UC-S004 + name: Track Application Status + endpoint: /placement/api/my-applications/ + method: GET + description: Students monitor the status and progress of submitted applications. + scenarios: + happy_path: + _test_id: UC04_HP_01 + _uc_id: UC04 + _scenario: Happy Path + _expected_result: Student sees a list of submitted applications with current statuses. + alternate_path: + _test_id: UC04_AP_01 + _uc_id: UC04 + _scenario: Alternate Path + _expected_result: Student sees interview rounds, next interview details, and offer references for an application. + exception_path: + _test_id: UC04_EP_01 + _uc_id: UC04 + _scenario: Exception Path + _expected_result: Student with no applications receives an empty applications list. + + - id: UC05 + source_id: PC-UC-S005 + name: Manage Job Offers + endpoint: /placement/api/my-offers/ + method: GET, POST + description: Students review, accept, or reject job offers received from companies. + scenarios: + happy_path: + _test_id: UC05_HP_01 + _uc_id: UC05 + _scenario: Happy Path + _expected_result: Student views active offers and can accept an eligible pending offer. + alternate_path: + _test_id: UC05_AP_01 + _uc_id: UC05 + _scenario: Alternate Path + _expected_result: Student rejects an offer and the response is recorded successfully. + exception_path: + _test_id: UC05_EP_01 + _uc_id: UC05 + _scenario: Exception Path + _expected_result: Expired or already-responded offers are blocked with an appropriate error. + + - id: UC06 + source_id: PC-UC-S006 + name: Generate Resume + endpoint: /placement/api/generate-cv/ + method: POST + description: Students generate formatted resumes using profile information stored in the placement module. + scenarios: + happy_path: + _test_id: UC06_HP_01 + _uc_id: UC06 + _scenario: Happy Path + _expected_result: Student generates and downloads a resume successfully. + alternate_path: + _test_id: UC06_AP_01 + _uc_id: UC06 + _scenario: Alternate Path + _expected_result: Resume is generated using the latest available profile, education, and project data. + exception_path: + _test_id: UC06_EP_01 + _uc_id: UC06 + _scenario: Exception Path + _expected_result: Resume generation fails gracefully when required data is incomplete or missing. + + - id: UC07 + source_id: PC-UC-S007 + name: View Placement Statistics + endpoint: /placement/api/statistics/ + method: GET + description: Students access placement statistics and trends for their batch and department. + scenarios: + happy_path: + _test_id: UC07_HP_01 + _uc_id: UC07 + _scenario: Happy Path + _expected_result: Student views placement statistics dashboard data successfully. + alternate_path: + _test_id: UC07_AP_01 + _uc_id: UC07 + _scenario: Alternate Path + _expected_result: Student filters statistics by company, year, or department and sees updated results. + exception_path: + _test_id: UC07_EP_01 + _uc_id: UC07 + _scenario: Exception Path + _expected_result: When no statistics match the filters, the API returns an empty dataset without failing. + + - id: UC08 + source_id: PC-UC-S008 + name: Withdraw Application + endpoint: /placement/api/apply-for-placement/{schedule_id}/ + method: DELETE + description: Students withdraw submitted applications before the recruitment process is completed. + scenarios: + happy_path: + _test_id: UC08_HP_01 + _uc_id: UC08 + _scenario: Happy Path + _expected_result: Student withdraws an active application successfully. + alternate_path: + _test_id: UC08_AP_01 + _uc_id: UC08 + _scenario: Alternate Path + _expected_result: Withdrawal also updates related invitation status and sends notifications. + exception_path: + _test_id: UC08_EP_01 + _uc_id: UC08 + _scenario: Exception Path + _expected_result: Already withdrawn applications cannot be withdrawn again. + + - id: UC09 + source_id: PC-UC-T001 + name: Manage Job Postings + endpoint: /placement/api/placement/ + method: GET, POST, PUT + description: TPO reviews, approves, creates, updates, and publishes job postings. + scenarios: + happy_path: + _test_id: UC09_HP_01 + _uc_id: UC09 + _scenario: Happy Path + _expected_result: TPO creates a valid job posting and it becomes visible to eligible students. + alternate_path: + _test_id: UC09_AP_01 + _uc_id: UC09 + _scenario: Alternate Path + _expected_result: TPO updates an existing job posting details successfully. + exception_path: + _test_id: UC09_EP_01 + _uc_id: UC09 + _scenario: Exception Path + _expected_result: Invalid schedule data such as past placement date is rejected. + + - id: UC10 + source_id: PC-UC-T002 + name: Schedule Interviews + endpoint: /placement/api/application-detail/{application_id}/interview/ + method: GET, POST + description: TPO coordinates interview schedules for shortlisted students and companies. + scenarios: + happy_path: + _test_id: UC10_HP_01 + _uc_id: UC10 + _scenario: Happy Path + _expected_result: TPO schedules an interview for an application successfully. + alternate_path: + _test_id: UC10_AP_01 + _uc_id: UC10 + _scenario: Alternate Path + _expected_result: Student and stakeholders can view the scheduled interview timeline. + exception_path: + _test_id: UC10_EP_01 + _uc_id: UC10 + _scenario: Exception Path + _expected_result: Conflicting or invalid interview timings are rejected. + + - id: UC11 + source_id: PC-UC-T003 + name: Generate Placement Reports + endpoint: /placement/api/reports/ + method: GET + description: TPO generates placement reports and analytics across company, batch, branch, and detailed views. + scenarios: + happy_path: + _test_id: UC11_HP_01 + _uc_id: UC11 + _scenario: Happy Path + _expected_result: TPO retrieves a placement report payload successfully. + alternate_path: + _test_id: UC11_AP_01 + _uc_id: UC11 + _scenario: Alternate Path + _expected_result: TPO applies report filters and receives a narrowed dataset. + exception_path: + _test_id: UC11_EP_01 + _uc_id: UC11 + _scenario: Exception Path + _expected_result: Non-TPO users are denied access to report APIs. + + - id: UC12 + source_id: PC-UC-C001 + name: Oversight Dashboard Access + endpoint: /placement/api/reports/ + method: GET + description: Placement chairman accesses consolidated oversight data for placement activities. + scenarios: + happy_path: + _test_id: UC12_HP_01 + _uc_id: UC12 + _scenario: Happy Path + _expected_result: Chairman accesses placement reports and dashboard-level summaries successfully. + alternate_path: + _test_id: UC12_AP_01 + _uc_id: UC12 + _scenario: Alternate Path + _expected_result: Chairman reviews filtered reports for a specific company, year, or department. + exception_path: + _test_id: UC12_EP_01 + _uc_id: UC12 + _scenario: Exception Path + _expected_result: Unauthorized users cannot access chairman-level oversight data. + + - id: UC13 + source_id: PC-UC-C002 + name: Make Official Announcements + endpoint: /placement/api/send-notification/ + method: POST + description: Chairman publishes official announcements and major placement updates to stakeholders. + scenarios: + happy_path: + _test_id: UC13_HP_01 + _uc_id: UC13 + _scenario: Happy Path + _expected_result: Chairman sends an announcement to all target recipients successfully. + alternate_path: + _test_id: UC13_AP_01 + _uc_id: UC13 + _scenario: Alternate Path + _expected_result: Chairman sends a targeted notification to a specific user or role group. + exception_path: + _test_id: UC13_EP_01 + _uc_id: UC13 + _scenario: Exception Path + _expected_result: Invalid recipient selection returns a not-found or validation error. + + - id: UC14 + source_id: PC-UC-R001 + name: Company Registration + endpoint: /placement/api/registration/ + method: GET, POST + description: Companies register on the platform and create a company profile for placement activities. + scenarios: + happy_path: + _test_id: UC14_HP_01 + _uc_id: UC14 + _scenario: Happy Path + _expected_result: Company submits registration details successfully. + alternate_path: + _test_id: UC14_AP_01 + _uc_id: UC14 + _scenario: Alternate Path + _expected_result: Authorized user retrieves the company directory or registration listing. + exception_path: + _test_id: UC14_EP_01 + _uc_id: UC14 + _scenario: Exception Path + _expected_result: Invalid or incomplete registration payload is rejected. + + - id: UC15 + source_id: PC-UC-R002 + name: Post Job Opportunities + endpoint: /placement/api/placement/ + method: POST + description: Companies create and submit job postings with detailed eligibility criteria and schedules. + scenarios: + happy_path: + _test_id: UC15_HP_01 + _uc_id: UC15 + _scenario: Happy Path + _expected_result: Company or authorized staff submits a valid job opportunity successfully. + alternate_path: + _test_id: UC15_AP_01 + _uc_id: UC15 + _scenario: Alternate Path + _expected_result: Job posting includes company linkage, filters, and optional attachment metadata. + exception_path: + _test_id: UC15_EP_01 + _uc_id: UC15 + _scenario: Exception Path + _expected_result: Posting with invalid schedule or malformed fields is rejected. + + - id: UC16 + source_id: PC-UC-R003 + name: Review Applications + endpoint: /placement/api/student-applications/{identifier}/ + method: GET, PUT + description: Companies review student applications, shortlist candidates, and update application status. + scenarios: + happy_path: + _test_id: UC16_HP_01 + _uc_id: UC16 + _scenario: Happy Path + _expected_result: Reviewer fetches submitted applications for a job and updates application status successfully. + alternate_path: + _test_id: UC16_AP_01 + _uc_id: UC16 + _scenario: Alternate Path + _expected_result: Reviewer accesses detailed application information including resume and interview history. + exception_path: + _test_id: UC16_EP_01 + _uc_id: UC16 + _scenario: Exception Path + _expected_result: Invalid application identifier or unauthorized review request is rejected. + + - id: UC17 + source_id: PC-UC-A001 + name: Alumni Registration and Verification + endpoint: /placement/api/alumni/profile/ + method: GET, POST, PUT + description: Alumni register on the platform, upload verification data, and await approval for alumni features. + scenarios: + happy_path: + _test_id: UC17_HP_01 + _uc_id: UC17 + _scenario: Happy Path + _expected_result: Alumni submits a new profile for verification successfully. + alternate_path: + _test_id: UC17_AP_01 + _uc_id: UC17 + _scenario: Alternate Path + _expected_result: Approved alumni updates profile details and mentorship preferences successfully. + exception_path: + _test_id: UC17_EP_01 + _uc_id: UC17 + _scenario: Exception Path + _expected_result: Unapproved alumni attempting restricted updates receive a pending-approval error. + + - id: UC18 + source_id: PC-UC-S009 + name: Receive Notifications + endpoint: /placement/api/notification-preferences/ + method: GET, PUT + description: Students receive and manage notification preferences for placement activities and updates. + scenarios: + happy_path: + _test_id: UC18_HP_01 + _uc_id: UC18 + _scenario: Happy Path + _expected_result: Student views current notification preferences successfully. + alternate_path: + _test_id: UC18_AP_01 + _uc_id: UC18 + _scenario: Alternate Path + _expected_result: Student updates portal, email, and SMS notification preferences successfully. + exception_path: + _test_id: UC18_EP_01 + _uc_id: UC18 + _scenario: Exception Path + _expected_result: Unauthorized access to notification preferences is denied. + + - id: UC19 + source_id: PC-UC-T004 + name: Generate Placement Reports + endpoint: /placement/api/report-schedules/ + method: GET, POST, PUT + description: TPO manages scheduled placement reports and recurring report generation settings. + scenarios: + happy_path: + _test_id: UC19_HP_01 + _uc_id: UC19 + _scenario: Happy Path + _expected_result: TPO creates a scheduled report successfully. + alternate_path: + _test_id: UC19_AP_01 + _uc_id: UC19 + _scenario: Alternate Path + _expected_result: TPO updates report schedule configuration and notification targets successfully. + exception_path: + _test_id: UC19_EP_01 + _uc_id: UC19 + _scenario: Exception Path + _expected_result: Non-admin users cannot manage report schedules. + + - id: UC20 + source_id: PC-UC-T005 + name: Manage Company Relations + endpoint: /placement/api/registration/ + method: GET, POST + description: TPO manages company records and tracks recruiting organizations in the placement system. + scenarios: + happy_path: + _test_id: UC20_HP_01 + _uc_id: UC20 + _scenario: Happy Path + _expected_result: TPO registers or maintains company information successfully. + alternate_path: + _test_id: UC20_AP_01 + _uc_id: UC20 + _scenario: Alternate Path + _expected_result: TPO reviews available company entries and uses them while creating schedules. + exception_path: + _test_id: UC20_EP_01 + _uc_id: UC20 + _scenario: Exception Path + _expected_result: Invalid company information is rejected by registration validation rules. + + - id: UC21 + source_id: PC-UC-T006 + name: Send Notifications + endpoint: /placement/api/send-notification/ + method: POST + description: TPO sends targeted notifications to students, companies, or other placement stakeholders. + scenarios: + happy_path: + _test_id: UC21_HP_01 + _uc_id: UC21 + _scenario: Happy Path + _expected_result: TPO sends a notification to all students successfully. + alternate_path: + _test_id: UC21_AP_01 + _uc_id: UC21 + _scenario: Alternate Path + _expected_result: TPO sends a notification to a specific recipient successfully. + exception_path: + _test_id: UC21_EP_01 + _uc_id: UC21 + _scenario: Exception Path + _expected_result: Notification request with an unknown recipient fails with a descriptive error. + + - id: UC22 + source_id: PC-UC-T007 + name: Manage Student Eligibility Override + endpoint: /placement/api/placement-appeals/ + method: GET, POST, PUT + description: TPO handles student eligibility exceptions, appeals, and override-style review decisions. + scenarios: + happy_path: + _test_id: UC22_HP_01 + _uc_id: UC22 + _scenario: Happy Path + _expected_result: TPO reviews a student appeal and records a decision successfully. + alternate_path: + _test_id: UC22_AP_01 + _uc_id: UC22 + _scenario: Alternate Path + _expected_result: Student submits an eligibility-related appeal with supporting notes successfully. + exception_path: + _test_id: UC22_EP_01 + _uc_id: UC22 + _scenario: Exception Path + _expected_result: Invalid or unauthorized appeal actions are rejected. diff --git a/FusionIIIT/applications/placement_cell/tests/specs/workflows.yaml b/FusionIIIT/applications/placement_cell/tests/specs/workflows.yaml new file mode 100644 index 000000000..a4c292a39 --- /dev/null +++ b/FusionIIIT/applications/placement_cell/tests/specs/workflows.yaml @@ -0,0 +1,201 @@ +workflows: + - id: WF01 + source_section: End-to-End Placement Workflow + name: End-to-End Placement Workflow + description: Placement season progresses from job publication through student application, selection, offer, and finalization. + steps: + - Company or TPO publishes a job + - Student applies and company reviews applications + - Interviews and selection decisions are recorded + - Offer is released and placement records are finalized + scenarios: + end_to_end: + _test_id: WF01_E2E_01 + _wf_id: WF01 + _scenario: End-to-End + _expected_result: Published job moves through application and offer stages to a completed placement outcome. + negative: + _test_id: WF01_NEG_01 + _wf_id: WF01 + _scenario: Negative + _expected_result: Workflow halts safely when the student cannot complete the application path. + + - id: WF02 + source_section: Student Application Workflow + name: Student Application Workflow + description: Student completes the placement profile, browses jobs, passes eligibility checks, and submits an application. + steps: + - Complete placement profile + - Retrieve profile summary + - Submit application for an eligible job + scenarios: + end_to_end: + _test_id: WF02_E2E_01 + _wf_id: WF02 + _scenario: End-to-End + _expected_result: Completed profile enables successful job application. + negative: + _test_id: WF02_NEG_01 + _wf_id: WF02 + _scenario: Negative + _expected_result: Incomplete profile blocks job application. + + - id: WF03 + source_section: TPO Job Posting Management Workflow + name: TPO Job Posting Management Workflow + description: TPO reviews job details, validates the posting, and publishes it to eligible students. + steps: + - Review job details and company information + - Validate compensation and content standards + - Approve and publish the job posting + scenarios: + end_to_end: + _test_id: WF03_E2E_01 + _wf_id: WF03 + _scenario: End-to-End + _expected_result: Valid posting is approved and becomes visible to students. + negative: + _test_id: WF03_NEG_01 + _wf_id: WF03 + _scenario: Negative + _expected_result: Invalid posting is rejected before publication. + + - id: WF04 + source_section: Interview Scheduling Workflow + name: Interview Scheduling Workflow + description: TPO creates interview schedules after shortlisting and ensures students can track upcoming rounds. + steps: + - Create or update application shortlist state + - Schedule interview round for an application + - Student views interview schedule in application timeline + scenarios: + end_to_end: + _test_id: WF04_E2E_01 + _wf_id: WF04 + _scenario: End-to-End + _expected_result: Interview is scheduled successfully and appears in the tracked application timeline. + negative: + _test_id: WF04_NEG_01 + _wf_id: WF04 + _scenario: Negative + _expected_result: Conflicting or invalid interview schedule request is rejected. + + - id: WF05 + source_section: Offer Management Workflow + name: Offer Release To Offer Dashboard Visibility + description: After an application is processed and an offer is released, the student can see it on the offer dashboard. + steps: + - Student applies for a schedule + - TPO updates application status to offer released + - Student views my offers + scenarios: + end_to_end: + _test_id: WF05_E2E_01 + _wf_id: WF05 + _scenario: End-to-End + _expected_result: Released offer becomes visible on the student offer dashboard. + negative: + _test_id: WF05_NEG_01 + _wf_id: WF05 + _scenario: Negative + _expected_result: Offer dashboard stays empty when no offer has been released. + + - id: WF06 + source_section: Company Registration Workflow + name: Company Registration Workflow + description: Company details are submitted, validated, and added to the placement system for later recruiting activity. + steps: + - Submit company registration details + - Validate registration payload + - Persist company record for placement usage + scenarios: + end_to_end: + _test_id: WF06_E2E_01 + _wf_id: WF06 + _scenario: End-to-End + _expected_result: Company registration is stored successfully and can be retrieved later. + negative: + _test_id: WF06_NEG_01 + _wf_id: WF06 + _scenario: Negative + _expected_result: Invalid company registration request is rejected safely. + + - id: WF07 + source_section: Alumni Engagement Workflow + name: Alumni Registration And Verification Workflow + description: Alumni submit registration data, wait for review, and then access alumni capabilities after approval. + steps: + - Alumni submits profile and verification details + - TPO reviews verification queue and approves profile + - Approved alumni updates or accesses alumni features + scenarios: + end_to_end: + _test_id: WF07_E2E_01 + _wf_id: WF07 + _scenario: End-to-End + _expected_result: Alumni profile is submitted, approved, and becomes accessible. + negative: + _test_id: WF07_NEG_01 + _wf_id: WF07 + _scenario: Negative + _expected_result: Pending or rejected alumni profile cannot access restricted alumni capabilities. + + - id: WF08 + source_section: Notification Management Workflow + name: Notification Management Workflow + description: Authorized placement users send notifications to all students or a specific recipient and the request is validated. + steps: + - Compose placement notification + - Select recipients or all-students target + - Submit the notification request + scenarios: + end_to_end: + _test_id: WF08_E2E_01 + _wf_id: WF08 + _scenario: End-to-End + _expected_result: Valid notification request is accepted and logged as sent. + negative: + _test_id: WF08_NEG_01 + _wf_id: WF08 + _scenario: Negative + _expected_result: Notification request with an invalid recipient is rejected. + + - id: WF09 + source_section: Load Balancing Workflow + name: Placement Access Under Load Workflow + description: Placement browsing and reporting continue to function under expected operational load and access patterns. + steps: + - User accesses placement listing or analytics endpoint + - System processes the request under normal operating conditions + - Response is returned in a usable form + scenarios: + end_to_end: + _test_id: WF09_E2E_01 + _wf_id: WF09 + _scenario: End-to-End + _expected_result: Core placement endpoint responds successfully for a normal request. + negative: + _test_id: WF09_NEG_01 + _wf_id: WF09 + _scenario: Negative + _expected_result: Degraded or invalid request path fails in a controlled way. + + - id: WF10 + source_section: Backup and Recovery Workflow + name: Reporting And Recovery Visibility Workflow + description: Historical placement data remains queryable through reports and statistics after normal operational changes. + steps: + - Create or retain placement records + - Request reports or statistics + - Validate accessible recovered or retained data view + scenarios: + end_to_end: + _test_id: WF10_E2E_01 + _wf_id: WF10 + _scenario: End-to-End + _expected_result: Reporting endpoints return retained placement information successfully. + negative: + _test_id: WF10_NEG_01 + _wf_id: WF10 + _scenario: Negative + _expected_result: Missing or invalid report request path is handled without breaking the workflow. diff --git a/FusionIIIT/applications/placement_cell/tests/test_business_rules.py b/FusionIIIT/applications/placement_cell/tests/test_business_rules.py new file mode 100644 index 000000000..7b48e79f9 --- /dev/null +++ b/FusionIIIT/applications/placement_cell/tests/test_business_rules.py @@ -0,0 +1,905 @@ +import datetime + +import yaml +from django.core.files.uploadedfile import SimpleUploadedFile +from django.test import override_settings +from django.utils import timezone + +from applications.academic_information.models import Student +from applications.placement_cell.models import ( + PlacementAppeal, + PlacementApplication, + PlacementInterviewSchedule, + PlacementPolicy, + PlacementRecord, + PlacementReportSchedule, + PlacementStatus, + StudentPlacement, +) +from applications.placement_cell.tests.conftest import PlacementCellSpecBase + + +class TestBusinessRuleCatalogIntegrity(PlacementCellSpecBase): + def test_all_documented_business_rules_define_two_scenarios(self): + with (self.specs_dir / "business_rules.yaml").open("r", encoding="utf-8") as handle: + payload = yaml.safe_load(handle) or {} + + business_rules = payload.get("business_rules", []) + self.assertGreaterEqual(len(business_rules), 20) + + for business_rule in business_rules: + self.assertIn("source_id", business_rule) + self.assertIn("endpoint", business_rule) + self.assertIn("method", business_rule) + self.assertEqual(sorted(business_rule.get("scenarios", {}).keys()), ["invalid", "valid"]) + + def test_business_rule_test_ids_are_unique(self): + with (self.specs_dir / "business_rules.yaml").open("r", encoding="utf-8") as handle: + payload = yaml.safe_load(handle) or {} + + test_ids = [] + for business_rule in payload.get("business_rules", []): + for scenario in business_rule.get("scenarios", {}).values(): + test_ids.append(scenario["_test_id"]) + + self.assertEqual(len(test_ids), len(set(test_ids))) + + +class TestBR01_FuturePlacementDate(PlacementCellSpecBase): + def test_valid_future_date_is_accepted(self): + metadata = self.load_spec("business_rules.yaml", "business_rules", "BR01")["scenarios"]["valid"] + + response = self.api_post( + "/placement/api/placement/", + user=self.officer, + data={ + "company_name": "Future Co", + "title": "Future Co", + "placement_type": "PLACEMENT", + "ctc": "12.50", + "description": "Campus drive", + "placement_date": (timezone.now().date() + datetime.timedelta(days=3)).isoformat(), + "schedule_at": "2026-04-15 10:30", + "location": "Auditorium", + }, + format="multipart", + ) + + self.assertEqual(metadata["_scenario"], "Valid") + self.assertEqual(response.status_code, 201) + self.assertEqual(response.data["company_name"], "Future Co") + + def test_invalid_past_date_is_rejected(self): + metadata = self.load_spec("business_rules.yaml", "business_rules", "BR01")["scenarios"]["invalid"] + + response = self.api_post( + "/placement/api/placement/", + user=self.officer, + data={ + "company_name": "Past Co", + "placement_type": "PLACEMENT", + "placement_date": (timezone.now().date() - datetime.timedelta(days=1)).isoformat(), + "schedule_at": "2026-04-01 10:30", + }, + format="multipart", + ) + + self.assertEqual(metadata["_scenario"], "Invalid") + self.assertEqual(response.status_code, 400) + self.assertIn("placement_date", response.data) + + +class TestBR02_MandatoryProfileFields(PlacementCellSpecBase): + def test_valid_profile_payload_is_accepted(self): + metadata = self.load_spec("business_rules.yaml", "business_rules", "BR02")["scenarios"]["valid"] + + response = self.api_put( + "/placement/api/profile/", + user=self.student_user, + data={ + "first_name": "Student", + "last_name": "One", + "email": "student1@example.com", + "phone_no": "9876543210", + "address": "Hostel A", + "about_me": "Ready for placements", + }, + format="multipart", + ) + + self.assertEqual(metadata["_scenario"], "Valid") + self.assertEqual(response.status_code, 200) + self.assertEqual(response.data["profile"]["address"], "Hostel A") + self.assertNotIn("first_name", response.data["field_errors"]) + self.assertNotIn("email", response.data["field_errors"]) + + def test_invalid_profile_payload_is_rejected(self): + metadata = self.load_spec("business_rules.yaml", "business_rules", "BR02")["scenarios"]["invalid"] + + response = self.api_put( + "/placement/api/profile/", + user=self.student_user, + data={ + "first_name": "", + "last_name": "", + "email": "not-an-email", + "phone_no": "123", + "address": "", + "about_me": "", + }, + format="multipart", + ) + + self.assertEqual(metadata["_scenario"], "Invalid") + self.assertEqual(response.status_code, 400) + self.assertIn("field_errors", response.data) + self.assertIn("phone_no", response.data["field_errors"]) + + +class TestBR03_NoDuplicateApplication(PlacementCellSpecBase): + def test_valid_first_application_succeeds(self): + metadata = self.load_spec("business_rules.yaml", "business_rules", "BR03")["scenarios"]["valid"] + student = self._make_profile_complete(self.student_user) + schedule = self._create_schedule(company_name="Rule Corp") + + response = self.api_post( + "/placement/api/apply-for-placement/", + user=self.student_user, + data={"jobId": schedule.id, "responses": []}, + ) + + self.assertEqual(metadata["_scenario"], "Valid") + self.assertEqual(response.status_code, 200) + self.assertTrue(PlacementApplication.objects.filter(schedule=schedule, student=student).exists()) + + def test_invalid_duplicate_application_is_rejected(self): + metadata = self.load_spec("business_rules.yaml", "business_rules", "BR03")["scenarios"]["invalid"] + self._make_profile_complete(self.student_user) + schedule = self._create_schedule(company_name="Rule Duplicate Corp") + self.api_post( + "/placement/api/apply-for-placement/", + user=self.student_user, + data={"jobId": schedule.id, "responses": []}, + ) + + response = self.api_post( + "/placement/api/apply-for-placement/", + user=self.student_user, + data={"jobId": schedule.id, "responses": []}, + ) + + self.assertEqual(metadata["_scenario"], "Invalid") + self.assertEqual(response.status_code, 409) + self.assertIn("already applied", response.data["detail"]) + + +class TestPlacementPolicyManagement(PlacementCellSpecBase): + def test_chairman_can_view_and_add_policies(self): + self._create_officer_designation(name="placement chairman") + + create_response = self.api_post( + "/placement/api/policies/", + user=self.officer, + data={ + "title": "Dream Offer Rule", + "description": "Students with an accepted dream offer cannot continue in regular drives.", + }, + ) + + self.assertEqual(create_response.status_code, 201) + self.assertEqual(create_response.data["title"], "Dream Offer Rule") + self.assertTrue(PlacementPolicy.objects.filter(title="Dream Offer Rule").exists()) + + list_response = self.api_get("/placement/api/policies/", user=self.officer) + + self.assertEqual(list_response.status_code, 200) + self.assertEqual(list_response.data[0]["title"], "Dream Offer Rule") + + def test_chairman_can_edit_policies(self): + self._create_officer_designation(name="placement chairman") + policy = PlacementPolicy.objects.create( + title="Original Rule", + description="Original description", + created_by=self.officer, + ) + + response = self.api_put( + f"/placement/api/policies/{policy.id}/", + user=self.officer, + data={ + "title": "Updated Rule", + "description": "Updated description", + }, + ) + + self.assertEqual(response.status_code, 200) + policy.refresh_from_db() + self.assertEqual(policy.title, "Updated Rule") + self.assertEqual(policy.description, "Updated description") + + def test_non_chairman_cannot_manage_policies(self): + response = self.api_get("/placement/api/policies/", user=self.student_user) + + self.assertEqual(response.status_code, 403) + self.assertIn("Only placement chairman users", response.data["detail"]) + + +class TestGeneratedBusinessRules(PlacementCellSpecBase): + def _get_br_metadata(self, br_id, scenario_key): + return self.load_spec("business_rules.yaml", "business_rules", br_id)["scenarios"][scenario_key] + + def _create_submitted_application(self, *, company_name="Rule Workflow Corp"): + student = self._make_profile_complete(self.student_user) + schedule = self._create_schedule(company_name=company_name) + response = self.api_post( + "/placement/api/apply-for-placement/", + user=self.student_user, + data={"jobId": schedule.id, "responses": []}, + ) + self.assertEqual(response.status_code, 200) + application = PlacementApplication.objects.get(schedule=schedule, student=student) + return student, schedule, application + + def _create_rejected_appeal_context(self): + student, schedule, application = self._create_submitted_application(company_name="Appeal Corp") + application.status = "reject" + application.remarks = "Rejected for rule validation" + application.save(update_fields=["status", "remarks", "updated_at"]) + placement_status, _ = PlacementStatus.objects.get_or_create( + notify_id=schedule.notify_id, + unique_id=student, + defaults={ + "invitation": "REJECTED", + "no_of_days": 2, + }, + ) + placement_status.invitation = "REJECTED" + placement_status.no_of_days = 2 + placement_status.save(update_fields=["invitation", "no_of_days", "timestamp"]) + return student, schedule, application, placement_status + + def _assert_metadata_and_status(self, metadata, expected_label, response, expected_statuses): + self.assertEqual(metadata["_scenario"], expected_label) + self.assertIn(response.status_code, expected_statuses) + + def _run_profile_update_valid(self, br_id): + metadata = self._get_br_metadata(br_id, "valid") + response = self.api_put( + "/placement/api/profile/", + user=self.student_user, + data={ + "first_name": "Student", + "last_name": "One", + "email": "student1@example.com", + "phone_no": "9876543210", + "address": "Hostel A", + "about_me": "Ready for placements", + }, + format="multipart", + ) + self._assert_metadata_and_status(metadata, "Valid", response, {200}) + self.assertEqual(response.data["profile"]["address"], "Hostel A") + + def _run_profile_update_invalid(self, br_id): + metadata = self._get_br_metadata(br_id, "invalid") + response = self.api_put( + "/placement/api/profile/", + user=self.student_user, + data={ + "first_name": "", + "last_name": "", + "email": "bad-email", + "phone_no": "123", + "address": "", + "about_me": "", + }, + format="multipart", + ) + self._assert_metadata_and_status(metadata, "Invalid", response, {400}) + self.assertIn("field_errors", response.data) + + def _run_profile_completeness_valid(self, br_id): + metadata = self._get_br_metadata(br_id, "valid") + self._make_profile_complete(self.student_user) + response = self.api_get("/placement/api/profile/", user=self.student_user) + self._assert_metadata_and_status(metadata, "Valid", response, {200}) + self.assertTrue(response.data["is_complete"]) + + def _run_profile_completeness_invalid(self, br_id): + metadata = self._get_br_metadata(br_id, "invalid") + response = self.api_get("/placement/api/profile/", user=self.student_user) + self._assert_metadata_and_status(metadata, "Invalid", response, {200}) + self.assertFalse(response.data["is_complete"]) + self.assertIn("skills", response.data["field_errors"]) + + def _run_document_upload_valid(self, br_id): + metadata = self._get_br_metadata(br_id, "valid") + response = self.api_post( + "/placement/api/profile/", + user=self.student_user, + data={ + "name": "Resume", + "document": SimpleUploadedFile("resume.pdf", b"pdf-content", content_type="application/pdf"), + }, + format="multipart", + ) + self._assert_metadata_and_status(metadata, "Valid", response, {201}) + self.assertEqual(response.data["name"], "Resume") + + def _run_document_upload_invalid(self, br_id): + metadata = self._get_br_metadata(br_id, "invalid") + response = self.api_post( + "/placement/api/profile/", + user=self.student_user, + data={ + "name": "Resume", + "document": SimpleUploadedFile("resume.exe", b"binary", content_type="application/octet-stream"), + }, + format="multipart", + ) + self._assert_metadata_and_status(metadata, "Invalid", response, {400}) + self.assertIn("document", response.data) + + def _run_schedule_valid(self, br_id): + metadata = self._get_br_metadata(br_id, "valid") + response = self.api_post( + "/placement/api/placement/", + user=self.officer, + data={ + "company_name": "Schedule Corp", + "title": "Schedule Corp", + "placement_type": "PLACEMENT", + "ctc": "12.50", + "description": "Campus drive", + "placement_date": (timezone.now().date() + datetime.timedelta(days=3)).isoformat(), + "schedule_at": "2026-04-15 10:30", + "location": "Auditorium", + }, + format="multipart", + ) + self._assert_metadata_and_status(metadata, "Valid", response, {201}) + self.assertEqual(response.data["company_name"], "Schedule Corp") + + def _run_schedule_invalid(self, br_id): + metadata = self._get_br_metadata(br_id, "invalid") + response = self.api_post( + "/placement/api/placement/", + user=self.officer, + data={ + "company_name": "Past Schedule Corp", + "placement_type": "PLACEMENT", + "placement_date": (timezone.now().date() - datetime.timedelta(days=1)).isoformat(), + "schedule_at": "2026-04-01 10:30", + }, + format="multipart", + ) + self._assert_metadata_and_status(metadata, "Invalid", response, {400}) + self.assertIn("placement_date", response.data) + + def _run_apply_eligible_valid(self, br_id): + metadata = self._get_br_metadata(br_id, "valid") + student = self._make_profile_complete(self.student_user) + schedule = self._create_schedule(company_name="Eligibility Rule Corp") + response = self.api_post( + "/placement/api/apply-for-placement/", + user=self.student_user, + data={"jobId": schedule.id, "responses": []}, + ) + self._assert_metadata_and_status(metadata, "Valid", response, {200}) + self.assertTrue(PlacementApplication.objects.filter(schedule=schedule, student=student).exists()) + + def _run_apply_eligible_invalid(self, br_id): + metadata = self._get_br_metadata(br_id, "invalid") + self._make_profile_complete(self.student_user) + schedule = self._create_schedule(company_name="Blocked Eligibility Corp") + schedule.branch = "ECE" + schedule.save(update_fields=["branch"]) + response = self.api_post( + "/placement/api/apply-for-placement/", + user=self.student_user, + data={"jobId": schedule.id, "responses": []}, + ) + self._assert_metadata_and_status(metadata, "Invalid", response, {403}) + self.assertIn("not eligible", response.data["detail"]) + + def _run_offer_deadline_valid(self, br_id): + metadata = self._get_br_metadata(br_id, "valid") + student = self._get_student(self.student_user) + schedule = self._create_schedule(company_name="Offer Deadline Corp") + offer = PlacementStatus.objects.create( + notify_id=schedule.notify_id, + unique_id=student, + invitation="PENDING", + timestamp=timezone.now(), + no_of_days=2, + ) + response = self.api_post( + f"/placement/api/offer/{offer.id}/respond/", + user=self.student_user, + data={"action": "ACCEPTED"}, + ) + self._assert_metadata_and_status(metadata, "Valid", response, {200}) + offer.refresh_from_db() + self.assertEqual(offer.invitation, "ACCEPTED") + + def _run_offer_deadline_invalid(self, br_id): + metadata = self._get_br_metadata(br_id, "invalid") + student = self._get_student(self.student_user) + schedule = self._create_schedule(company_name="Expired Offer Corp") + offer = PlacementStatus.objects.create( + notify_id=schedule.notify_id, + unique_id=student, + invitation="PENDING", + no_of_days=1, + ) + PlacementStatus.objects.filter(pk=offer.pk).update(timestamp=timezone.now() - datetime.timedelta(days=3)) + response = self.api_post( + f"/placement/api/offer/{offer.id}/respond/", + user=self.student_user, + data={"action": "ACCEPTED"}, + ) + self._assert_metadata_and_status(metadata, "Invalid", response, {403}) + self.assertIn("expired", response.data["detail"]) + + def _run_duplicate_valid(self, br_id): + self._run_apply_eligible_valid(br_id) + + def _run_duplicate_invalid(self, br_id): + metadata = self._get_br_metadata(br_id, "invalid") + self._make_profile_complete(self.student_user) + schedule = self._create_schedule(company_name="Duplicate Rule Corp") + self.api_post( + "/placement/api/apply-for-placement/", + user=self.student_user, + data={"jobId": schedule.id, "responses": []}, + ) + response = self.api_post( + "/placement/api/apply-for-placement/", + user=self.student_user, + data={"jobId": schedule.id, "responses": []}, + ) + self._assert_metadata_and_status(metadata, "Invalid", response, {409}) + self.assertIn("already applied", response.data["detail"]) + + def _run_application_update_valid(self, br_id): + metadata = self._get_br_metadata(br_id, "valid") + self._create_officer_designation() + _, _, application = self._create_submitted_application(company_name="Status Update Corp") + response = self.api_put( + f"/placement/api/application-detail/{application.id}/", + user=self.officer, + data={"status": "shortlisted", "remarks": "Shortlisted"}, + ) + self._assert_metadata_and_status(metadata, "Valid", response, {200}) + application.refresh_from_db() + self.assertEqual(application.status, "shortlisted") + + def _run_application_update_invalid(self, br_id): + metadata = self._get_br_metadata(br_id, "invalid") + _, _, application = self._create_submitted_application(company_name="Blocked Update Corp") + response = self.api_put( + f"/placement/api/application-detail/{application.id}/", + user=self.student_user, + data={"status": "shortlisted", "remarks": "Student cannot update"}, + ) + self._assert_metadata_and_status(metadata, "Invalid", response, {403}) + + @override_settings(PLACEMENT_MAX_ACTIVE_APPLICATIONS=1) + def _run_application_limit_valid(self, br_id): + self._run_apply_eligible_valid(br_id) + + @override_settings(PLACEMENT_MAX_ACTIVE_APPLICATIONS=1) + def _run_application_limit_invalid(self, br_id): + metadata = self._get_br_metadata(br_id, "invalid") + self._make_profile_complete(self.student_user) + first_schedule = self._create_schedule(company_name="Limit One Corp") + second_schedule = self._create_schedule(company_name="Limit Two Corp") + first_response = self.api_post( + "/placement/api/apply-for-placement/", + user=self.student_user, + data={"jobId": first_schedule.id, "responses": []}, + ) + self.assertEqual(first_response.status_code, 200) + response = self.api_post( + "/placement/api/apply-for-placement/", + user=self.student_user, + data={"jobId": second_schedule.id, "responses": []}, + ) + self._assert_metadata_and_status(metadata, "Invalid", response, {403}) + self.assertIn("active applications", response.data["detail"]) + + def _run_interview_schedule_valid(self, br_id): + metadata = self._get_br_metadata(br_id, "valid") + self._create_officer_designation() + _, _, application = self._create_submitted_application(company_name="Interview Rule Corp") + scheduled_at = (timezone.now() + datetime.timedelta(days=1)).strftime("%Y-%m-%d %H:%M") + end_datetime = (timezone.now() + datetime.timedelta(days=1, hours=1)).strftime("%Y-%m-%d %H:%M") + response = self.api_post( + f"/placement/api/application-detail/{application.id}/interview/", + user=self.officer, + data={ + "scheduled_at": scheduled_at, + "end_datetime": end_datetime, + "round_no": 1, + "title": "Technical Round", + "remarks": "Interview scheduled", + }, + ) + self._assert_metadata_and_status(metadata, "Valid", response, {201}) + self.assertEqual(response.data["round_no"], 1) + + def _run_interview_schedule_invalid(self, br_id): + metadata = self._get_br_metadata(br_id, "invalid") + self._create_officer_designation() + _, _, application = self._create_submitted_application(company_name="Interview Error Corp") + response = self.api_post( + f"/placement/api/application-detail/{application.id}/interview/", + user=self.officer, + data={"round_no": 1, "title": "Missing time"}, + ) + self._assert_metadata_and_status(metadata, "Invalid", response, {400}) + self.assertIn("scheduled_at", response.data) + + def _run_application_tracking_valid(self, br_id): + metadata = self._get_br_metadata(br_id, "valid") + student, _, application = self._create_submitted_application(company_name="Tracking Corp") + PlacementInterviewSchedule.objects.create( + application=application, + round_no=1, + title="Round 1", + scheduled_at=timezone.now() + datetime.timedelta(days=1), + end_datetime=timezone.now() + datetime.timedelta(days=1, hours=1), + remarks="Scheduled round", + ) + response = self.api_get("/placement/api/my-applications/", user=self.student_user) + self._assert_metadata_and_status(metadata, "Valid", response, {200}) + self.assertEqual(response.data["applications"][0]["company_name"], "Tracking Corp") + self.assertIsNotNone(response.data["applications"][0]["next_interview"]) + self.assertEqual(student.id.user, self.student_user) + + def _run_application_tracking_invalid(self, br_id): + metadata = self._get_br_metadata(br_id, "invalid") + response = self.api_get("/placement/api/my-applications/", user=self.student_user) + self._assert_metadata_and_status(metadata, "Invalid", response, {200}) + self.assertEqual(response.data["applications"], []) + + def _run_offer_management_valid(self, br_id): + metadata = self._get_br_metadata(br_id, "valid") + student = self._get_student(self.student_user) + first_schedule = self._create_schedule(company_name="Offer Accept Corp") + offer = PlacementStatus.objects.create( + notify_id=first_schedule.notify_id, + unique_id=student, + invitation="PENDING", + timestamp=timezone.now(), + no_of_days=2, + ) + response = self.api_post( + f"/placement/api/offer/{offer.id}/respond/", + user=self.student_user, + data={"action": "ACCEPTED"}, + ) + self._assert_metadata_and_status(metadata, "Valid", response, {200}) + offer.refresh_from_db() + self.assertEqual(offer.invitation, "ACCEPTED") + + def _run_offer_management_invalid(self, br_id): + metadata = self._get_br_metadata(br_id, "invalid") + student = self._get_student(self.student_user) + existing_schedule = self._create_schedule(company_name="Accepted Offer Corp") + PlacementStatus.objects.create( + notify_id=existing_schedule.notify_id, + unique_id=student, + invitation="ACCEPTED", + timestamp=timezone.now(), + no_of_days=2, + ) + pending_schedule = self._create_schedule(company_name="Blocked Offer Corp") + pending_offer = PlacementStatus.objects.create( + notify_id=pending_schedule.notify_id, + unique_id=student, + invitation="PENDING", + timestamp=timezone.now(), + no_of_days=2, + ) + response = self.api_post( + f"/placement/api/offer/{pending_offer.id}/respond/", + user=self.student_user, + data={"action": "ACCEPTED"}, + ) + self._assert_metadata_and_status(metadata, "Invalid", response, {409}) + self.assertIn("accepted offer", response.data["detail"]) + + def _run_notification_valid(self, br_id): + metadata = self._get_br_metadata(br_id, "valid") + response = self.api_post( + "/placement/api/send-notification/", + user=self.officer, + data={"sendTo": "All", "description": "Placement update"}, + ) + self._assert_metadata_and_status(metadata, "Valid", response, {200}) + self.assertEqual(response.data["message"], "Notification sent successfully.") + + def _run_notification_invalid(self, br_id): + metadata = self._get_br_metadata(br_id, "invalid") + response = self.api_post( + "/placement/api/send-notification/", + user=self.officer, + data={"sendTo": "Specific", "recipient": "unknown-user", "description": "Placement update"}, + ) + self._assert_metadata_and_status(metadata, "Invalid", response, {404}) + self.assertIn("recipient", response.data) + + def _run_reports_valid(self, br_id): + metadata = self._get_br_metadata(br_id, "valid") + self._create_officer_designation() + response = self.api_get("/placement/api/reports/", user=self.officer) + self._assert_metadata_and_status(metadata, "Valid", response, {200}) + self.assertIn("templates", response.data) + + def _run_reports_invalid(self, br_id): + metadata = self._get_br_metadata(br_id, "invalid") + response = self.api_get("/placement/api/reports/", user=self.student_user) + self._assert_metadata_and_status(metadata, "Invalid", response, {403}) + self.assertIn("Only TPO and chairman users", response.data["detail"]) + + def _run_auth_required_valid(self, br_id): + metadata = self._get_br_metadata(br_id, "valid") + response = self.api_get("/placement/api/profile/", user=self.student_user) + self._assert_metadata_and_status(metadata, "Valid", response, {200}) + self.assertIn("profile", response.data) + + def _run_auth_required_invalid(self, br_id): + metadata = self._get_br_metadata(br_id, "invalid") + response = self.api_get("/placement/api/profile/") + self._assert_metadata_and_status(metadata, "Invalid", response, {401, 403}) + + def _run_statistics_valid(self, br_id): + metadata = self._get_br_metadata(br_id, "valid") + student_one = self._get_student(self.student_user) + student_two = self._get_student(self.other_student_user) + record_one = PlacementRecord.objects.create(placement_type="PLACEMENT", name="Acme", ctc="18.00", year=2026) + record_two = PlacementRecord.objects.create(placement_type="PLACEMENT", name="Beta", ctc="8.00", year=2025) + StudentPlacement.objects.get_or_create(unique_id=student_one) + StudentPlacement.objects.get_or_create(unique_id=student_two) + student_one.studentrecord_set.create(record_id=record_one) + student_two.studentrecord_set.create(record_id=record_two) + response = self.api_get("/placement/api/statistics/", user=self.officer, data={"aggregate_by": "department"}) + self._assert_metadata_and_status(metadata, "Valid", response, {200}) + self.assertTrue(len(response.data) >= 1) + + def _run_statistics_invalid(self, br_id): + metadata = self._get_br_metadata(br_id, "invalid") + response = self.api_get("/placement/api/statistics/") + self._assert_metadata_and_status(metadata, "Invalid", response, {401, 403}) + + def _run_report_schedule_valid(self, br_id): + metadata = self._get_br_metadata(br_id, "valid") + self._create_officer_designation() + response = self.api_post( + "/placement/api/report-schedules/", + user=self.officer, + data={ + "name": "Weekly Report", + "report_type": "custom", + "frequency": "weekly", + "export_format": "excel", + "recipients": ["officer@example.com"], + }, + ) + self._assert_metadata_and_status(metadata, "Valid", response, {201}) + self.assertEqual(response.data["name"], "Weekly Report") + + def _run_report_schedule_invalid(self, br_id): + metadata = self._get_br_metadata(br_id, "invalid") + response = self.api_post( + "/placement/api/report-schedules/", + user=self.student_user, + data={"name": "Blocked Report"}, + ) + self._assert_metadata_and_status(metadata, "Invalid", response, {403}) + self.assertIn("Only TPO and chairman users", response.data["detail"]) + + def _run_registration_valid(self, br_id): + metadata = self._get_br_metadata(br_id, "valid") + response = self.api_post( + "/placement/api/registration/", + user=self.officer, + data={ + "companyName": "New Company", + "description": "Placement partner", + "address": "Hyderabad", + "website": "https://company.example.com", + }, + ) + self._assert_metadata_and_status(metadata, "Valid", response, {200}) + self.assertEqual(response.data["companyName"], "New Company") + + def _run_registration_invalid(self, br_id): + metadata = self._get_br_metadata(br_id, "invalid") + response = self.api_post( + "/placement/api/registration/", + data={"companyName": "No Auth Company"}, + ) + self._assert_metadata_and_status(metadata, "Invalid", response, {401, 403}) + + def _run_policy_management_valid(self, br_id): + metadata = self._get_br_metadata(br_id, "valid") + self._create_officer_designation(name="placement chairman") + response = self.api_post( + "/placement/api/policies/", + user=self.officer, + data={ + "title": "One Offer Rule", + "description": "Students may hold only one accepted placement offer at a time.", + }, + ) + self._assert_metadata_and_status(metadata, "Valid", response, {201}) + self.assertEqual(response.data["title"], "One Offer Rule") + self.assertTrue(PlacementPolicy.objects.filter(title="One Offer Rule").exists()) + + def _run_policy_management_invalid(self, br_id): + metadata = self._get_br_metadata(br_id, "invalid") + response = self.api_get("/placement/api/policies/", user=self.student_user) + self._assert_metadata_and_status(metadata, "Invalid", response, {403}) + self.assertIn("Only placement chairman users", response.data["detail"]) + + def _run_alumni_profile_valid(self, br_id): + metadata = self._get_br_metadata(br_id, "valid") + alumni_user = self._create_alumni_like_user(username="alumni_valid") + response = self.api_post( + "/placement/api/alumni/profile/", + user=alumni_user, + data={"graduation_year": 2020, "degree": "B.Tech", "current_company": "Alumni Corp"}, + ) + self._assert_metadata_and_status(metadata, "Valid", response, {201}) + self.assertEqual(response.data["graduation_year"], 2020) + + def _run_alumni_profile_invalid(self, br_id): + metadata = self._get_br_metadata(br_id, "invalid") + alumni_user = self._create_alumni_like_user(username="alumni_pending") + create_response = self.api_post( + "/placement/api/alumni/profile/", + user=alumni_user, + data={"graduation_year": 2021, "degree": "B.Tech"}, + ) + self.assertEqual(create_response.status_code, 201) + response = self.api_put( + "/placement/api/alumni/profile/", + user=alumni_user, + data={"degree": "M.Tech"}, + ) + self._assert_metadata_and_status(metadata, "Invalid", response, {403}) + self.assertIn("awaiting approval", response.data["detail"]) + + def _create_alumni_like_user(self, *, username): + user = self._create_student_user( + roll_no=f"AL{username[:6]}", + username=username, + department=self.department_cse, + ) + extra = user.extrainfo + extra.user_type = "faculty" + extra.save(update_fields=["user_type"]) + Student.objects.filter(id=extra).delete() + return user + + def _run_appeal_create_valid(self, br_id): + metadata = self._get_br_metadata(br_id, "valid") + _, _, _, placement_status = self._create_rejected_appeal_context() + response = self.api_post( + "/placement/api/placement-appeals/", + user=self.student_user, + data={"placement_status": placement_status.id, "reason": "Please review the rejection"}, + ) + self._assert_metadata_and_status(metadata, "Valid", response, {201}) + self.assertEqual(response.data["status"], "pending") + + def _run_appeal_create_invalid(self, br_id): + metadata = self._get_br_metadata(br_id, "invalid") + _, _, _, placement_status = self._create_rejected_appeal_context() + response = self.api_post( + "/placement/api/placement-appeals/", + user=self.student_user, + data={"placement_status": placement_status.id, "reason": ""}, + ) + self._assert_metadata_and_status(metadata, "Invalid", response, {400}) + self.assertIn("reason", response.data) + + def _run_appeal_review_valid(self, br_id): + metadata = self._get_br_metadata(br_id, "valid") + self._create_officer_designation() + _, _, _, placement_status = self._create_rejected_appeal_context() + create_response = self.api_post( + "/placement/api/placement-appeals/", + user=self.student_user, + data={"placement_status": placement_status.id, "reason": "Please review"}, + ) + self.assertEqual(create_response.status_code, 201) + appeal = PlacementAppeal.objects.get(pk=create_response.data["id"]) + response = self.api_put( + f"/placement/api/placement-appeals/{appeal.id}/", + user=self.officer, + data={"status": "reviewed", "response": "Reviewed by TPO"}, + ) + self._assert_metadata_and_status(metadata, "Valid", response, {200}) + self.assertEqual(response.data["status"], "reviewed") + + def _run_appeal_review_invalid(self, br_id): + metadata = self._get_br_metadata(br_id, "invalid") + self._create_officer_designation() + _, _, _, placement_status = self._create_rejected_appeal_context() + create_response = self.api_post( + "/placement/api/placement-appeals/", + user=self.student_user, + data={"placement_status": placement_status.id, "reason": "Please review"}, + ) + self.assertEqual(create_response.status_code, 201) + appeal = PlacementAppeal.objects.get(pk=create_response.data["id"]) + response = self.api_put( + f"/placement/api/placement-appeals/{appeal.id}/", + user=self.student_user, + data={"status": "reviewed", "response": "Student cannot review"}, + ) + self._assert_metadata_and_status(metadata, "Invalid", response, {403, 404}) + + +_BR_HELPERS = { + "BR04": ("_run_profile_update_valid", "_run_profile_update_invalid"), + "BR05": ("_run_profile_completeness_valid", "_run_profile_completeness_invalid"), + "BR06": ("_run_schedule_valid", "_run_schedule_invalid"), + "BR07": ("_run_apply_eligible_valid", "_run_apply_eligible_invalid"), + "BR08": ("_run_offer_deadline_valid", "_run_offer_deadline_invalid"), + "BR09": ("_run_duplicate_valid", "_run_duplicate_invalid"), + "BR10": ("_run_application_update_valid", "_run_application_update_invalid"), + "BR11": ("_run_application_limit_valid", "_run_application_limit_invalid"), + "BR12": ("_run_schedule_valid", "_run_schedule_invalid"), + "BR13": ("_run_interview_schedule_valid", "_run_interview_schedule_invalid"), + "BR14": ("_run_application_update_valid", "_run_application_update_invalid"), + "BR15": ("_run_application_tracking_valid", "_run_application_tracking_invalid"), + "BR16": ("_run_offer_management_valid", "_run_offer_management_invalid"), + "BR17": ("_run_appeal_create_valid", "_run_appeal_create_invalid"), + "BR18": ("_run_notification_valid", "_run_notification_invalid"), + "BR19": ("_run_notification_valid", "_run_notification_invalid"), + "BR20": ("_run_notification_valid", "_run_auth_required_invalid"), + "BR21": ("_run_notification_valid", "_run_notification_invalid"), + "BR22": ("_run_reports_valid", "_run_reports_invalid"), + "BR23": ("_run_reports_valid", "_run_reports_invalid"), + "BR24": ("_run_auth_required_valid", "_run_auth_required_invalid"), + "BR25": ("_run_statistics_valid", "_run_statistics_invalid"), + "BR26": ("_run_report_schedule_valid", "_run_report_schedule_invalid"), + "BR27": ("_run_registration_valid", "_run_registration_invalid"), + "BR28": ("_run_profile_update_valid", "_run_profile_update_invalid"), + "BR29": ("_run_reports_valid", "_run_reports_invalid"), + "BR30": ("_run_reports_valid", "_run_reports_invalid"), + "BR31": ("_run_alumni_profile_valid", "_run_alumni_profile_invalid"), + "BR32": ("_run_statistics_valid", "_run_statistics_invalid"), + "BR33": ("_run_profile_completeness_valid", "_run_profile_completeness_invalid"), + "BR34": ("_run_notification_valid", "_run_notification_invalid"), + "BR35": ("_run_registration_valid", "_run_registration_invalid"), + "BR36": ("_run_application_update_valid", "_run_application_update_invalid"), + "BR37": ("_run_statistics_valid", "_run_statistics_invalid"), + "BR38": ("_run_appeal_review_valid", "_run_appeal_review_invalid"), + "BR39": ("_run_notification_valid", "_run_notification_invalid"), + "BR40": ("_run_appeal_review_valid", "_run_appeal_review_invalid"), +} + + +def _make_generated_br_test(br_id, helper_name): + def _test(self): + getattr(self, helper_name)(br_id) + + return _test + + +for _br_id, (_valid_helper, _invalid_helper) in _BR_HELPERS.items(): + setattr( + TestGeneratedBusinessRules, + f"test_{_br_id.lower()}_valid_rule_is_enforced", + _make_generated_br_test(_br_id, _valid_helper), + ) + setattr( + TestGeneratedBusinessRules, + f"test_{_br_id.lower()}_invalid_rule_is_rejected", + _make_generated_br_test(_br_id, _invalid_helper), + ) diff --git a/FusionIIIT/applications/placement_cell/tests/test_module.py b/FusionIIIT/applications/placement_cell/tests/test_module.py new file mode 100644 index 000000000..9b95a2ed7 --- /dev/null +++ b/FusionIIIT/applications/placement_cell/tests/test_module.py @@ -0,0 +1,1376 @@ +import datetime +from decimal import Decimal +from unittest.mock import Mock, patch + +from django.contrib.auth.models import User +from django.core.exceptions import ValidationError +from django.core.files.uploadedfile import SimpleUploadedFile +from django.http import HttpResponse +from django.test import SimpleTestCase, TestCase, override_settings +from django.utils import timezone +from notifications.models import Notification +from rest_framework.test import APIRequestFactory, force_authenticate + +from applications.academic_information.models import Student +from applications.globals.models import DepartmentInfo, Designation, ExtraInfo, HoldsDesignation +from applications.placement_cell import selectors, services +from applications.placement_cell.api.views import placement_api, placement_statistics_api +from applications.placement_cell.models import ( + AlumniConnection, + AlumniMentorshipSession, + AlumniProfile, + AlumniReferral, + CompanyDetails, + Education, + Has, + NotifyStudent, + PlacementApplication, + PlacementField, + PlacementApplicationTimeline, + PlacementInterviewSchedule, + PlacementNotificationPreference, + PlacementProfileAuditLog, + PlacementProfileDocument, + PlacementRecord, + PlacementSchedule, + PlacementStatus, + Project, + Skill, + StudentPlacement, +) + + +class PlacementCellSelectorTests(SimpleTestCase): + @patch("applications.placement_cell.selectors.CompanyDetails.objects.filter") + def test_get_company_names_by_prefix_returns_flat_list(self, mock_filter): + mock_filter.return_value.values_list.return_value = ["A", "B"] + + company_names = selectors.get_company_names_by_prefix("A") + + self.assertEqual(company_names, ["A", "B"]) + + +class PlacementCellServiceTests(SimpleTestCase): + @patch("applications.placement_cell.services.PlacementRecord.objects.create") + def test_create_placement_record_preserves_existing_fields(self, mock_create): + record = Mock() + mock_create.return_value = record + + result = services.create_placement_record( + placement_type="PLACEMENT", + student_name="Alice", + ctc="10.5", + year="2026", + test_type="", + test_score="", + ) + + mock_create.assert_called_once_with( + placement_type="PLACEMENT", + name="Alice", + ctc="10.5", + year="2026", + test_type="", + test_score="", + ) + record.save.assert_called_once_with() + self.assertIs(result, record) + + @patch("applications.placement_cell.services.PlacementSchedule.objects.create") + @patch("applications.placement_cell.services.NotifyStudent.objects.create") + @patch("applications.placement_cell.services.selectors.get_or_create_role") + @patch("applications.placement_cell.services.selectors.get_or_create_company_detail") + def test_create_schedule_and_notification_uses_selector_and_models( + self, + mock_company, + mock_role, + mock_notify_create, + mock_schedule_create, + ): + notify = Mock() + company = Mock() + role = Mock() + schedule = Mock() + mock_company.return_value = company + mock_role.return_value = role + mock_notify_create.return_value = notify + mock_schedule_create.return_value = schedule + + created_notify, created_schedule = services.create_schedule_and_notification( + placement_type="PLACEMENT", + company_name="Acme", + ctc="12", + description="desc", + placement_date=timezone.now().date() + datetime.timedelta(days=1), + location="Campus", + time="10:00", + role_name="SDE", + ) + + mock_company.assert_called_once_with("Acme") + mock_role.assert_called_once_with("SDE") + mock_schedule_create.assert_called_once_with( + notify_id=notify, + title="Acme", + description="desc", + placement_date=timezone.now().date() + datetime.timedelta(days=1), + attached_file=None, + role=role, + location="Campus", + time="10:00", + company=company, + ) + self.assertIs(created_notify, notify) + self.assertIs(created_schedule, schedule) + + +class PlacementCellApiTests(TestCase): + def setUp(self): + self.factory = APIRequestFactory() + self.department_cse = DepartmentInfo.objects.create(name="CSE") + self.department_ece = DepartmentInfo.objects.create(name="ECE") + self.student_designation = Designation.objects.create(name="student") + self.officer = User.objects.create_user( + username="officer", + password="password", + email="officer@example.com", + ) + self.student_user = self._create_student_user( + roll_no="2023001", + username="student1", + department=self.department_cse, + ) + self.other_student_user = self._create_student_user( + roll_no="2023002", + username="student2", + department=self.department_ece, + ) + + def _create_student_user(self, *, roll_no, username, department): + user = User.objects.create_user( + username=username, + password="password", + email=f"{username}@example.com", + first_name=username, + ) + extra = ExtraInfo.objects.get(user=user) + extra.user_type = "student" + extra.department = department + extra.save(update_fields=["user_type", "department"]) + Student.objects.create( + id=extra, + programme="B.Tech", + batch=2026, + cpi=8.5, + category="GEN", + ) + HoldsDesignation.objects.create( + user=user, + working=user, + designation=self.student_designation, + ) + return user + + def _create_officer_designation(self, name): + designation, _ = Designation.objects.get_or_create(name=name, full_name=name.title()) + HoldsDesignation.objects.get_or_create( + user=self.officer, + working=self.officer, + designation=designation, + ) + return designation + + def _get_student(self, user): + return Student.objects.get(id__user=user) + + def _create_alumni_user(self, *, username="alumni1"): + user = User.objects.create_user( + username=username, + password="password", + email=f"{username}@example.com", + first_name="Alumni", + last_name="User", + ) + extra = ExtraInfo.objects.get(user=user) + extra.user_type = "faculty" + extra.department = self.department_cse + extra.save(update_fields=["user_type", "department"]) + return user + + def _create_schedule(self, *, company_name, placement_type, placement_date): + notify = NotifyStudent.objects.create( + placement_type=placement_type, + company_name=company_name, + ctc=Decimal("10.00"), + description="desc", + ) + return PlacementSchedule.objects.create( + notify_id=notify, + title=company_name, + placement_date=placement_date, + location="Campus", + description="desc", + time=datetime.time(10, 0), + schedule_at=timezone.now(), + ) + + def _make_profile_complete(self, user): + student = self._get_student(user) + extra = student.id + extra.about_me = "About me" + extra.address = "Hostel" + extra.phone_no = 9876543210 + extra.save(update_fields=["about_me", "address", "phone_no"]) + Education.objects.create( + unique_id=student, + degree="B.Tech", + grade="90", + institute="IIITDMJ", + sdate=datetime.date(2020, 1, 1), + edate=datetime.date(2021, 1, 1), + ) + skill = Skill.objects.create(skill="Python") + Has.objects.create(skill_id=skill, unique_id=student, skill_rating=80) + Project.objects.create( + unique_id=student, + project_name="Capstone", + project_status="COMPLETED", + sdate=datetime.date(2020, 1, 1), + edate=datetime.date(2020, 2, 1), + ) + PlacementProfileDocument.objects.create( + student=student, + name="Resume", + document=SimpleUploadedFile("resume.pdf", b"pdf-content", content_type="application/pdf"), + ) + return student + + def test_update_invitation_status_is_immutable_after_first_response(self): + status = PlacementStatus.objects.create( + notify_id=NotifyStudent.objects.create( + placement_type="PLACEMENT", + company_name="Acme", + ctc=Decimal("10.00"), + ), + unique_id=self._get_student(self.student_user), + invitation="ACCEPTED", + ) + + updated = services.update_invitation_status(status.id, "REJECTED") + + status.refresh_from_db() + self.assertEqual(updated, 0) + self.assertEqual(status.invitation, "ACCEPTED") + + def test_education_clean_uses_model_dates_and_grade_limit(self): + education = Education( + unique_id=self._get_student(self.student_user), + degree="B.Tech", + grade="1234", + institute="IIITDMJ", + sdate=datetime.date(2024, 1, 1), + edate=datetime.date(2024, 1, 1), + ) + + with self.assertRaises(ValidationError) as exc: + education.full_clean() + + self.assertIn("grade", exc.exception.message_dict) + self.assertIn("sdate", exc.exception.message_dict) + + def test_skill_rating_rejects_values_above_100(self): + has_skill = Has( + unique_id=self._get_student(self.student_user), + skill_id_id=1, + skill_rating=101, + ) + + with self.assertRaises(ValidationError) as exc: + has_skill.full_clean(exclude=["skill_id"]) + + self.assertIn("skill_rating", exc.exception.message_dict) + + def test_placement_api_get_filters_past_schedules_and_shows_recruitment_events_to_students(self): + StudentPlacement.objects.create( + unique_id=self._get_student(self.student_user), + future_aspect="PLACEMENT", + ) + today = timezone.now().date() + expected_schedule = self._create_schedule( + company_name="Future Placement", + placement_type="PLACEMENT", + placement_date=today + datetime.timedelta(days=1), + ) + self._create_schedule( + company_name="Past Placement", + placement_type="PLACEMENT", + placement_date=today - datetime.timedelta(days=1), + ) + self._create_schedule( + company_name="Future PBI", + placement_type="PBI", + placement_date=today + datetime.timedelta(days=2), + ) + + request = self.factory.get("/placement/api/placement/") + force_authenticate(request, user=self.student_user) + + response = placement_api(request) + + self.assertEqual(response.status_code, 200) + self.assertEqual(len(response.data), 2) + returned_ids = {int(item["id"]) for item in response.data} + self.assertIn(expected_schedule.id, returned_ids) + self.assertIn( + PlacementSchedule.objects.get(notify_id__company_name="Future PBI").id, + returned_ids, + ) + + def test_placement_api_post_creates_company_when_missing(self): + request = self.factory.post( + "/placement/api/placement/", + { + "company_name": "New Co", + "title": "New Co", + "placement_type": "PLACEMENT", + "ctc": "12.50", + "description": "Campus drive", + "placement_date": (timezone.now().date() + datetime.timedelta(days=3)).isoformat(), + "schedule_at": "2026-04-15 10:30", + "location": "Auditorium", + }, + format="multipart", + ) + force_authenticate(request, user=self.officer) + + response = placement_api(request) + + self.assertEqual(response.status_code, 201) + self.assertTrue(CompanyDetails.objects.filter(company_name="New Co").exists()) + schedule = PlacementSchedule.objects.get(pk=response.data["id"]) + self.assertEqual(schedule.company.company_name, "New Co") + + def test_placement_api_post_rejects_past_dates(self): + request = self.factory.post( + "/placement/api/placement/", + { + "company_name": "Past Co", + "placement_type": "PLACEMENT", + "placement_date": (timezone.now().date() - datetime.timedelta(days=1)).isoformat(), + "schedule_at": "2026-04-01 10:30", + }, + format="multipart", + ) + force_authenticate(request, user=self.officer) + + response = placement_api(request) + + self.assertEqual(response.status_code, 400) + self.assertIn("placement_date", response.data) + + def test_placement_api_get_supports_company_role_location_and_package_filters(self): + self._create_officer_designation("placement officer") + engineer_role = selectors.get_or_create_role("Software Engineer") + analyst_role = selectors.get_or_create_role("Analyst") + + alpha_schedule = self._create_schedule( + company_name="Alpha Corp", + placement_type="PLACEMENT", + placement_date=timezone.now().date() + datetime.timedelta(days=5), + ) + alpha_schedule.location = "Bangalore" + alpha_schedule.role = engineer_role + alpha_schedule.notify_id.ctc = Decimal("22.00") + alpha_schedule.notify_id.save(update_fields=["ctc"]) + alpha_schedule.save(update_fields=["location", "role"]) + + beta_schedule = self._create_schedule( + company_name="Beta Labs", + placement_type="PLACEMENT", + placement_date=timezone.now().date() + datetime.timedelta(days=6), + ) + beta_schedule.location = "Delhi" + beta_schedule.role = analyst_role + beta_schedule.notify_id.ctc = Decimal("12.00") + beta_schedule.notify_id.save(update_fields=["ctc"]) + beta_schedule.save(update_fields=["location", "role"]) + + request = self.factory.get( + "/placement/api/placement/", + { + "company": "Alpha", + "role": "Engineer", + "location": "Bangalore", + "min_package": "20", + "max_package": "25", + }, + ) + force_authenticate(request, user=self.officer) + + response = placement_api(request) + + self.assertEqual(response.status_code, 200) + self.assertEqual(len(response.data), 1) + self.assertEqual(response.data[0]["company_name"], "Alpha Corp") + self.assertEqual(response.data[0]["role_st"], "Software Engineer") + + def test_placement_detail_api_get_returns_full_job_details(self): + student = self._get_student(self.student_user) + self._make_profile_complete(self.student_user) + company = CompanyDetails.objects.create( + company_name="Detail Corp", + description="Product engineering company", + address="Bangalore, India", + website="https://detail.example.com", + ) + role = selectors.get_or_create_role("Backend Engineer") + field = PlacementField.objects.create(name="github_profile", type="text", required=True) + schedule = self._create_schedule( + company_name="Detail Corp", + placement_type="PLACEMENT", + placement_date=timezone.now().date() + datetime.timedelta(days=3), + ) + schedule.company = company + schedule.role = role + schedule.location = "Bangalore" + schedule.description = "Build backend systems" + schedule.eligibility = "CPI >= 8" + schedule.branch = "CSE" + schedule.cpi = "8.0" + schedule.end_datetime = timezone.now() + datetime.timedelta(days=2) + schedule.save( + update_fields=[ + "company", + "role", + "location", + "description", + "eligibility", + "branch", + "cpi", + "end_datetime", + ], + ) + schedule.fields.add(field) + + request = self.factory.get(f"/placement/api/placement/{schedule.id}/") + force_authenticate(request, user=self.student_user) + + from applications.placement_cell.api.views import placement_detail_api + + response = placement_detail_api(request, schedule.id) + + self.assertEqual(response.status_code, 200) + self.assertEqual(response.data["company_name"], "Detail Corp") + self.assertEqual(response.data["role_st"], "Backend Engineer") + self.assertEqual(response.data["location"], "Bangalore") + self.assertEqual(response.data["company_details"]["website"], "https://detail.example.com") + self.assertEqual(len(response.data["application_fields"]), 1) + self.assertEqual(response.data["application_fields"][0]["name"], "github_profile") + self.assertTrue(response.data["eligible"]) + + def test_placement_statistics_api_supports_filters_and_department_aggregation(self): + student_one = self._get_student(self.student_user) + student_two = self._get_student(self.other_student_user) + acme_record = PlacementRecord.objects.create( + placement_type="PLACEMENT", + name="Acme", + ctc=Decimal("18.00"), + year=2026, + ) + beta_record = PlacementRecord.objects.create( + placement_type="PLACEMENT", + name="Beta", + ctc=Decimal("8.00"), + year=2025, + ) + StudentPlacement.objects.get_or_create(unique_id=student_one) + StudentPlacement.objects.get_or_create(unique_id=student_two) + student_one.studentrecord_set.create(record_id=acme_record) + student_two.studentrecord_set.create(record_id=beta_record) + + filtered_request = self.factory.get( + "/placement/api/statistics/", + {"company": "Acme", "ctc_min": "15", "year": "2026"}, + ) + force_authenticate(filtered_request, user=self.officer) + filtered_response = placement_statistics_api(filtered_request) + + self.assertEqual(filtered_response.status_code, 200) + self.assertEqual(len(filtered_response.data), 1) + self.assertEqual(filtered_response.data[0]["placement_name"], "Acme") + + aggregate_request = self.factory.get( + "/placement/api/statistics/", + {"aggregate_by": "department"}, + ) + force_authenticate(aggregate_request, user=self.officer) + aggregate_response = placement_statistics_api(aggregate_request) + + self.assertEqual(aggregate_response.status_code, 200) + self.assertEqual( + aggregate_response.data, + [ + {"department": "CSE", "count": 1}, + {"department": "ECE", "count": 1}, + ], + ) + + def test_apply_for_placement_rejects_incomplete_profile(self): + student = self._get_student(self.student_user) + schedule = self._create_schedule( + company_name="Acme", + placement_type="PLACEMENT", + placement_date=timezone.now().date() + datetime.timedelta(days=1), + ) + request = self.factory.post( + "/placement/api/apply-for-placement/", + {"jobId": schedule.id, "responses": []}, + format="json", + ) + force_authenticate(request, user=self.student_user) + + from applications.placement_cell.api.views import apply_for_placement_api + + response = apply_for_placement_api(request) + + self.assertEqual(response.status_code, 400) + self.assertIn("errors", response.data) + self.assertFalse(PlacementApplication.objects.filter(student=student).exists()) + + @override_settings(PLACEMENT_MAX_ACTIVE_APPLICATIONS=3) + def test_apply_for_placement_enforces_active_application_limit(self): + student = self._make_profile_complete(self.student_user) + schedule = self._create_schedule( + company_name="Limit Co", + placement_type="PLACEMENT", + placement_date=timezone.now().date() + datetime.timedelta(days=1), + ) + for index in range(3): + prior_schedule = self._create_schedule( + company_name=f"Company {index}", + placement_type="PLACEMENT", + placement_date=timezone.now().date() + datetime.timedelta(days=2 + index), + ) + PlacementApplication.objects.create(schedule=prior_schedule, student=student) + + request = self.factory.post( + "/placement/api/apply-for-placement/", + {"jobId": schedule.id, "responses": []}, + format="json", + ) + force_authenticate(request, user=self.student_user) + + from applications.placement_cell.api.views import apply_for_placement_api + + response = apply_for_placement_api(request) + + self.assertEqual(response.status_code, 403) + self.assertIn("3 active applications", response.data["detail"]) + + @override_settings(PLACEMENT_MAX_ACTIVE_APPLICATIONS=3) + def test_apply_for_placement_warns_when_student_is_near_limit(self): + student = self._make_profile_complete(self.student_user) + existing_schedule = self._create_schedule( + company_name="Existing Co", + placement_type="PLACEMENT", + placement_date=timezone.now().date() + datetime.timedelta(days=1), + ) + next_schedule = self._create_schedule( + company_name="Warning Co", + placement_type="PLACEMENT", + placement_date=timezone.now().date() + datetime.timedelta(days=2), + ) + PlacementApplication.objects.create( + schedule=existing_schedule, + student=student, + ) + + request = self.factory.post( + "/placement/api/apply-for-placement/", + {"jobId": next_schedule.id, "responses": []}, + format="json", + ) + force_authenticate(request, user=self.student_user) + + from applications.placement_cell.api.views import apply_for_placement_api + + response = apply_for_placement_api(request) + + self.assertEqual(response.status_code, 200) + self.assertEqual( + response.data["warning"], + "You have 1 active applications. The limit is 3.", + ) + + def test_withdraw_application_marks_state_and_notifies_student(self): + self._create_officer_designation("placement officer") + student = self._get_student(self.student_user) + schedule = self._create_schedule( + company_name="Withdraw Co", + placement_type="PLACEMENT", + placement_date=timezone.now().date() + datetime.timedelta(days=1), + ) + application = PlacementApplication.objects.create(schedule=schedule, student=student) + PlacementStatus.objects.create( + notify_id=schedule.notify_id, + unique_id=student, + invitation="ACCEPTED", + ) + request = self.factory.delete(f"/placement/api/apply-for-placement/{schedule.id}/") + force_authenticate(request, user=self.student_user) + + from applications.placement_cell.api.views import withdraw_application_api + + response = withdraw_application_api(request, schedule.id) + + self.assertEqual(response.status_code, 200) + application.refresh_from_db() + self.assertEqual(application.status, "withdrawn") + self.assertIsNotNone(application.withdrawn_at) + self.assertTrue( + PlacementProfileAuditLog.objects.filter( + student=student, + action="application_withdrawn", + ).exists(), + ) + + def test_application_detail_api_returns_timeline_and_interviews_for_tpo(self): + self._create_officer_designation("placement officer") + student = self._make_profile_complete(self.student_user) + schedule = self._create_schedule( + company_name="Detail Co", + placement_type="PLACEMENT", + placement_date=timezone.now().date() + datetime.timedelta(days=1), + ) + application = PlacementApplication.objects.create( + schedule=schedule, + student=student, + status="shortlisted", + remarks="Strong profile", + ) + PlacementApplicationTimeline.objects.create( + application=application, + stage="Shortlisted", + remarks="Moved to shortlist", + actor=self.officer, + ) + PlacementInterviewSchedule.objects.create( + application=application, + round_no=1, + title="Technical Interview", + scheduled_at=timezone.now() + datetime.timedelta(days=2), + mode="ONLINE", + meeting_link="https://meet.test/room", + remarks="Prepare DSA", + created_by=self.officer, + ) + request = self.factory.get(f"/placement/api/application-detail/{application.id}/") + force_authenticate(request, user=self.officer) + + from applications.placement_cell.api.views import application_detail_api + + response = application_detail_api(request, application.id) + + self.assertEqual(response.status_code, 200) + self.assertEqual(response.data["id"], application.id) + self.assertEqual(response.data["timeline"][1]["stage"], "Shortlisted") + self.assertEqual(response.data["interviews"][0]["title"], "Technical Interview") + self.assertEqual(response.data["student"]["branch"], "CSE") + self.assertEqual(response.data["student"]["passout_year"], 2026) + self.assertEqual(len(response.data["documents"]), 1) + self.assertEqual(response.data["resume"]["name"], "Resume") + + def test_application_interview_schedule_api_creates_interview_and_notifies_student(self): + self._create_officer_designation("placement officer") + student = self._get_student(self.student_user) + schedule = self._create_schedule( + company_name="Interview Co", + placement_type="PLACEMENT", + placement_date=timezone.now().date() + datetime.timedelta(days=1), + ) + application = PlacementApplication.objects.create(schedule=schedule, student=student) + request = self.factory.post( + f"/placement/api/application-detail/{application.id}/interview/", + { + "round_no": 1, + "title": "Technical Interview", + "scheduled_at": (timezone.now() + datetime.timedelta(days=2)).isoformat(), + "mode": "ONLINE", + "meeting_link": "https://meet.test/interview", + "remarks": "Join 10 minutes early", + }, + format="json", + ) + force_authenticate(request, user=self.officer) + + from applications.placement_cell.api.views import application_interview_schedule_api + + response = application_interview_schedule_api(request, application.id) + + self.assertEqual(response.status_code, 201) + application.refresh_from_db() + self.assertEqual(application.status, "interview_scheduled") + self.assertTrue( + PlacementInterviewSchedule.objects.filter(application=application, title="Technical Interview").exists() + ) + self.assertTrue( + Notification.objects.filter( + recipient=self.student_user, + verb__icontains="Interview scheduled", + module="Placement Cell", + ).exists() + ) + + def test_application_detail_api_selected_status_adds_record_to_placement_stats(self): + self._create_officer_designation("placement officer") + student = self._get_student(self.student_user) + schedule = self._create_schedule( + company_name="Selected Co", + placement_type="PLACEMENT", + placement_date=timezone.now().date() + datetime.timedelta(days=1), + ) + application = PlacementApplication.objects.create(schedule=schedule, student=student, status="offer_released") + request = self.factory.put( + f"/placement/api/application-detail/{application.id}/", + { + "status": "accept", + "remarks": "Final selected", + }, + format="json", + ) + force_authenticate(request, user=self.officer) + + from applications.placement_cell.api.views import application_detail_api + + response = application_detail_api(request, application.id) + + self.assertEqual(response.status_code, 200) + self.assertTrue( + StudentRecord.objects.filter( + unique_id=student, + record_id__name="Selected Co", + ).exists() + ) + self.assertTrue( + PlacementApplicationTimeline.objects.filter( + application=application, + stage="Selected", + ).exists() + ) + + def test_my_offers_api_includes_offer_released_applications(self): + student = self._get_student(self.student_user) + schedule = self._create_schedule( + company_name="Offer Co", + placement_type="PLACEMENT", + placement_date=timezone.now().date() + datetime.timedelta(days=5), + ) + PlacementApplication.objects.create( + schedule=schedule, + student=student, + status="offer_released", + ) + offer = PlacementStatus.objects.create( + notify_id=schedule.notify_id, + unique_id=student, + invitation="PENDING", + timestamp=timezone.now(), + no_of_days=2, + ) + request = self.factory.get("/placement/api/my-offers/") + force_authenticate(request, user=self.student_user) + + from applications.placement_cell.api.views import my_offers_api + + response = my_offers_api(request) + + self.assertEqual(response.status_code, 200) + self.assertEqual(len(response.data["offers"]), 1) + self.assertEqual(response.data["offers"][0]["id"], offer.id) + self.assertEqual(response.data["offers"][0]["company_name"], "Offer Co") + self.assertEqual(response.data["offers"][0]["status"], "PENDING") + + def test_profile_api_returns_documents_audit_logs_and_preferences(self): + student = self._get_student(self.student_user) + preference = PlacementNotificationPreference.objects.create( + student=student, + enable_portal=True, + enable_email=False, + enable_sms=True, + ) + PlacementProfileDocument.objects.create( + student=student, + name="Resume", + document=SimpleUploadedFile("resume.pdf", b"pdf-content", content_type="application/pdf"), + ) + PlacementProfileAuditLog.objects.create( + student=student, + actor=self.student_user, + action="profile_updated", + details={"about_me": "Updated"}, + ) + request = self.factory.get("/placement/api/profile/") + force_authenticate(request, user=self.student_user) + + from applications.placement_cell.api.views import placement_profile_api + + response = placement_profile_api(request) + + self.assertEqual(response.status_code, 200) + self.assertEqual(response.data["preferences"]["email"], preference.enable_email) + self.assertEqual(len(response.data["documents"]), 1) + self.assertEqual(len(response.data["audit_logs"]), 1) + self.assertEqual(response.data["profile"]["branch"], "CSE") + self.assertEqual(response.data["profile"]["passout_year"], 2026) + self.assertEqual(response.data["profile"]["cpi"], 8.5) + self.assertIn("eligibility_summary", response.data) + + def test_profile_payload_includes_skill_id_for_editing(self): + student = self._get_student(self.student_user) + skill = Skill.objects.create(skill="React") + has = Has.objects.create(skill_id=skill, unique_id=student, skill_rating=1) + request = self.factory.get("/api/profile/") + force_authenticate(request, user=self.student_user) + + from applications.globals.api.views import profile + + response = profile(request) + + self.assertEqual(response.status_code, 200) + self.assertEqual(response.data["skills"][0]["id"], has.id) + + def test_profile_api_returns_fallback_payload_when_extrainfo_is_missing(self): + user_without_profile = User.objects.create_user( + username="nologinprofile", + password="testpass123", + email="nologinprofile@example.com", + ) + request = self.factory.get("/api/profile/") + force_authenticate(request, user=user_without_profile) + + from applications.globals.api.views import profile + + response = profile(request) + + self.assertEqual(response.status_code, 200) + self.assertEqual(response.data["user"]["username"], "nologinprofile") + self.assertIsNone(response.data["profile"]) + self.assertEqual(response.data["current"], []) + + def test_profile_api_put_enforces_mandatory_fields(self): + request = self.factory.put( + "/placement/api/profile/", + { + "first_name": "", + "last_name": "", + "email": "invalid-email", + "phone_no": "123", + "address": "", + "about_me": "", + }, + format="multipart", + ) + force_authenticate(request, user=self.student_user) + + from applications.placement_cell.api.views import placement_profile_api + + response = placement_profile_api(request) + + self.assertEqual(response.status_code, 400) + self.assertIn("field_errors", response.data) + self.assertIn("first_name", response.data["field_errors"]) + self.assertIn("email", response.data["field_errors"]) + self.assertIn("phone_no", response.data["field_errors"]) + + def test_profile_api_put_updates_profile_and_creates_audit_log(self): + student = self._get_student(self.student_user) + request = self.factory.put( + "/placement/api/profile/", + { + "first_name": "Student", + "last_name": "One", + "email": "student1@example.com", + "phone_no": "9876543210", + "address": "Hostel A", + "about_me": "Ready for placements", + }, + format="multipart", + ) + force_authenticate(request, user=self.student_user) + + from applications.placement_cell.api.views import placement_profile_api + + response = placement_profile_api(request) + + self.assertEqual(response.status_code, 200) + student.refresh_from_db() + student.id.user.refresh_from_db() + self.assertEqual(student.id.user.first_name, "Student") + self.assertEqual(student.id.user.last_name, "One") + self.assertEqual(student.id.user.email, "student1@example.com") + self.assertEqual(student.id.address, "Hostel A") + self.assertEqual(student.id.about_me, "Ready for placements") + self.assertTrue( + PlacementProfileAuditLog.objects.filter( + student=student, + action="profile_updated", + ).exists(), + ) + + def test_profile_api_put_notifies_student_when_profile_is_updated(self): + request = self.factory.put( + "/placement/api/profile/", + { + "first_name": "Student", + "last_name": "One", + "email": "student1@example.com", + "phone_no": "9876543210", + "address": "Hostel A", + "about_me": "Ready for placements", + }, + format="multipart", + ) + force_authenticate(request, user=self.student_user) + + from applications.placement_cell.api.views import placement_profile_api + + response = placement_profile_api(request) + + self.assertEqual(response.status_code, 200) + notification = Notification.objects.filter( + recipient=self.student_user, + module="Placement Cell", + ).latest("timestamp") + self.assertEqual(notification.verb, "Your placement profile has been updated.") + + def test_profile_update_put_updates_existing_skill_without_duplicate_error(self): + student = self._get_student(self.student_user) + skill = Skill.objects.create(skill="Python") + has = Has.objects.create(skill_id=skill, unique_id=student, skill_rating=80) + request = self.factory.put( + "/api/profile_update/", + { + "skillsubmit": { + "id": has.id, + "skill_id": {"skill": "Python"}, + "skill_rating": 90, + }, + }, + format="json", + ) + force_authenticate(request, user=self.student_user) + + from applications.globals.api.views import profile_update + + response = profile_update(request) + + self.assertEqual(response.status_code, 200) + has.refresh_from_db() + self.assertEqual(has.skill_id.skill, "Python") + self.assertEqual(has.skill_rating, 90) + self.assertEqual(Has.objects.filter(unique_id=student, skill_id=skill).count(), 1) + + def test_profile_update_put_updates_existing_education_record(self): + student = self._get_student(self.student_user) + education = Education.objects.create( + unique_id=student, + degree="B.Tech", + stream="CSE", + institute="IIITDMJ", + grade="9.1", + sdate=datetime.date(2020, 1, 1), + edate=datetime.date(2024, 1, 1), + ) + request = self.factory.put( + "/api/profile_update/", + { + "education": { + "id": education.id, + "degree": "M.Tech", + "stream": "AI", + "institute": "IIITDMJ", + "grade": "9.5", + "sdate": "2021-01-01", + "edate": "2025-01-01", + }, + }, + format="json", + ) + force_authenticate(request, user=self.student_user) + + from applications.globals.api.views import profile_update + + response = profile_update(request) + + self.assertEqual(response.status_code, 200) + education.refresh_from_db() + self.assertEqual(education.degree, "M.Tech") + self.assertEqual(education.stream, "AI") + self.assertEqual(education.grade, "9.5") + self.assertEqual(education.unique_id, student) + + def test_profile_update_rejects_phone_number_that_is_not_10_digits(self): + request = self.factory.put( + "/api/profile_update/", + { + "profilesubmit": { + "about_me": "Ready for placements", + "date_of_birth": "2004-01-01", + "address": "Hostel A", + "phone_no": "12345", + }, + }, + format="json", + ) + force_authenticate(request, user=self.student_user) + + from applications.globals.api.views import profile_update + + response = profile_update(request) + + self.assertEqual(response.status_code, 400) + self.assertIn("phone_no", response.data) + self.assertIn("10 digits", response.data["phone_no"][0]) + + def test_profile_update_notification_is_returned_in_dashboard_feed(self): + request = self.factory.put( + "/api/profile_update/", + { + "profilesubmit": { + "about_me": "Ready for placements", + "date_of_birth": "2004-01-01", + "address": "Hostel A", + "phone_no": "9876543210", + }, + }, + format="json", + ) + force_authenticate(request, user=self.student_user) + + from applications.globals.api.views import NotificationList, profile_update + + update_response = profile_update(request) + self.assertEqual(update_response.status_code, 200) + + list_request = self.factory.get("/api/notification/") + force_authenticate(list_request, user=self.student_user) + list_response = NotificationList(list_request) + + self.assertEqual(list_response.status_code, 200) + self.assertTrue(len(list_response.data["notifications"]) > 0) + self.assertEqual( + list_response.data["notifications"][0]["verb"], + "Your profile has been updated.", + ) + + def test_profile_update_creates_dashboard_notification_with_expected_metadata(self): + request = self.factory.put( + "/api/profile_update/", + { + "profilesubmit": { + "about_me": "Updated bio", + "date_of_birth": "2004-01-01", + "address": "Hostel B", + "phone_no": "9876543210", + }, + }, + format="json", + ) + force_authenticate(request, user=self.student_user) + + from applications.globals.api.views import profile_update + + response = profile_update(request) + + self.assertEqual(response.status_code, 200) + notification = Notification.objects.filter(recipient=self.student_user).latest("timestamp") + self.assertEqual(notification.verb, "Your profile has been updated.") + self.assertEqual(notification.data.get("module"), "Placement Cell") + self.assertEqual(notification.data.get("url"), "/profile") + + def test_profile_update_notification_is_unread_by_default_and_can_be_marked_read(self): + update_request = self.factory.put( + "/api/profile_update/", + { + "profilesubmit": { + "about_me": "Updated bio", + "date_of_birth": "2004-01-01", + "address": "Hostel C", + "phone_no": "9876543210", + }, + }, + format="json", + ) + force_authenticate(update_request, user=self.student_user) + + from applications.globals.api.views import NotificationRead, profile_update + + update_response = profile_update(update_request) + self.assertEqual(update_response.status_code, 200) + + notification = Notification.objects.filter(recipient=self.student_user).latest("timestamp") + self.assertTrue(notification.unread) + + read_request = self.factory.post( + "/api/notificationread", + {"id": notification.id}, + format="json", + ) + force_authenticate(read_request, user=self.student_user) + read_response = NotificationRead(read_request) + + self.assertEqual(read_response.status_code, 200) + notification.refresh_from_db() + self.assertFalse(notification.unread) + + def test_profile_update_notification_list_is_scoped_to_current_user(self): + own_update_request = self.factory.put( + "/api/profile_update/", + { + "profilesubmit": { + "about_me": "Own update", + "date_of_birth": "2004-01-01", + "address": "Hostel D", + "phone_no": "9876543210", + }, + }, + format="json", + ) + force_authenticate(own_update_request, user=self.student_user) + + other_update_request = self.factory.put( + "/api/profile_update/", + { + "profilesubmit": { + "about_me": "Other update", + "date_of_birth": "2004-01-01", + "address": "Hostel E", + "phone_no": "9123456789", + }, + }, + format="json", + ) + force_authenticate(other_update_request, user=self.other_student_user) + + from applications.globals.api.views import NotificationList, profile_update + + own_update_response = profile_update(own_update_request) + other_update_response = profile_update(other_update_request) + self.assertEqual(own_update_response.status_code, 200) + self.assertEqual(other_update_response.status_code, 200) + + list_request = self.factory.get("/api/notification/") + force_authenticate(list_request, user=self.student_user) + list_response = NotificationList(list_request) + + self.assertEqual(list_response.status_code, 200) + self.assertEqual(len(list_response.data["notifications"]), 1) + self.assertEqual( + list_response.data["notifications"][0]["verb"], + "Your profile has been updated.", + ) + + def test_profile_api_post_accepts_png_document_upload(self): + student = self._get_student(self.student_user) + request = self.factory.post( + "/placement/api/profile/", + { + "name": "Offer Letter", + "document": SimpleUploadedFile( + "offer-letter.png", + b"png-content", + content_type="image/png", + ), + }, + format="multipart", + ) + force_authenticate(request, user=self.student_user) + + from applications.placement_cell.api.views import placement_profile_api + + response = placement_profile_api(request) + + self.assertEqual(response.status_code, 201) + self.assertTrue( + PlacementProfileDocument.objects.filter( + student=student, + name="Offer Letter", + ).exists(), + ) + + @patch("applications.placement_cell.api.views.render_to_pdf") + def test_generate_cv_api_creates_audit_log_on_download(self, mock_render_to_pdf): + student = self._get_student(self.student_user) + mock_render_to_pdf.return_value = HttpResponse( + b"%PDF-1.4", + content_type="application/pdf", + ) + request = self.factory.post( + "/placement/api/generate-cv/", + { + "achievements": True, + "education": True, + "skills": False, + "projects": True, + }, + format="json", + ) + force_authenticate(request, user=self.student_user) + + from applications.placement_cell.api.views import generate_cv_api + + response = generate_cv_api(request) + + self.assertEqual(response.status_code, 200) + self.assertTrue( + PlacementProfileAuditLog.objects.filter( + student=student, + actor=self.student_user, + action="resume_downloaded", + ).exists() + ) + audit_log = PlacementProfileAuditLog.objects.filter( + student=student, + action="resume_downloaded", + ).latest("id") + self.assertEqual(audit_log.details["filename"], "student_cv.pdf") + self.assertEqual( + audit_log.details["selected_sections"], + ["achievements", "education", "projects"], + ) + + def test_profile_api_post_rejects_documents_larger_than_5mb(self): + request = self.factory.post( + "/placement/api/profile/", + { + "document": SimpleUploadedFile( + "resume.pdf", + b"a" * (5 * 1024 * 1024 + 1), + content_type="application/pdf", + ), + }, + format="multipart", + ) + force_authenticate(request, user=self.student_user) + + from applications.placement_cell.api.views import placement_profile_api + + response = placement_profile_api(request) + + self.assertEqual(response.status_code, 400) + self.assertIn("document", response.data) + + def test_alumni_profile_submission_creates_pending_request(self): + alumni_user = self._create_alumni_user() + request = self.factory.post( + "/placement/api/alumni/profile/", + { + "graduation_year": 2020, + "degree": "B.Tech", + "current_company": "Acme", + "topics": "Mentoring, Placements", + "availability": "Weekends", + "bio": "Happy to help", + "mentorship_enabled": True, + }, + format="multipart", + ) + force_authenticate(request, user=alumni_user) + + from applications.placement_cell.api.views import alumni_profile_api + + response = alumni_profile_api(request) + + self.assertEqual(response.status_code, 201) + profile = AlumniProfile.objects.get(user=alumni_user) + self.assertEqual(profile.status, "pending") + self.assertTrue(profile.mentorship_enabled) + + def test_tpo_can_approve_alumni_and_assign_designation(self): + self._create_officer_designation("placement officer") + alumni_user = self._create_alumni_user(username="alumni2") + profile = AlumniProfile.objects.create( + user=alumni_user, + graduation_year=2021, + degree="B.Tech", + status="pending", + ) + request = self.factory.put( + f"/placement/api/alumni/verification/{profile.id}/", + {"status": "approved", "verification_notes": "Verified"}, + format="json", + ) + force_authenticate(request, user=self.officer) + + from applications.placement_cell.api.views import alumni_verification_detail_api + + response = alumni_verification_detail_api(request, profile.id) + + self.assertEqual(response.status_code, 200) + profile.refresh_from_db() + self.assertEqual(profile.status, "approved") + self.assertTrue( + HoldsDesignation.objects.filter( + working=alumni_user, + designation__name="alumni", + ).exists() + ) + + def test_approved_alumni_can_post_referral_and_student_can_connect_and_request_session(self): + alumni_user = self._create_alumni_user(username="alumni3") + alumni_profile = AlumniProfile.objects.create( + user=alumni_user, + graduation_year=2019, + degree="B.Tech", + status="approved", + mentorship_enabled=True, + topics="Career", + availability="Weekend", + ) + referral_request = self.factory.post( + "/placement/api/alumni/referrals/", + { + "title": "SDE Referral", + "company": "Acme", + "description": "Referral opportunity", + }, + format="json", + ) + force_authenticate(referral_request, user=alumni_user) + + from applications.placement_cell.api.views import ( + alumni_connections_api, + alumni_referrals_api, + alumni_sessions_api, + ) + + referral_response = alumni_referrals_api(referral_request) + self.assertEqual(referral_response.status_code, 201) + self.assertEqual(AlumniReferral.objects.count(), 1) + + student_user = self.student_user + connection_request = self.factory.post( + "/placement/api/alumni/connections/", + {"alumni_id": alumni_profile.id, "message": "Would love to connect"}, + format="json", + ) + force_authenticate(connection_request, user=student_user) + connection_response = alumni_connections_api(connection_request) + self.assertEqual(connection_response.status_code, 201) + self.assertEqual(AlumniConnection.objects.count(), 1) + + session_request = self.factory.post( + "/placement/api/alumni/sessions/", + { + "alumni_id": alumni_profile.id, + "topic": "Resume Review", + "agenda": "Need help with interviews", + "scheduled_at": timezone.now().isoformat(), + "mode": "online", + }, + format="json", + ) + force_authenticate(session_request, user=student_user) + session_response = alumni_sessions_api(session_request) + self.assertEqual(session_response.status_code, 201) + self.assertEqual(AlumniMentorshipSession.objects.count(), 1) diff --git a/FusionIIIT/applications/placement_cell/tests/test_use_cases.py b/FusionIIIT/applications/placement_cell/tests/test_use_cases.py new file mode 100644 index 000000000..7f199499f --- /dev/null +++ b/FusionIIIT/applications/placement_cell/tests/test_use_cases.py @@ -0,0 +1,265 @@ +import datetime + +from django.contrib.auth.models import User +from django.core.cache import cache +import yaml +from django.utils import timezone + +from applications.globals.models import Designation, HoldsDesignation +from applications.placement_cell.models import ( + PlacementApplication, + StudentPlacement, +) +from applications.placement_cell.tests.conftest import PlacementCellSpecBase + + +class TestUseCaseCatalogIntegrity(PlacementCellSpecBase): + def test_all_documented_use_cases_define_three_scenarios(self): + with (self.specs_dir / "use_cases.yaml").open("r", encoding="utf-8") as handle: + payload = yaml.safe_load(handle) or {} + + use_cases = payload.get("use_cases", []) + self.assertGreaterEqual(len(use_cases), 20) + + for use_case in use_cases: + self.assertIn("source_id", use_case) + self.assertIn("endpoint", use_case) + self.assertIn("method", use_case) + self.assertEqual( + sorted(use_case.get("scenarios", {}).keys()), + ["alternate_path", "exception_path", "happy_path"], + ) + + def test_use_case_test_ids_are_unique(self): + with (self.specs_dir / "use_cases.yaml").open("r", encoding="utf-8") as handle: + payload = yaml.safe_load(handle) or {} + + test_ids = [] + for use_case in payload.get("use_cases", []): + for scenario in use_case.get("scenarios", {}).values(): + test_ids.append(scenario["_test_id"]) + + self.assertEqual(len(test_ids), len(set(test_ids))) + + +class TestUC01_ProfileManagement(PlacementCellSpecBase): + def test_happy_path_student_can_update_profile(self): + metadata = self.load_spec("use_cases.yaml", "use_cases", "UC01")["scenarios"]["happy_path"] + self._make_profile_complete(self.student_user) + response = self.api_get("/placement/api/profile/", user=self.student_user) + + self.assertEqual(metadata["_scenario"], "Happy Path") + self.assertEqual(response.status_code, 200) + self.assertTrue(response.data["is_complete"]) + self.assertEqual(response.data["profile"]["address"], "Hostel A") + + def test_alternate_path_student_can_view_profile_payload(self): + metadata = self.load_spec("use_cases.yaml", "use_cases", "UC01")["scenarios"]["alternate_path"] + self._create_schedule(company_name="Profile Linked Corp") + response = self.api_get("/placement/api/profile/", user=self.student_user) + + self.assertEqual(metadata["_scenario"], "Alternate Path") + self.assertEqual(response.status_code, 200) + self.assertIn("profile", response.data) + self.assertIn("eligibility_summary", response.data) + self.assertEqual(response.data["eligibility_summary"]["eligible_count"], 1) + self.assertIn("documents", response.data["field_errors"]) + + def test_exception_path_invalid_profile_payload_is_rejected(self): + metadata = self.load_spec("use_cases.yaml", "use_cases", "UC01")["scenarios"]["exception_path"] + + response = self.api_put( + "/placement/api/profile/", + user=self.student_user, + data={ + "first_name": "", + "last_name": "", + "email": "invalid-email", + "phone_no": "123", + "address": "", + "about_me": "", + }, + format="multipart", + ) + + self.assertEqual(metadata["_scenario"], "Exception Path") + self.assertEqual(response.status_code, 400) + self.assertIn("field_errors", response.data) + self.assertIn("email", response.data["field_errors"]) + + +class TestUC02_BrowseAndSearchJobs(PlacementCellSpecBase): + def test_happy_path_student_sees_upcoming_opportunities(self): + metadata = self.load_spec("use_cases.yaml", "use_cases", "UC02")["scenarios"]["happy_path"] + StudentPlacement.objects.create(unique_id=self._get_student(self.student_user), future_aspect="PLACEMENT") + expected_schedule = self._create_schedule(company_name="Future Corp") + past_schedule = self._create_schedule( + company_name="Past Corp", + placement_date=timezone.now().date() - datetime.timedelta(days=1), + ) + + response = self.api_get("/placement/api/placement/", user=self.student_user) + + self.assertEqual(metadata["_scenario"], "Happy Path") + self.assertEqual(response.status_code, 200) + returned_ids = {int(row["id"]) for row in response.data} + self.assertIn(expected_schedule.id, returned_ids) + self.assertIn(past_schedule.id, returned_ids) + + def test_alternate_path_filters_opportunities(self): + metadata = self.load_spec("use_cases.yaml", "use_cases", "UC02")["scenarios"]["alternate_path"] + self._create_schedule(company_name="Alpha Corp", location="Bangalore", ctc="22.00") + self._create_schedule(company_name="Beta Labs", location="Delhi", ctc="12.00") + + response = self.api_get( + "/placement/api/placement/", + user=self.officer, + data={"company": "Alpha", "location": "Bangalore", "min_package": "20"}, + ) + + self.assertEqual(metadata["_scenario"], "Alternate Path") + self.assertEqual(response.status_code, 200) + self.assertEqual(len(response.data), 1) + self.assertEqual(response.data[0]["company_name"], "Alpha Corp") + + def test_tpo_can_see_jobs_posted_by_another_tpo(self): + self._create_officer_designation("placement officer") + other_tpo = User.objects.create_user( + username="other_tpo", + password="password", + email="other_tpo@example.com", + ) + self._create_officer_designation_for_user(other_tpo, "placement officer") + shared_schedule = self._create_schedule(company_name="Shared TPO Job") + + response = self.api_get("/placement/api/placement/", user=other_tpo) + + self.assertEqual(response.status_code, 200) + self.assertIn( + shared_schedule.id, + {int(row["id"]) for row in response.data}, + ) + + def test_tpo_can_see_past_job_postings(self): + self._create_officer_designation("placement officer") + past_schedule = self._create_schedule( + company_name="Past Admin Job", + placement_date=timezone.now().date() - datetime.timedelta(days=2), + ) + + response = self.api_get("/placement/api/placement/", user=self.officer) + + self.assertEqual(response.status_code, 200) + self.assertIn( + past_schedule.id, + {int(row["id"]) for row in response.data}, + ) + + def test_tpo_designation_takes_precedence_over_student_designation(self): + self._create_officer_designation("placement officer") + self._make_profile_complete(self.student_user) + HoldsDesignation.objects.create( + user=self.student_user, + working=self.student_user, + designation=Designation.objects.get(name="placement officer"), + ) + cache.set(f"last_selected_role_{self.student_user.id}", "placement officer", None) + past_schedule = self._create_schedule( + company_name="Mixed Role Admin Job", + placement_date=timezone.now().date() - datetime.timedelta(days=2), + ) + + response = self.api_get("/placement/api/placement/", user=self.student_user) + + self.assertEqual(response.status_code, 200) + self.assertIn( + past_schedule.id, + {int(row["id"]) for row in response.data}, + ) + + def test_student_selected_role_keeps_applied_state_for_mixed_role_user(self): + self._create_officer_designation("placement officer") + student = self._make_profile_complete(self.student_user) + HoldsDesignation.objects.create( + user=self.student_user, + working=self.student_user, + designation=Designation.objects.get(name="placement officer"), + ) + schedule = self._create_schedule(company_name="Applied State Job") + PlacementApplication.objects.create( + schedule=schedule, + student=student, + status="pending", + ) + cache.set(f"last_selected_role_{self.student_user.id}", "student", None) + + response = self.api_get("/placement/api/placement/", user=self.student_user) + + self.assertEqual(response.status_code, 200) + matched = next(item for item in response.data if int(item["id"]) == schedule.id) + self.assertTrue(matched["check"]) + + def test_exception_path_returns_empty_list_when_no_jobs_are_available(self): + metadata = self.load_spec("use_cases.yaml", "use_cases", "UC02")["scenarios"]["exception_path"] + response = self.api_get("/placement/api/placement/", user=self.student_user) + + self.assertEqual(metadata["_scenario"], "Exception Path") + self.assertEqual(response.status_code, 200) + self.assertEqual(response.data, []) + + +class TestUC03_ApplyForPlacementOpportunity(PlacementCellSpecBase): + def test_happy_path_student_can_submit_application(self): + metadata = self.load_spec("use_cases.yaml", "use_cases", "UC03")["scenarios"]["happy_path"] + student = self._make_profile_complete(self.student_user) + schedule = self._create_schedule(company_name="Apply Corp") + + response = self.api_post( + "/placement/api/apply-for-placement/", + user=self.student_user, + data={"jobId": schedule.id, "responses": []}, + ) + + self.assertEqual(metadata["_scenario"], "Happy Path") + self.assertEqual(response.status_code, 200) + self.assertEqual(response.data["message"], "Application submitted successfully.") + self.assertTrue(PlacementApplication.objects.filter(schedule=schedule, student=student).exists()) + + def test_alternate_path_ineligible_student_cannot_apply(self): + metadata = self.load_spec("use_cases.yaml", "use_cases", "UC03")["scenarios"]["alternate_path"] + self._make_profile_complete(self.student_user) + schedule = self._create_schedule(company_name="Eligibility Corp") + schedule.branch = "ECE" + schedule.save(update_fields=["branch"]) + + response = self.api_post( + "/placement/api/apply-for-placement/", + user=self.student_user, + data={"jobId": schedule.id, "responses": []}, + ) + + self.assertEqual(metadata["_scenario"], "Alternate Path") + self.assertEqual(response.status_code, 403) + self.assertIn("You are not eligible for this job posting.", response.data["detail"]) + self.assertIn("Branch requirement not met.", response.data["errors"]) + + def test_exception_path_duplicate_application_is_rejected(self): + metadata = self.load_spec("use_cases.yaml", "use_cases", "UC03")["scenarios"]["exception_path"] + self._make_profile_complete(self.student_user) + schedule = self._create_schedule(company_name="Duplicate Corp") + first_response = self.api_post( + "/placement/api/apply-for-placement/", + user=self.student_user, + data={"jobId": schedule.id, "responses": []}, + ) + + response = self.api_post( + "/placement/api/apply-for-placement/", + user=self.student_user, + data={"jobId": schedule.id, "responses": []}, + ) + + self.assertEqual(first_response.status_code, 200) + self.assertEqual(metadata["_scenario"], "Exception Path") + self.assertEqual(response.status_code, 409) + self.assertIn("already applied", response.data["detail"]) diff --git a/FusionIIIT/applications/placement_cell/tests/test_workflows.py b/FusionIIIT/applications/placement_cell/tests/test_workflows.py new file mode 100644 index 000000000..4a3b7ec54 --- /dev/null +++ b/FusionIIIT/applications/placement_cell/tests/test_workflows.py @@ -0,0 +1,427 @@ +import datetime + +import yaml + +from applications.academic_information.models import Student +from applications.placement_cell.models import ( + PlacementApplication, + PlacementInterviewSchedule, + PlacementRecord, + PlacementStatus, + StudentPlacement, +) +from applications.placement_cell.tests.conftest import PlacementCellSpecBase + + +class TestWorkflowCatalogIntegrity(PlacementCellSpecBase): + def test_all_documented_workflows_define_two_scenarios(self): + with (self.specs_dir / "workflows.yaml").open("r", encoding="utf-8") as handle: + payload = yaml.safe_load(handle) or {} + + workflows = payload.get("workflows", []) + self.assertGreaterEqual(len(workflows), 10) + + for workflow in workflows: + self.assertIn("source_section", workflow) + self.assertTrue(workflow.get("steps")) + self.assertEqual(sorted(workflow.get("scenarios", {}).keys()), ["end_to_end", "negative"]) + + def test_workflow_test_ids_are_unique(self): + with (self.specs_dir / "workflows.yaml").open("r", encoding="utf-8") as handle: + payload = yaml.safe_load(handle) or {} + + test_ids = [] + for workflow in payload.get("workflows", []): + for scenario in workflow.get("scenarios", {}).values(): + test_ids.append(scenario["_test_id"]) + + self.assertEqual(len(test_ids), len(set(test_ids))) + + +class TestGeneratedWorkflows(PlacementCellSpecBase): + def _wf_metadata(self, wf_id, scenario_key): + return self.load_spec("workflows.yaml", "workflows", wf_id)["scenarios"][scenario_key] + + def _assert_metadata(self, metadata, label): + self.assertEqual(metadata["_scenario"], label) + + def _create_submitted_application(self, *, company_name): + student = self._make_profile_complete(self.student_user) + schedule = self._create_schedule(company_name=company_name) + response = self.api_post( + "/placement/api/apply-for-placement/", + user=self.student_user, + data={"jobId": schedule.id, "responses": []}, + ) + self.assertEqual(response.status_code, 200) + application = PlacementApplication.objects.get(schedule=schedule, student=student) + return student, schedule, application + + def _create_alumni_like_user(self, *, username): + user = self._create_student_user( + roll_no=f"AL{username[:6]}", + username=username, + department=self.department_cse, + ) + extra = user.extrainfo + extra.user_type = "faculty" + extra.save(update_fields=["user_type"]) + Student.objects.filter(id=extra).delete() + return user + + def _seed_statistics_record(self, *, user, company_name, ctc="18.00", year=2026): + student = self._get_student(user) + StudentPlacement.objects.get_or_create(unique_id=student) + record = PlacementRecord.objects.create( + placement_type="PLACEMENT", + name=company_name, + ctc=ctc, + year=year, + ) + student.studentrecord_set.create(record_id=record) + return record + + def _wf01_end_to_end(self): + metadata = self._wf_metadata("WF01", "end_to_end") + self._create_officer_designation() + student, schedule, application = self._create_submitted_application(company_name="WF01 Corp") + + update_response = self.api_put( + f"/placement/api/application-detail/{application.id}/", + user=self.officer, + data={"status": "offer_released", "remarks": "Offer released"}, + ) + offers_response = self.api_get("/placement/api/my-offers/", user=self.student_user) + + self._assert_metadata(metadata, "End-to-End") + self.assertEqual(update_response.status_code, 200) + self.assertEqual(offers_response.status_code, 200) + self.assertEqual(len(offers_response.data["offers"]), 1) + self.assertEqual(offers_response.data["offers"][0]["company_name"], schedule.notify_id.company_name) + self.assertEqual(application.student, student) + + def _wf01_negative(self): + metadata = self._wf_metadata("WF01", "negative") + schedule = self._create_schedule(company_name="WF01 Blocked Corp") + response = self.api_post( + "/placement/api/apply-for-placement/", + user=self.student_user, + data={"jobId": schedule.id, "responses": []}, + ) + + self._assert_metadata(metadata, "Negative") + self.assertEqual(response.status_code, 400) + self.assertIn("Placement profile is incomplete", response.data["detail"]) + + def _wf02_end_to_end(self): + metadata = self._wf_metadata("WF02", "end_to_end") + schedule = self._create_schedule(company_name="WF02 Corp") + + self._make_profile_complete(self.student_user) + profile_response = self.api_get("/placement/api/profile/", user=self.student_user) + summary_response = self.api_get("/placement/api/profile/", user=self.student_user) + apply_response = self.api_post( + "/placement/api/apply-for-placement/", + user=self.student_user, + data={"jobId": schedule.id, "responses": []}, + ) + + self._assert_metadata(metadata, "End-to-End") + self.assertEqual(profile_response.status_code, 200) + self.assertEqual(summary_response.status_code, 200) + self.assertTrue(summary_response.data["is_complete"]) + self.assertEqual(apply_response.status_code, 200) + self.assertEqual(apply_response.data["message"], "Application submitted successfully.") + + def _wf02_negative(self): + metadata = self._wf_metadata("WF02", "negative") + schedule = self._create_schedule(company_name="WF02 Blocked Corp") + response = self.api_post( + "/placement/api/apply-for-placement/", + user=self.student_user, + data={"jobId": schedule.id, "responses": []}, + ) + + self._assert_metadata(metadata, "Negative") + self.assertEqual(response.status_code, 400) + self.assertIn("Placement profile is incomplete", response.data["detail"]) + + def _wf03_end_to_end(self): + metadata = self._wf_metadata("WF03", "end_to_end") + StudentPlacement.objects.create(unique_id=self._get_student(self.student_user), future_aspect="PLACEMENT") + create_response = self.api_post( + "/placement/api/placement/", + user=self.officer, + data={ + "company_name": "WF03 Corp", + "title": "WF03 Corp", + "placement_type": "PLACEMENT", + "ctc": "15.00", + "description": "Campus drive", + "placement_date": (datetime.date.today() + datetime.timedelta(days=3)).isoformat(), + "schedule_at": "2026-04-15 10:30", + "location": "Auditorium", + }, + format="multipart", + ) + list_response = self.api_get("/placement/api/placement/", user=self.student_user) + + self._assert_metadata(metadata, "End-to-End") + self.assertEqual(create_response.status_code, 201) + self.assertEqual(list_response.status_code, 200) + self.assertIn("WF03 Corp", [row["company_name"] for row in list_response.data]) + + def _wf03_negative(self): + metadata = self._wf_metadata("WF03", "negative") + response = self.api_post( + "/placement/api/placement/", + user=self.officer, + data={ + "company_name": "WF03 Past Corp", + "placement_type": "PLACEMENT", + "placement_date": (datetime.date.today() - datetime.timedelta(days=1)).isoformat(), + "schedule_at": "2026-04-01 10:30", + }, + format="multipart", + ) + + self._assert_metadata(metadata, "Negative") + self.assertEqual(response.status_code, 400) + self.assertIn("placement_date", response.data) + + def _wf04_end_to_end(self): + metadata = self._wf_metadata("WF04", "end_to_end") + self._create_officer_designation() + _, _, application = self._create_submitted_application(company_name="WF04 Corp") + + schedule_response = self.api_post( + f"/placement/api/application-detail/{application.id}/interview/", + user=self.officer, + data={ + "scheduled_at": (datetime.datetime.now() + datetime.timedelta(days=1)).strftime("%Y-%m-%d %H:%M"), + "end_datetime": (datetime.datetime.now() + datetime.timedelta(days=1, hours=1)).strftime("%Y-%m-%d %H:%M"), + "round_no": 1, + "title": "Technical Interview", + "remarks": "Interview scheduled", + }, + ) + applications_response = self.api_get("/placement/api/my-applications/", user=self.student_user) + + self._assert_metadata(metadata, "End-to-End") + self.assertEqual(schedule_response.status_code, 201) + self.assertEqual(applications_response.status_code, 200) + self.assertIsNotNone(applications_response.data["applications"][0]["next_interview"]) + self.assertEqual(applications_response.data["applications"][0]["next_interview"]["title"], "Technical Interview") + + def _wf04_negative(self): + metadata = self._wf_metadata("WF04", "negative") + self._create_officer_designation() + _, _, application = self._create_submitted_application(company_name="WF04 Invalid Corp") + response = self.api_post( + f"/placement/api/application-detail/{application.id}/interview/", + user=self.officer, + data={"round_no": 1, "title": "Missing datetime"}, + ) + + self._assert_metadata(metadata, "Negative") + self.assertEqual(response.status_code, 400) + self.assertIn("scheduled_at", response.data) + + def _wf05_end_to_end(self): + metadata = self._wf_metadata("WF05", "end_to_end") + self._create_officer_designation() + student, schedule, application = self._create_submitted_application(company_name="WF05 Corp") + apply_response = self.api_put( + f"/placement/api/application-detail/{application.id}/", + user=self.officer, + data={"status": "offer_released", "remarks": "Offer issued"}, + ) + offers_response = self.api_get("/placement/api/my-offers/", user=self.student_user) + + self._assert_metadata(metadata, "End-to-End") + self.assertEqual(apply_response.status_code, 200) + self.assertEqual(offers_response.status_code, 200) + self.assertEqual(len(offers_response.data["offers"]), 1) + self.assertEqual(offers_response.data["offers"][0]["company_name"], "WF05 Corp") + self.assertEqual(student.id.user, self.student_user) + self.assertEqual(schedule.notify_id.company_name, "WF05 Corp") + + def _wf05_negative(self): + metadata = self._wf_metadata("WF05", "negative") + student, schedule, _ = self._create_submitted_application(company_name="WF05 Pending Corp") + offers_response = self.api_get("/placement/api/my-offers/", user=self.student_user) + + self._assert_metadata(metadata, "Negative") + self.assertEqual(offers_response.status_code, 200) + self.assertEqual(offers_response.data["offers"], []) + self.assertEqual(student.id.user, self.student_user) + self.assertEqual(schedule.notify_id.company_name, "WF05 Pending Corp") + + def _wf06_end_to_end(self): + metadata = self._wf_metadata("WF06", "end_to_end") + create_response = self.api_post( + "/placement/api/registration/", + user=self.officer, + data={ + "companyName": "WF06 Company", + "description": "Placement partner", + "address": "Hyderabad", + "website": "https://wf06.example.com", + }, + ) + list_response = self.api_get("/placement/api/registration/", user=self.officer) + + self._assert_metadata(metadata, "End-to-End") + self.assertEqual(create_response.status_code, 200) + self.assertEqual(list_response.status_code, 200) + self.assertIn("WF06 Company", [row["companyName"] for row in list_response.data]) + + def _wf06_negative(self): + metadata = self._wf_metadata("WF06", "negative") + response = self.api_post( + "/placement/api/registration/", + data={"companyName": "Blocked WF06 Company"}, + ) + + self._assert_metadata(metadata, "Negative") + self.assertIn(response.status_code, (401, 403)) + + def _wf07_end_to_end(self): + metadata = self._wf_metadata("WF07", "end_to_end") + self._create_officer_designation() + alumni_user = self._create_alumni_like_user(username="wf07alumni") + + create_response = self.api_post( + "/placement/api/alumni/profile/", + user=alumni_user, + data={"graduation_year": 2020, "degree": "B.Tech", "current_company": "WF07 Corp"}, + ) + approve_response = self.api_put( + f"/placement/api/alumni/verification/{create_response.data['id']}/", + user=self.officer, + data={"status": "approved", "verification_notes": "Verified"}, + ) + profile_response = self.api_get("/placement/api/alumni/profile/", user=alumni_user) + + self._assert_metadata(metadata, "End-to-End") + self.assertEqual(create_response.status_code, 201) + self.assertEqual(approve_response.status_code, 200) + self.assertEqual(profile_response.status_code, 200) + self.assertTrue(profile_response.data["can_access"]) + + def _wf07_negative(self): + metadata = self._wf_metadata("WF07", "negative") + alumni_user = self._create_alumni_like_user(username="wf07pending") + create_response = self.api_post( + "/placement/api/alumni/profile/", + user=alumni_user, + data={"graduation_year": 2021, "degree": "B.Tech"}, + ) + update_response = self.api_put( + "/placement/api/alumni/profile/", + user=alumni_user, + data={"degree": "M.Tech"}, + ) + + self._assert_metadata(metadata, "Negative") + self.assertEqual(create_response.status_code, 201) + self.assertEqual(update_response.status_code, 403) + self.assertIn("awaiting approval", update_response.data["detail"]) + + def _wf08_end_to_end(self): + metadata = self._wf_metadata("WF08", "end_to_end") + response = self.api_post( + "/placement/api/send-notification/", + user=self.officer, + data={"sendTo": "All", "description": "WF08 notification"}, + ) + + self._assert_metadata(metadata, "End-to-End") + self.assertEqual(response.status_code, 200) + self.assertEqual(response.data["message"], "Notification sent successfully.") + + def _wf08_negative(self): + metadata = self._wf_metadata("WF08", "negative") + response = self.api_post( + "/placement/api/send-notification/", + user=self.officer, + data={"sendTo": "Specific", "recipient": "missing-user", "description": "WF08 notification"}, + ) + + self._assert_metadata(metadata, "Negative") + self.assertEqual(response.status_code, 404) + self.assertIn("recipient", response.data) + + def _wf09_end_to_end(self): + metadata = self._wf_metadata("WF09", "end_to_end") + StudentPlacement.objects.create(unique_id=self._get_student(self.student_user), future_aspect="PLACEMENT") + self._create_schedule(company_name="WF09 Corp") + response = self.api_get("/placement/api/placement/", user=self.student_user) + + self._assert_metadata(metadata, "End-to-End") + self.assertEqual(response.status_code, 200) + self.assertIn("WF09 Corp", [row["company_name"] for row in response.data]) + + def _wf09_negative(self): + metadata = self._wf_metadata("WF09", "negative") + response = self.api_get("/placement/api/placement/") + + self._assert_metadata(metadata, "Negative") + self.assertIn(response.status_code, (401, 403)) + + def _wf10_end_to_end(self): + metadata = self._wf_metadata("WF10", "end_to_end") + self._create_officer_designation() + self._seed_statistics_record(user=self.student_user, company_name="WF10 Corp") + response = self.api_get( + "/placement/api/reports/", + user=self.officer, + data={"report_type": "company"}, + ) + + self._assert_metadata(metadata, "End-to-End") + self.assertEqual(response.status_code, 200) + self.assertIn("rows", response.data) + self.assertEqual(response.data["rows"][0]["company"], "WF10 Corp") + + def _wf10_negative(self): + metadata = self._wf_metadata("WF10", "negative") + response = self.api_get("/placement/api/reports/", user=self.student_user) + + self._assert_metadata(metadata, "Negative") + self.assertEqual(response.status_code, 403) + self.assertIn("Only TPO and chairman users", response.data["detail"]) + + +_WF_HELPERS = { + "WF01": ("_wf01_end_to_end", "_wf01_negative"), + "WF02": ("_wf02_end_to_end", "_wf02_negative"), + "WF03": ("_wf03_end_to_end", "_wf03_negative"), + "WF04": ("_wf04_end_to_end", "_wf04_negative"), + "WF05": ("_wf05_end_to_end", "_wf05_negative"), + "WF06": ("_wf06_end_to_end", "_wf06_negative"), + "WF07": ("_wf07_end_to_end", "_wf07_negative"), + "WF08": ("_wf08_end_to_end", "_wf08_negative"), + "WF09": ("_wf09_end_to_end", "_wf09_negative"), + "WF10": ("_wf10_end_to_end", "_wf10_negative"), +} + + +def _make_generated_workflow_test(helper_name): + def _test(self): + getattr(self, helper_name)() + + return _test + + +for _wf_id, (_e2e_helper, _negative_helper) in _WF_HELPERS.items(): + setattr( + TestGeneratedWorkflows, + f"test_{_wf_id.lower()}_end_to_end_workflow", + _make_generated_workflow_test(_e2e_helper), + ) + setattr( + TestGeneratedWorkflows, + f"test_{_wf_id.lower()}_negative_workflow_path", + _make_generated_workflow_test(_negative_helper), + ) diff --git a/FusionIIIT/applications/placement_cell/urls.py b/FusionIIIT/applications/placement_cell/urls.py deleted file mode 100644 index 190638861..000000000 --- a/FusionIIIT/applications/placement_cell/urls.py +++ /dev/null @@ -1,29 +0,0 @@ -from django.conf.urls import url -from . import views - -app_name = 'placement' - -urlpatterns = [ - url(r'^$', views.placement, name='placement'), - url(r'^get_reference_list/$', views.get_reference_list, name='get_reference_list'), - url(r'^checking_roles/$', views.checking_roles, name='checking_roles'), - url(r'^companyname_dropdown/$', views.company_name_dropdown, name='companyname_dropdown'), - url(r'^student_records/invitation_status$', views.invitation_status, name='invitation_status'), - url(r'^student_records/delete_invitation_status$', views.delete_invitation_status, name='delete_invitation_status'), - url(r'^student_records/$', views.student_records, name='student_records'), - url(r'^manage_records/$', views.manage_records, name='manage_records'), - url(r'^statistics/$', views.placement_statistics, name='placement_statistics'), - - url(r'^delete_placement_statistics/$', views.delete_placement_statistics, name='delete_placement_statistics'), - url(r'^cv/(?P[a-zA-Z0-9\.]{1,20})/$', views.cv, name="cv"), - - - #added new url - url(r'^add_placement_schedule/$', views.add_placement_schedule, name='add_placement_schedule'), - url(r'^placement_schedule_save/$', views.placement_schedule_save, name='placement_schedule_save'), - url(r'^delete_placement_record/$', views.delete_placement_record, name='delete_placement_record'), - url(r'^add_placement_record/$', views.add_placement_record, name='add_placement_record'), - url(r'^placement_record_save/$', views.placement_record_save, name='placement_record_save'), - url(r'^add_placement_visit/$', views.add_placement_visit, name='add_placement_visit'), - url(r'^placement_visit_save/$', views.placement_visit_save, name='placement_visit_save'), -] diff --git a/FusionIIIT/applications/placement_cell/views.py b/FusionIIIT/applications/placement_cell/views.py deleted file mode 100644 index 25ecadebd..000000000 --- a/FusionIIIT/applications/placement_cell/views.py +++ /dev/null @@ -1,5810 +0,0 @@ -import os -import shutil -import datetime -import decimal -import zipfile -import xlwt -import logging - -from html import escape -from datetime import date -from io import BytesIO -from wsgiref.util import FileWrapper -from django.conf import settings -from django.contrib.auth.decorators import login_required -from django.contrib.auth.models import User -from django.contrib import messages -from django.core.cache import cache -from django.core.files.storage import FileSystemStorage -from django.core.paginator import Paginator -from django.db.models import Count, Q -from django.http import HttpResponse, JsonResponse -from django.shortcuts import get_object_or_404, redirect, render -from django.template.loader import get_template, render_to_string -from django.utils import timezone -from django.utils.encoding import smart_str -from xhtml2pdf import pisa -from django.core import serializers -from applications.academic_information.models import Student -from notification.views import placement_cell_notif -from applications.globals.models import (DepartmentInfo, ExtraInfo, - HoldsDesignation) -from applications.academic_information.models import Student -from .forms import (AddAchievement, AddChairmanVisit, AddCourse, AddEducation, - AddExperience, AddReference, AddPatent, AddProfile, AddProject, - AddPublication, AddSchedule, AddSkill, ManageHigherRecord, - ManagePbiRecord, ManagePlacementRecord, SearchHigherRecord, - SearchPbiRecord, SearchPlacementRecord, - SearchStudentRecord, SendInvite) - -from .models import (Achievement, ChairmanVisit, Course, Education, Experience, Conference, - Has, NotifyStudent, Patent, PlacementRecord, Extracurricular, Reference, - PlacementSchedule, PlacementStatus, Project, Publication, - Skill, StudentPlacement, StudentRecord, Role, CompanyDetails,) -''' - @variables: - user - logged in user - profile - variable for extrainfo - studentrecord - storing all fetched student record from database - years - yearwise record of student placement - records - all the record of placement record table - tcse - all record of cse - tece - all record of ece - tme - all record of me - tadd - all record of student - form respective form object - stuname - student name obtained from the form - ctc - salary offered obtained from the form - cname - company name obtained from the form - rollno - roll no of student obtained from the form - year - year of placement obtained from the form - s - extra info data of the student obtained from the form - p - placement data of the student obtained from the form - placementrecord - placement record of the student obtained from the form - pbirecord - pbi data of the student obtained from the form - test_type - type of higher study test obtained from the form - uname - name of universty obtained from the form - test_score - score in the test obtained from the form - higherrecord - higher study record of the student obtained from the form - current - current user on a particular designation - status - status of the sent invitation by placement cell regarding placement/pbi - institute - institute for previous education obtained from the form - degree - degree for previous education obtained from the form - grade - grade for previous education obtained from the form - stream - stream for previous education obtained from the form - sdate - start date for previous education obtained from the form - edate - end date for previous education obtained from the form - education_obj - object variable of Education table - about_me - about me data obtained from the form - age - age data obtained from the form - address - address obtained from the form - contact - contact obtained from the form - pic - picture obtained from the form - skill - skill of the user obtained from the form - skill_rating - rating of respective skill obtained from the form - has_obj - object variable of Has table - achievement - achievement of user obtained from the form - achievement_type - type of achievement obtained from the form - description - description of respective achievement obtained from the form - issuer - certifier of respective achievement obtained from the form - date_earned - date of the respective achievement obtained from the form - achievement_obj - object variable of Achievement table - publication_title - title of the publication obtained from the form - description - description of respective publication obtained from the form - publisher - publisher of respective publication obtained from the form - publication_date - date of respective publication obtained from the form - publication_obj - object variable of Publication table - patent_name - name of patent obtained from the form - description - description of respective patent obtained from the form - patent_office - office of respective patent obtained from the form - patent_date - date of respective patent obtained from the form - patent_obj - object variable of Patent table - course_name - name of the course obtained from the form - description description of respective course obtained from the form - license_no - license_no of respective course obtained from the form - sdate - start date of respective course obtained from the form - edate - end date of respective course obtained from the form - course_obj - object variable of Course table - project_name - name of project obtained from the form - project_status - status of respective project obtained from the form - summary - summery of the respective project obtained from the form - project_link - link of the respective project obtained from the form - sdate - start date of respective project obtained from the form - edate - end date of respective project obtained from the form - project_obj - object variable of Project table - title - title of any kind of experience obtained from the form - status - status of the respective experience obtained from the form - company - company from which respective experience is gained as obtained from the form - location - location of the respective experience obtained from the form - description - description of respective experience obtained from the form - sdate - start date of respective experience obtained from the form - edate - end date of respective experience obtained from the form - experience_obj - object variable of Experience table - context - to sent the relevant context for html rendering - company_name - name of visiting comapany obtained from the form - location -location of visiting company obtained from the form - description - description of respective company obtained from the form - visiting_date - visiting date of respective company obtained from the form - visit_obj -object variable of ChairmanVisit table - notify - object of NotifyStudent table - schedule - object variable of PlacementSchedule table - q1 - all data of Has table - q3 - all data of Student table - st - all data of Student table - spid - id of student to be debar - sr - record from StudentPlacement of student having id=spid - achievementcheck - checking for achievent to be shown in cv - educationcheck - checking for education to be shown in cv - publicationcheck - checking for publication to be shown in cv - patentcheck - checking for patent to be shown in cv - internshipcheck - checking for internship to be shown in cv - projectcheck - checking for project to be shown in cv - coursecheck - checking for course to be shown in cv - skillcheck - checking for skill to be shown in cv -''' - -logger = logging.getLogger('django.server') -@login_required -def placement__Statistics(request): - ''' - logic of the view shown under Placement Statistics tab - ''' - user = request.user - - - statistics_tab = 1 - strecord_tab=1 - delete_operation = 0 - pagination_placement = 0 - pagination_pbi = 0 - pagination_higher = 0 - is_disabled = 0 - paginator = '' - page_range = '' - officer_statistics_past_pbi_search = 0 - officer_statistics_past_higher_search = 0 - - profile = get_object_or_404(ExtraInfo, Q(user=user)) - studentrecord = StudentRecord.objects.select_related('unique_id','record_id').all() - - years = PlacementRecord.objects.filter(~Q(placement_type="HIGHER STUDIES")).values('year').annotate(Count('year')) - records = PlacementRecord.objects.values('name', 'year', 'ctc', 'placement_type').annotate(Count('name'), Count('year'), Count('placement_type'), Count('ctc')) - - - - - #working here to fetch all placement record - all_records=PlacementRecord.objects.all() - print(all_records) - - - - - - - invitecheck=0 - for r in records: - r['name__count'] = 0 - r['year__count'] = 0 - r['placement_type__count'] = 0 - tcse = dict() - tece = dict() - tme = dict() - tadd = dict() - for y in years: - tcse[y['year']] = 0 - tece[y['year']] = 0 - tme[y['year']] = 0 - for r in records: - if r['year'] == y['year']: - if r['placement_type'] != "HIGHER STUDIES": - for z in studentrecord: - if z.record_id.name == r['name'] and z.record_id.year == r['year'] and z.unique_id.id.department.name == "CSE": - tcse[y['year']] = tcse[y['year']]+1 - r['name__count'] = r['name__count']+1 - if z.record_id.name == r['name'] and z.record_id.year == r['year'] and z.unique_id.id.department.name == "ECE": - tece[y['year']] = tece[y['year']]+1 - r['year__count'] = r['year__count']+1 - if z.record_id.name == r['name'] and z.record_id.year == r['year'] and z.unique_id.id.department.name == "ME": - tme[y['year']] = tme[y['year']]+1 - r['placement_type__count'] = r['placement_type__count']+1 - tadd[y['year']] = tcse[y['year']]+tece[y['year']]+tme[y['year']] - y['year__count'] = [tadd[y['year']], tcse[y['year']], tece[y['year']], tme[y['year']]] - - form2 = SearchPlacementRecord(initial={}) - form3 = SearchPbiRecord(initial={}) - form4 = SearchHigherRecord(initial={}) - - - current1 = HoldsDesignation.objects.filter(Q(working=user, designation__name="placement chairman")) - current2 = HoldsDesignation.objects.filter(Q(working=user, designation__name="placement officer")) - current = HoldsDesignation.objects.filter(Q(working=user, designation__name="student")) - - if len(current1)!=0 or len(current2)!=0: - delete_operation = 1 - if len(current) == 0: - current = None - pbirecord= '' - placementrecord= '' - higherrecord= '' - total_query=0 - total_query1 = 0 - total_query2= 0 - p="" - p1="" - p2="" - placement_search_record=" " - pbi_search_record=" " - higher_search_record=" " - # results of the searched query under placement tab - if 'studentplacementrecordsubmit' in request.POST: - officer_statistics_past = 1 - form = SearchPlacementRecord(request.POST) - if form.is_valid(): - - - - - print("IS VALID") - - - - #for student name - if form.cleaned_data['stuname']: - stuname = form.cleaned_data['stuname'] - try: - first_name = stuname.split(" ")[0] - last_name = stuname.split(" ")[1] - except Exception as e: - print("Error") - print(e) - first_name = stuname - last_name = '' - else: - stuname = '' - first_name = '' - last_name = '' - - - # for student CTC - if form.cleaned_data['ctc']: - ctc = form.cleaned_data['ctc'] - else: - ctc = 0 - - #for company name - if form.cleaned_data['cname']: - cname = form.cleaned_data['cname'] - else: - cname = '' - - #for student roll - if form.cleaned_data['roll']: - rollno = form.cleaned_data['roll'] - else: - rollno = '' - - #for admission year - if form.cleaned_data['year']: - year = form.cleaned_data['year'] - s = Student.objects.filter((Q(id__in=ExtraInfo.objects.filter( - Q(user__in=User.objects.filter( - first_name__icontains=first_name, - last_name__icontains=last_name), - id__icontains=rollno)) - ))) - - p = PlacementRecord.objects.filter(Q(placement_type="PLACEMENT", name__icontains=stuname, ctc__icontains=ctc, year__icontains=year)) - - - - - """placementrecord = StudentRecord.objects.select_related('unique_id','record_id').filter( - Q(record_id__in=PlacementRecord.objects.filter( - Q(placement_type="PLACEMENT", name__icontains=cname, ctc__gte=ctc, year=year)), - unique_id__in=Student.objects.filter( - (Q(id__in=ExtraInfo.objects.filter( - Q(user__in=User.objects.filter( - first_name__icontains=first_name, - last_name__icontains=last_name, - id__icontains=rollno)))))))) - #print("In if:", placementrecord) - else: - s = Student.objects.filter((Q(id__in=ExtraInfo.objects.filter( - Q(user__in=User.objects.filter( - first_name__icontains=first_name, - last_name__icontains=last_name), - id__icontains=rollno)) - ))) - - p = PlacementRecord.objects.filter(Q(placement_type="PLACEMENT", name__icontains=cname, ctc__gte=ctc)) - print("Agein p:",p) - placementrecord = StudentRecord.objects.select_related('unique_id','record_id').filter( - Q(record_id__in=PlacementRecord.objects.filter( - Q(placement_type="PLACEMENT", name__icontains=cname, ctc__gte=ctc)), - unique_id__in=Student.objects.filter( - (Q(id__in=ExtraInfo.objects.filter( - Q(user__in=User.objects.filter( - first_name__icontains=first_name, - last_name__icontains=last_name), - id__icontains=rollno))))))) - - request.session['first_name'] = first_name - request.session['last_name'] = last_name - request.session['ctc'] = ctc - request.session['cname'] = cname - request.session['rollno'] = rollno - request.session['year'] = form.cleaned_data['year']""" - - print(p) - - - total_query = p.count() - - if total_query > 30: - pagination_placement = 1 - paginator = Paginator(placementrecord, 30) - page = request.GET.get('page', 1) - placementrecord = paginator.page(page) - page = int(page) - total_page = int(page + 3) - - if page<(paginator.num_pages-3): - if total_query > 30 and total_query <=60: - page_range = range(1, 3) - else: - page_range = range(1, total_page+1) - - if page >= 5: - is_disabled = 1 - page_range = range(page-2, total_page) - else: - if page >= 5: - is_disabled = 1 - page_range = range(page-2, paginator.num_pages+1) - else: - page_range = range(1, paginator.num_pages+1) - else: - pagination_placement = 0 - else: - if request.GET.get('page') != None: - try: - if request.session['year']: - s = Student.objects.filter( - (Q(id__in=ExtraInfo.objects.filter( - Q(user__in=User.objects.filter( - Q(first_name__icontains=request.session['first_name'], - last_name__icontains=request.session['last_name'])), - id__icontains=request.session['rollno'])) - ))) - - p = PlacementRecord.objects.filter( - Q(placement_type="PLACEMENT", - name__icontains=request.session['cname'], - ctc__gte=request.session['ctc'], - year=request.session['year'])) - - - placementrecord = StudentRecord.objects.select_related('unique_id','record_id').filter( - Q(record_id__in=PlacementRecord.objects.filter( - Q(placement_type="PLACEMENT", - name__icontains=request.session['cname'], - ctc__gte=request.session['ctc'], - year=request.session['year'])), - unique_id__in=Student.objects.filter( - (Q(id__in=ExtraInfo.objects.filter( - Q(user__in=User.objects.filter( - Q(first_name__icontains=request.session['first_name'], - last_name__icontains=request.session['last_name'])), - id__icontains=request.session['rollno']))))))) - else: - s = Student.objects.filter((Q(id__in=ExtraInfo.objects.filter - (Q(user__in=User.objects.filter - (Q(first_name__icontains=request.session['first_name'], - last_name__icontains=request.session['last_name'])), - id__icontains=request.session['rollno'])) - ))) - - p = PlacementRecord.objects.filter( - Q(placement_type="PLACEMENT", - name__icontains=request.session['cname'], - ctc__gte=request.session['ctc'])) - - placementrecord = StudentRecord.objects.select_related('unique_id','record_id').filter( - Q(record_id__in=PlacementRecord.objects.filter( - Q(placement_type="PLACEMENT", - name__icontains=request.session['cname'], - ctc__gte=request.session['ctc'])), - unique_id__in=Student.objects.filter( - (Q(id__in=ExtraInfo.objects.filter( - Q(user__in=User.objects.filter( - Q(first_name__icontains=request.session['first_name'], - last_name__icontains=request.session['last_name'])), - id__icontains=request.session['rollno']))))))) - except Exception as e: - print(e) - placementrecord = '' - - if placementrecord != '': - total_query = placementrecord.count() - else: - total_query = 0 - no_records=1 - print(placementrecord) - if total_query > 30: - pagination_placement = 1 - paginator = Paginator(placementrecord, 30) - page = request.GET.get('page', 1) - placementrecord = paginator.page(page) - page = int(page) - total_page = int(page + 3) - - if page<(paginator.num_pages-3): - if total_query > 30 and total_query <=60: - page_range = range(1, 3) - else: - page_range = range(1, total_page+1) - - if page >= 5: - is_disabled = 1 - page_range = range(page-2, total_page) - else: - if page >= 5: - is_disabled = 1 - page_range = range(page-2, paginator.num_pages+1) - else: - page_range = range(1, paginator.num_pages+1) - else: - pagination_placement = 0 - else: - placementrecord = '' - - if total_query!=0: - placement_search_record=p - # results of the searched query under pbi tab - if 'studentpbirecordsubmit' in request.POST: - officer_statistics_past_pbi_search = 1 - form = SearchPbiRecord(request.POST) - if form.is_valid(): - if form.cleaned_data['stuname']: - stuname = form.cleaned_data['stuname'] - try: - first_name = stuname.split(" ")[0] - last_name = stuname.split(" ")[1] - except: - first_name = stuname - last_name = '' - else: - stuname = '' - first_name = '' - last_name = '' - if form.cleaned_data['ctc']: - ctc = form.cleaned_data['ctc'] - else: - ctc = 0 - if form.cleaned_data['cname']: - cname = form.cleaned_data['cname'] - else: - cname = '' - if form.cleaned_data['roll']: - rollno = form.cleaned_data['roll'] - else: - rollno = '' - if form.cleaned_data['year']: - year = form.cleaned_data['year'] - pbirecord = StudentRecord.objects.select_related('unique_id','record_id').filter(Q(record_id__in=PlacementRecord.objects.filter - (Q(placement_type="PBI", - name__icontains=cname, - ctc__gte=ctc, year=year)), - unique_id__in=Student.objects.filter - ((Q(id__in=ExtraInfo.objects.filter - (Q(user__in=User.objects.filter - (Q(first_name__icontains=first_name, - last_name__icontains=last_name)), - id__icontains=rollno)) - ))))) - p1 = PlacementRecord.objects.filter( - Q(placement_type="PBI", name__icontains=stuname, ctc__icontains=ctc, year__icontains=year)) - """else: - pbirecord = StudentRecord.objects.select_related('unique_id','record_id').filter(Q(record_id__in=PlacementRecord.objects.filter - (Q(placement_type="PBI", - name__icontains=cname, - ctc__gte=ctc)), - unique_id__in=Student.objects.filter - ((Q(id__in=ExtraInfo.objects.filter - (Q(user__in=User.objects.filter - (Q(first_name__icontains=first_name, - last_name__icontains=last_name)), - id__icontains=rollno)) - ))))) - request.session['first_name'] = first_name - request.session['last_name'] = last_name - request.session['ctc'] = ctc - request.session['cname'] = cname - request.session['rollno'] = rollno - request.session['year'] = form.cleaned_data['year'] -""" - total_query1 = p1.count() - - if total_query1 > 30: - pagination_pbi = 1 - paginator = Paginator(pbirecord, 30) - page = request.GET.get('page', 1) - pbirecord = paginator.page(page) - page = int(page) - total_page = int(page + 3) - - if page<(paginator.num_pages-3): - if total_query1 > 30 and total_query1 <=60: - page_range = range(1, 3) - else: - page_range = range(1, total_page+1) - - if page >= 5: - is_disabled = 1 - page_range = range(page-2, total_page) - else: - if page >= 5: - is_disabled = 1 - page_range = range(page-2, paginator.num_pages+1) - else: - page_range = range(1, paginator.num_pages+1) - else: - pagination_pbi = 0 - else: - if request.GET.get('page') != None: - try: - if request.session['year']: - pbirecord = StudentRecord.objects.select_related('unique_id','record_id').filter( - Q(record_id__in=PlacementRecord.objects.filter( - Q(placement_type="PBI", - name__icontains=request.session['cname'], - ctc__gte=ctc, year=request.session['year'])), - unique_id__in=Student.objects.filter(( - Q(id__in=ExtraInfo.objects.filter( - Q(user__in=User.objects.filter( - Q(first_name__icontains=request.session['first_name'], - last_name__icontains=request.session['last_name'])), - id__icontains=request.session['rollno']))))))) - else: - pbirecord = StudentRecord.objects.select_related('unique_id','record_id').filter( - Q(record_id__in=PlacementRecord.objects.filter(Q(placement_type="PBI", - name__icontains=request.session['cname'], - ctc__gte=request.session['ctc'])), - unique_id__in=Student.objects.filter( - (Q(id__in=ExtraInfo.objects.filter( - Q(user__in=User.objects.filter( - Q(first_name__icontains=request.session['first_name'], - last_name__icontains=request.session['last_name'])), - id__icontains=request.session['rollno']))))))) - except: - print('except') - pbirecord = '' - - if pbirecord != '': - total_query = pbirecord.count() - else: - total_query = 0 - - if total_query > 30: - pagination_pbi = 1 - paginator = Paginator(pbirecord, 30) - page = request.GET.get('page', 1) - pbirecord = paginator.page(page) - page = int(page) - total_page = int(page + 3) - - if page<(paginator.num_pages-3): - if total_query > 30 and total_query <=60: - page_range = range(1, 3) - else: - page_range = range(1, total_page+1) - - if page >= 5: - is_disabled = 1 - page_range = range(page-2, total_page) - else: - if page >= 5: - is_disabled = 1 - page_range = range(page-2, paginator.num_pages+1) - else: - page_range = range(1, paginator.num_pages+1) - else: - pagination_pbi = 0 - else: - pbirecord = '' - if total_query1!=0: - pbi_search_record=p1 - - # results of the searched query under higher studies tab - if 'studenthigherrecordsubmit' in request.POST: - officer_statistics_past_higher_search = 1 - form = SearchHigherRecord(request.POST) - if form.is_valid(): - # getting all the variables send through form - if form.cleaned_data['stuname']: - stuname = form.cleaned_data['stuname'] - try: - first_name = stuname.split(" ")[0] - last_name = stuname.split(" ")[1] - except: - first_name = stuname - last_name = '' - else: - stuname = '' - first_name = '' - last_name = '' - if form.cleaned_data['test_type']: - test_type = form.cleaned_data['test_type'] - else: - test_type = '' - if form.cleaned_data['uname']: - uname = form.cleaned_data['uname'] - else: - uname = '' - if form.cleaned_data['test_score']: - test_score = form.cleaned_data['test_score'] - else: - test_score = 0 - if form.cleaned_data['roll']: - rollno = form.cleaned_data['roll'] - else: - rollno = '' - if form.cleaned_data['year']: - year = form.cleaned_data['year'] - # result of the query when year is given - higherrecord = StudentRecord.objects.select_related('unique_id','record_id').filter(Q(record_id__in=PlacementRecord.objects.filter - (Q(placement_type="HIGHER STUDIES", - test_type__icontains=test_type, - name__icontains=uname, year=year, - test_score__gte=test_score)), - unique_id__in=Student.objects.filter - ((Q(id__in=ExtraInfo.objects.filter - (Q(user__in=User.objects.filter - (Q(first_name__icontains=first_name, - last_name__icontains=last_name)), - id__icontains=rollno)) - ))))) - - p2 = PlacementRecord.objects.filter( - Q(placement_type="HIGHER STUDIES", name__icontains=stuname, year__icontains=year)) - - """else: - # result of the query when year is not given - higherrecord = StudentRecord.objects.select_related('unique_id','record_id').filter( - Q(record_id__in=PlacementRecord.objects.filter - (Q(placement_type="HIGHER STUDIES", - test_type__icontains=test_type, - name__icontains=uname, - test_score__gte=test_score)), - unique_id__in=Student.objects.filter - ((Q(id__in=ExtraInfo.objects.filter - (Q(user__in=User.objects.filter - (Q(first_name__icontains=first_name, - last_name__icontains=last_name)), - id__icontains=rollno)) - ))))) - request.session['first_name'] = first_name - request.session['last_name'] = last_name - request.session['test_score'] = test_score - request.session['uname'] = uname - request.session['test_type'] = test_type - request.session['rollno'] = rollno - request.session['year'] = form.cleaned_data['year']""" - - total_query2 = p2.count() - - if total_query2 > 30: - pagination_higher = 1 - paginator = Paginator(p2, 30) - page = request.GET.get('page', 1) - p2 = paginator.page(page) - page = int(page) - total_page = int(page+3) - - if page < (paginator.num_pages-3): - if total_query2 > 30 and total_query2 <= 60: - page_range = range(1, 3) - else: - page_range = range(1, total_page+1) - - if page >= 5: - is_disabled = 1 - page_range = range(page-2, total_page) - else: - if page >= 5: - is_disabled = 1 - page_range = range(page-2, paginator.num_pages+1) - else: - page_range = range(page-2, paginator.num_pages+1) - else: - pagination_higher = 0 - else: - if request.GET.get('page') != None: - try: - if request.session['year']: - higherrecord = StudentRecord.objects.select_related('unique_id','record_id').filter( - Q(record_id__in=PlacementRecord.objects.filter( - Q(placement_type="HIGHER STUDIES", - test_type__icontains=request.session['test_type'], - name__icontains=request.session['uname'], - year=request.session['year'], - test_score__gte=request.session['test_score'])), - unique_id__in=Student.objects.filter( - (Q(id__in=ExtraInfo.objects.filter( - Q(user__in=User.objects.filter( - Q(first_name__icontains=request.session['first_name'], - last_name__icontains=request.session['last_name'])), - id__icontains=request.session['rollno'])) - ))))) - else: - higherrecord = StudentRecord.objects.select_related('unique_id','record_id').filter( - Q(record_id__in=PlacementRecord.objects.filter( - Q(placement_type="HIGHER STUDIES", - test_type__icontains=request.session['test_type'], - name__icontains=request.session['uname'], - test_score__gte=request.session['test_score'])), - unique_id__in=Student.objects.filter - ((Q(id__in=ExtraInfo.objects.filter( - Q(user__in=User.objects.filter( - Q(first_name__icontains=request.session['first_name'], - last_name__icontains=request.session['last_name'])), - id__icontains=request.session['rollno'])) - ))))) - except: - higherrecord = '' - - if higherrecord != '': - total_query = higherrecord.count() - else: - total_query = 0 - - if total_query > 30: - no_pagination = 1 - paginator = Paginator(higherrecord, 30) - page = request.GET.get('page', 1) - higherrecord = paginator.page(page) - page = int(page) - total_page = int(page + 3) - - if page<(paginator.num_pages-3): - if total_query > 30 and total_query <=60: - page_range = range(1, 3) - else: - page_range = range(1, total_page+1) - - if page >= 5: - is_disabled = 1 - page_range = range(page-2, total_page) - else: - if page >= 5: - is_disabled = 1 - page_range = range(page-2, paginator.num_pages+1) - else: - page_range = range(1, paginator.num_pages+1) - else: - no_pagination = 0 - else: - higherrecord = '' - if total_query2!=0: - higher_search_record=p2 - - context = { - 'form2' : form2, - 'form3' : form3, - 'form4' : form4, - 'current' : current, - 'current1' : current1, - 'current2' : current2, - - - 'all_records': all_records, #for flashing all placement Schedule - - 'placement_search_record': placement_search_record, - 'pbi_search_record': pbi_search_record, - 'higher_search_record': higher_search_record, - - - - 'statistics_tab' : statistics_tab, - 'pbirecord' : pbirecord, - 'placementrecord' : placementrecord, - 'higherrecord' : higherrecord, - 'years' : years, - 'records' : records, - 'delete_operation' : delete_operation, - 'page_range': page_range, - 'paginator': paginator, - 'pagination_placement': pagination_placement, - 'pagination_pbi': pagination_pbi, - 'pagination_higher': pagination_higher, - 'is_disabled': is_disabled, - 'officer_statistics_past_pbi_search': officer_statistics_past_pbi_search, - 'officer_statistics_past_higher_search': officer_statistics_past_higher_search - } - - return render(request, 'placementModule/placementstatistics.html', context) - - - -def get_reference_list(request): - if request.method == 'POST': - # arr = request.POST.getlist('arr[]') - # print(arr) - # print(type(arr)) - user = request.user - profile = get_object_or_404(ExtraInfo, Q(user=user)) - student = get_object_or_404(Student, Q(id=profile.id)) - print(student) - reference_objects = Reference.select_related('unique_id').objects.filter(unique_id=student) - reference_objects = serializers.serialize('json', list(reference_objects)) - - context = { - 'reference_objs': reference_objects - } - return JsonResponse(context) - - -# Ajax for the company name dropdown for CompanyName when filling AddSchedule -def company_name_dropdown(request): - if request.method == 'POST': - current_value = request.POST.get('current_value') - company_names = CompanyDetails.objects.filter(Q(company_name__startswith=current_value)) - company_name = [] - for name in company_names: - company_name.append(name.company_name) - - context = { - 'company_names': company_name - } - - return JsonResponse(context) - - -# Ajax for all the roles in the dropdown -def checking_roles(request): - if request.method == 'POST': - current_value = request.POST.get('current_value') - all_roles = Role.objects.filter(Q(role__startswith=current_value)) - role_name = [] - for role in all_roles: - role_name.append(role.role) - return JsonResponse({'all_roles': role_name}) - -@login_required -def Placement__Schedule(request): - ''' - function include the functionality of first tab of UI - for student, placement officer & placement chairman - - placement officer & placement chairman - - can add schedule - - can delete schedule - student - - accepted or declined schedule - - ''' - user = request.user - profile = get_object_or_404(ExtraInfo, Q(user=user)) - schedule_tab = 1 - placementstatus = '' - - - form5 = AddSchedule(initial={}) - current1 = HoldsDesignation.objects.filter(Q(working=user, designation__name="placement chairman")) - current2 = HoldsDesignation.objects.filter(Q(working=user, designation__name="placement officer")) - current = HoldsDesignation.objects.filter(Q(working=user, designation__name="student")) - print(current) - - # If the user is Student - if current: - student = get_object_or_404(Student, Q(id=profile.id)) - - # Student view for showing accepted or declined schedule - if request.method == 'POST': - if 'studentapprovesubmit' in request.POST: - status = PlacementStatus.objects.select_related('unique_id','notify_id').filter( - pk=request.POST['studentapprovesubmit']).update( - invitation='ACCEPTED', - timestamp=timezone.now()) - if 'studentdeclinesubmit' in request.POST: - status = PlacementStatus.objects.select_related('unique_id','notify_id').filter( - Q(pk=request.POST['studentdeclinesubmit'])).update( - invitation='REJECTED', - timestamp=timezone.now()) - - if 'educationsubmit' in request.POST: - form = AddEducation(request.POST) - if form.is_valid(): - institute = form.cleaned_data['institute'] - degree = form.cleaned_data['degree'] - grade = form.cleaned_data['grade'] - stream = form.cleaned_data['stream'] - sdate = form.cleaned_data['sdate'] - edate = form.cleaned_data['edate'] - education_obj = Education.objects.select_related('unique_id').create( - unique_id=student, degree=degree, - grade=grade, institute=institute, - stream=stream, sdate=sdate, edate=edate) - education_obj.save() - if 'profilesubmit' in request.POST: - about_me = request.POST.get('about') - age = request.POST.get('age') - address = request.POST.get('address') - contact = request.POST.get('contact') - pic = request.POST.get('pic') - # futu = request.POST.get('futu') - # print(studentplacement_obj.future_aspect) - # print('fut=', fut) - # print('futu=', futu) - # if studentplacement_obj.future_aspect == "HIGHER STUDIES": - # if futu == 2: - # studentplacement_obj.future_aspect = "PLACEMENT" - # elif studentplacement_obj.future_aspect == "PLACEMENT": - # if futu == None: - # studentplacement_obj.future_aspect = "HIGHER STUDIES" - extrainfo_obj = ExtraInfo.objects.get(user=user) - extrainfo_obj.about_me = about_me - extrainfo_obj.age = age - extrainfo_obj.address = address - extrainfo_obj.phone_no = contact - extrainfo_obj.profile_picture = pic - extrainfo_obj.save() - profile = get_object_or_404(ExtraInfo, Q(user=user)) - if 'skillsubmit' in request.POST: - form = AddSkill(request.POST) - if form.is_valid(): - skill = form.cleaned_data['skill'] - skill_rating = form.cleaned_data['skill_rating'] - has_obj = Has.objects.select_related('skill_id','unique_id').create(unique_id=student, - skill_id=Skill.objects.get(skill=skill), - skill_rating = skill_rating) - has_obj.save() - if 'achievementsubmit' in request.POST: - form = AddAchievement(request.POST) - if form.is_valid(): - achievement = form.cleaned_data['achievement'] - achievement_type = form.cleaned_data['achievement_type'] - description = form.cleaned_data['description'] - issuer = form.cleaned_data['issuer'] - date_earned = form.cleaned_data['date_earned'] - achievement_obj = Achievement.objects.select_related('unique_id').create(unique_id=student, - achievement=achievement, - achievement_type=achievement_type, - description=description, - issuer=issuer, - date_earned=date_earned) - achievement_obj.save() - if 'publicationsubmit' in request.POST: - form = AddPublication(request.POST) - if form.is_valid(): - publication_title = form.cleaned_data['publication_title'] - description = form.cleaned_data['description'] - publisher = form.cleaned_data['publisher'] - publication_date = form.cleaned_data['publication_date'] - publication_obj = Publication.objects.select_related('unique_id').create(unique_id=student, - publication_title= - publication_title, - publisher=publisher, - description=description, - publication_date=publication_date) - publication_obj.save() - if 'patentsubmit' in request.POST: - form = AddPatent(request.POST) - if form.is_valid(): - patent_name = form.cleaned_data['patent_name'] - description = form.cleaned_data['description'] - patent_office = form.cleaned_data['patent_office'] - patent_date = form.cleaned_data['patent_date'] - patent_obj = Patent.objects.select_related('unique_id').create(unique_id=student, patent_name=patent_name, - patent_office=patent_office, - description=description, - patent_date=patent_date) - patent_obj.save() - if 'coursesubmit' in request.POST: - form = AddCourse(request.POST) - if form.is_valid(): - course_name = form.cleaned_data['course_name'] - description = form.cleaned_data['description'] - license_no = form.cleaned_data['license_no'] - sdate = form.cleaned_data['sdate'] - edate = form.cleaned_data['edate'] - course_obj = Course.objects.select_related('unique_id').create(unique_id=student, course_name=course_name, - license_no=license_no, - description=description, - sdate=sdate, edate=edate) - course_obj.save() - if 'projectsubmit' in request.POST: - form = AddProject(request.POST) - if form.is_valid(): - project_name = form.cleaned_data['project_name'] - project_status = form.cleaned_data['project_status'] - summary = form.cleaned_data['summary'] - project_link = form.cleaned_data['project_link'] - sdate = form.cleaned_data['sdate'] - edate = form.cleaned_data['edate'] - project_obj = Project.objects.create(unique_id=student, summary=summary, - project_name=project_name, - project_status=project_status, - project_link=project_link, - sdate=sdate, edate=edate) - project_obj.save() - if 'experiencesubmit' in request.POST: - form = AddExperience(request.POST) - if form.is_valid(): - title = form.cleaned_data['title'] - status = form.cleaned_data['status'] - company = form.cleaned_data['company'] - location = form.cleaned_data['location'] - description = form.cleaned_data['description'] - sdate = form.cleaned_data['sdate'] - edate = form.cleaned_data['edate'] - experience_obj = Experience.objects.select_related('unique_id').create(unique_id=student, title=title, - company=company, location=location, - status=status, - description=description, - sdate=sdate, edate=edate) - experience_obj.save() - - if 'deleteskill' in request.POST: - hid = request.POST['deleteskill'] - hs = Has.objects.select_related('skill_id','unique_id').get(Q(pk=hid)) - hs.delete() - if 'deleteedu' in request.POST: - hid = request.POST['deleteedu'] - hs = Education.objects.select_related('unique_id').get(Q(pk=hid)) - hs.delete() - if 'deletecourse' in request.POST: - hid = request.POST['deletecourse'] - hs = Course.objects.get(Q(pk=hid)) - hs.delete() - if 'deleteexp' in request.POST: - hid = request.POST['deleteexp'] - hs = Experience.objects.get(Q(pk=hid)) - hs.delete() - if 'deletepro' in request.POST: - hid = request.POST['deletepro'] - hs = Project.objects.get(Q(pk=hid)) - hs.delete() - if 'deleteach' in request.POST: - hid = request.POST['deleteach'] - hs = Achievement.objects.get(Q(pk=hid)) - hs.delete() - if 'deletepub' in request.POST: - hid = request.POST['deletepub'] - hs = Publication.objects.select_related('unique_id').get(Q(pk=hid)) - hs.delete() - if 'deletepat' in request.POST: - hid = request.POST['deletepat'] - hs = Patent.objects.get(Q(pk=hid)) - hs.delete() - - placementschedule = PlacementSchedule.objects.select_related('notify_id').filter( - Q(placement_date__gte=date.today())).values_list('notify_id', flat=True) - - placementstatus = PlacementStatus.objects.select_related('unique_id','notify_id').filter( - Q(unique_id=student, - notify_id__in=placementschedule)).order_by('-timestamp') - - - check_invitation_date(placementstatus) - - # facult and other staff view only statistics - if not (current or current1 or current2): - return redirect('/placement/statistics/') - - # delete the schedule - if 'deletesch' in request.POST: - delete_sch_key = request.POST['delete_sch_key'] - try: - placement_schedule = PlacementSchedule.objects.select_related('notify_id').get(pk = delete_sch_key) - NotifyStudent.objects.get(pk=placement_schedule.notify_id.id).delete() - placement_schedule.delete() - messages.success(request, 'Schedule Deleted Successfully') - except Exception as e: - messages.error(request, 'Problem Occurred for Schedule Delete!!!') - - # saving all the schedule details - if 'schedulesubmit' in request.POST: - form5 = AddSchedule(request.POST, request.FILES) - if form5.is_valid(): - company_name = form5.cleaned_data['company_name'] - placement_date = form5.cleaned_data['placement_date'] - location = form5.cleaned_data['location'] - ctc = form5.cleaned_data['ctc'] - time = form5.cleaned_data['time'] - attached_file = form5.cleaned_data['attached_file'] - placement_type = form5.cleaned_data['placement_type'] - role_offered = request.POST.get('role') - description = form5.cleaned_data['description'] - - try: - comp_name = CompanyDetails.objects.filter(company_name=company_name)[0] - except: - CompanyDetails.objects.create(company_name=company_name) - - try: - role = Role.objects.filter(role=role_offered)[0] - except: - role = Role.objects.create(role=role_offered) - role.save() - - - notify = NotifyStudent.objects.create(placement_type=placement_type, - company_name=company_name, - description=description, - ctc=ctc, - timestamp=timezone.now()) - - schedule = PlacementSchedule.objects.select_related('notify_id').create(notify_id=notify, - title=company_name, - description=description, - placement_date=placement_date, - attached_file = attached_file, - role=role, - location=location, time=time) - - notify.save() - schedule.save() - messages.success(request, "Schedule Added Successfull!!") - - - schedules = PlacementSchedule.objects.select_related('notify_id').all() - - - context = { - 'current': current, - 'current1': current1, - 'current2': current2, - 'schedule_tab': schedule_tab, - 'schedules': schedules, - 'placementstatus': placementstatus, - 'form5': form5, - } - - return render(request, 'placementModule/placement.html', context) - - - -def invite_status(request): - ''' - function to check the invitation status - ''' - user = request.user - strecord_tab = 1 - mnpbi_tab = 0 - mnplacement_post = 0 - mnpbi_post = 0 - invitation_status_tab = 1 - placementstatus_placement = [] - placementstatus_pbi = [] - mnplacement_tab = 1 - - no_pagination = 1 - is_disabled = 0 - paginator = '' - page_range = '' - placement_get_request = False - pbi_get_request = False - - # invitation status for placement - if 'studentplacementsearchsubmit' in request.POST: - mnplacement_post = 1 - mnpbi_post = 0 - form = ManagePlacementRecord(request.POST) - - if form.is_valid(): - if form.cleaned_data['stuname']: - stuname = form.cleaned_data['stuname'] - else: - stuname = '' - if form.cleaned_data['ctc']: - ctc = form.cleaned_data['ctc'] - else: - ctc = 0 - if form.cleaned_data['company']: - cname = form.cleaned_data['company'] - else: - cname = '' - if form.cleaned_data['roll']: - rollno = form.cleaned_data['roll'] - else: - rollno = '' - - request.session['mn_stuname'] = stuname - request.session['mn_ctc'] = ctc - request.session['mn_cname'] = cname - request.session['mn_rollno'] = rollno - - placementstatus_placement = PlacementStatus.objects.select_related('unique_id','notify_id').filter(Q(notify_id__in=NotifyStudent.objects.filter - (Q(placement_type="PLACEMENT", - company_name__icontains=cname, - ctc__gte=ctc)), - unique_id__in=Student.objects.filter - ((Q(id__in=ExtraInfo.objects.filter - (Q(user__in=User.objects.filter - (Q(first_name__icontains=stuname)), - id__icontains=rollno)) - ))))) - # pagination stuff starts from here - total_query = placementstatus_placement.count() - - if total_query > 30: - no_pagination = 1 - paginator = Paginator(placementstatus_placement, 30) - page = request.GET.get('page', 1) - placementstatus_placement = paginator.page(page) - page = int(page) - total_page = int(page + 3) - - if page<(paginator.num_pages-3): - if total_query > 30 and total_query <=60: - page_range = range(1, 3) - else: - page_range = range(1, total_page+1) - - if page >= 5: - is_disabled = 1 - page_range = range(page-2, total_page) - else: - if page >= 5: - is_disabled = 1 - page_range = range(page-2, paginator.num_pages+1) - else: - page_range = range(1, paginator.num_pages+1) - else: - no_pagination = 0 - else: - # when the request from pagination with some page number - if request.GET.get('placement_page') != None: - mnplacement_post = 1 - mnpbi_post = 0 - no_pagination = 1 - try: - placementstatus_placement = PlacementStatus.objects.select_related('unique_id','notify_id').filter(Q(notify_id__in=NotifyStudent.objects.filter - (Q(placement_type="PLACEMENT", - company_name__icontains=request.session['mn_cname'], - ctc__gte=request.session['mn_ctc'])), - unique_id__in=Student.objects.filter - ((Q(id__in=ExtraInfo.objects.filter - (Q(user__in=User.objects.filter - (Q(first_name__icontains=request.session['mn_stuname'])), - id__icontains=request.session['mn_rollno'])) - ))))) - except: - placementstatus_placement = [] - - if placementstatus_placement != '': - total_query = placementstatus_placement.count() - else: - total_query = 0 - - if total_query > 30: - paginator = Paginator(placementstatus_placement, 30) - page = request.GET.get('placement_page', 1) - placementstatus_placement = paginator.page(page) - page = int(page) - total_page = int(page + 3) - - if page<(paginator.num_pages-3): - if total_query > 30 and total_query <=60: - page_range = range(1, 3) - else: - page_range = range(1, total_page+1) - - if page >= 5: - is_disabled = 1 - page_range = range(page-2, total_page) - else: - if page >= 5: - is_disabled = 1 - page_range = range(page-2, paginator.num_pages+1) - else: - page_range = range(1, paginator.num_pages+1) - else: - no_pagination = 0 - - # invitation status for pbi - if 'studentpbisearchsubmit' in request.POST: - mnpbi_tab = 1 - mnpbi_post = 1 - mnplacement_post = 0 - form = ManagePbiRecord(request.POST) - if form.is_valid(): - if form.cleaned_data['stuname']: - stuname = form.cleaned_data['stuname'] - else: - stuname = '' - if form.cleaned_data['ctc']: - ctc = form.cleaned_data['ctc'] - else: - ctc = 0 - if form.cleaned_data['company']: - cname = form.cleaned_data['company'] - else: - cname = '' - if form.cleaned_data['roll']: - rollno = form.cleaned_data['roll'] - else: - rollno = '' - request.session['mn_pbi_stuname'] = stuname - request.session['mn_pbi_ctc'] = ctc - request.session['mn_pbi_cname'] = cname - request.session['mn_pbi_rollno'] = rollno - placementstatus_pbi = PlacementStatus.objects.select_related('unique_id','notify_id').filter( - Q(notify_id__in=NotifyStudent.objects.filter( - Q(placement_type="PBI", - company_name__icontains=cname, - ctc__gte=ctc)), - unique_id__in=Student.objects.filter( - (Q(id__in=ExtraInfo.objects.filter( - Q(user__in=User.objects.filter( - Q(first_name__icontains=stuname)), - id__icontains=rollno))))))).order_by('id') - - total_query = placementstatus_pbi.count() - - if total_query > 30: - no_pagination = 1 - paginator = Paginator(placementstatus_pbi, 30) - page = request.GET.get('pbi_page', 1) - placementstatus_pbi = paginator.page(page) - page = int(page) - total_page = int(page + 3) - - if page<(paginator.num_pages-3): - if total_query > 30 and total_query <=60: - page_range = range(1, 3) - else: - page_range = range(1, total_page+1) - - if page >= 5: - is_disabled = 1 - page_range = range(page-2, total_page) - else: - if page >= 5: - is_disabled = 1 - page_range = range(page-2, paginator.num_pages+1) - else: - page_range = range(1, paginator.num_pages+1) - else: - no_pagination = 0 - else: - if request.GET.get('pbi_page') != None: - mnpbi_tab = 1 - mnpbi_post = 1 - no_pagination = 1 - try: - placementstatus_pbi = PlacementStatus.objects.select_related('unique_id','notify_id').filter( - Q(notify_id__in=NotifyStudent.objects.filter( - Q(placement_type="PBI", - company_name__icontains=request.session['mn_pbi_cname'], - ctc__gte=request.session['mn_pbi_ctc'])), - unique_id__in=Student.objects.filter( - (Q(id__in=ExtraInfo.objects.filter( - Q(user__in=User.objects.filter( - Q(first_name__icontains=request.session['mn_pbi_stuname'])), - id__icontains=request.session['mn_pbi_rollno'])) - ))))) - except: - placementstatus_pbi = '' - - if placementstatus_pbi != '': - total_query = placementstatus_pbi.count() - else: - total_query = 0 - if total_query > 30: - paginator = Paginator(placementstatus_pbi, 30) - page = request.GET.get('pbi_page', 1) - placementstatus_pbi = paginator.page(page) - page = int(page) - total_page = int(page + 3) - - if page<(paginator.num_pages-3): - if total_query > 30 and total_query <=60: - page_range = range(1, 3) - else: - page_range = range(1, total_page+1) - - if page >= 5: - is_disabled = 1 - page_range = range(page-2, total_page) - else: - if page >= 5: - is_disabled = 1 - page_range = range(page-2, paginator.num_pages+1) - else: - page_range = range(1, paginator.num_pages+1) - else: - no_pagination = 0 - - - if 'pdf_gen_invitation_status' in request.POST: - - placementstatus = None - if 'pdf_gen_invitation_status_placement' in request.POST: - stuname = request.session['mn_stuname'] - ctc = request.session['mn_ctc'] - cname = request.session['mn_cname'] - rollno = request.session['mn_rollno'] - - placementstatus = PlacementStatus.objects.select_related('unique_id','notify_id').filter(Q(notify_id__in=NotifyStudent.objects.filter - (Q(placement_type="PLACEMENT", - company_name__icontains=cname, - ctc__gte=ctc)), - unique_id__in=Student.objects.filter - ((Q(id__in=ExtraInfo.objects.filter - (Q(user__in=User.objects.filter - (Q(first_name__icontains=stuname)), - id__icontains=rollno)) - ))))) - - if 'pdf_gen_invitation_status_pbi' in request.POST: - stuname = request.session['mn_pbi_stuname'] - ctc = request.session['mn_pbi_ctc'] - cname = request.session['mn_pbi_cname'] - rollno = request.session['mn_pbi_rollno'] - - placementstatus = PlacementStatus.objects.select_related('unique_id','notify_id').filter( - Q(notify_id__in=NotifyStudent.objects.filter( - Q(placement_type="PBI", - company_name__icontains=cname, - ctc__gte=ctc)), - unique_id__in=Student.objects.filter( - (Q(id__in=ExtraInfo.objects.filter( - Q(user__in=User.objects.filter( - Q(first_name__icontains=stuname)), - id__icontains=rollno))))))).order_by('id') - - context = { - 'placementstatus' : placementstatus - } - - return render_to_pdf('placementModule/pdf_invitation_status.html', context) - - if 'excel_gen_invitation_status' in request.POST: - - placementstatus = None - if 'excel_gen_invitation_status_placement' in request.POST: - stuname = request.session['mn_stuname'] - ctc = request.session['mn_ctc'] - cname = request.session['mn_cname'] - rollno = request.session['mn_rollno'] - - placementstatus = PlacementStatus.objects.select_related('unique_id','notify_id').filter(Q(notify_id__in=NotifyStudent.objects.filter - (Q(placement_type="PLACEMENT", - company_name__icontains=cname, - ctc__gte=ctc)), - unique_id__in=Student.objects.filter - ((Q(id__in=ExtraInfo.objects.filter - (Q(user__in=User.objects.filter - (Q(first_name__icontains=stuname)), - id__icontains=rollno)) - ))))) - - if 'excel_gen_invitation_status_pbi' in request.POST: - stuname = request.session['mn_pbi_stuname'] - ctc = request.session['mn_pbi_ctc'] - cname = request.session['mn_pbi_cname'] - rollno = request.session['mn_pbi_rollno'] - - placementstatus = PlacementStatus.objects.select_related('unique_id','notify_id').filter( - Q(notify_id__in=NotifyStudent.objects.filter( - Q(placement_type="PBI", - company_name__icontains=cname, - ctc__gte=ctc)), - unique_id__in=Student.objects.filter( - (Q(id__in=ExtraInfo.objects.filter( - Q(user__in=User.objects.filter( - Q(first_name__icontains=stuname)), - id__icontains=rollno))))))).order_by('id') - - context = { - 'placementstatus' : placementstatus - } - - - years = PlacementRecord.objects.filter(~Q(placement_type="HIGHER STUDIES")).values('year').annotate(Count('year')) - records = PlacementRecord.objects.values('name', 'year', 'ctc', 'placement_type').annotate(Count('name'), Count('year'), Count('placement_type'), Count('ctc')) - - - return export_to_xls_invitation_status(placementstatus) - - form1 = SearchStudentRecord(initial={}) - form9 = ManagePbiRecord(initial={}) - form11 = ManagePlacementRecord(initial={}) - form13 = SendInvite(initial={}) - current1 = HoldsDesignation.objects.filter(Q(working=user, designation__name="placement chairman")) - current2 = HoldsDesignation.objects.filter(Q(working=user, designation__name="placement officer")) - - context = { - 'form1': form1, - 'form9': form9, - 'form11': form11, - 'form13': form13, - 'invitation_status_tab': invitation_status_tab, - 'mnplacement_post': mnplacement_post, - 'mnpbi_tab': mnpbi_tab, - 'mnplacement_tab': mnplacement_tab, - 'placementstatus_placement': placementstatus_placement, - 'placementstatus_pbi': placementstatus_pbi, - 'current1': current1, - 'current2': current2, - 'strecord_tab': strecord_tab, - 'mnpbi_post': mnpbi_post, - 'page_range': page_range, - 'paginator': paginator, - 'no_pagination': no_pagination, - 'is_disabled': is_disabled, - } - - return render(request, 'placementModule/studentrecords.html', context) - - - - - - - - invitecheck=0 - for r in records: - r['name__count'] = 0 - r['year__count'] = 0 - r['placement_type__count'] = 0 - tcse = dict() - tece = dict() - tme = dict() - tadd = dict() - for y in years: - tcse[y['year']] = 0 - tece[y['year']] = 0 - tme[y['year']] = 0 - for r in records: - if r['year'] == y['year']: - if r['placement_type'] != "HIGHER STUDIES": - for z in studentrecord: - if z.record_id.name == r['name'] and z.record_id.year == r['year'] and z.unique_id.id.department.name == "CSE": - tcse[y['year']] = tcse[y['year']]+1 - r['name__count'] = r['name__count']+1 - if z.record_id.name == r['name'] and z.record_id.year == r['year'] and z.unique_id.id.department.name == "ECE": - tece[y['year']] = tece[y['year']]+1 - r['year__count'] = r['year__count']+1 - if z.record_id.name == r['name'] and z.record_id.year == r['year'] and z.unique_id.id.department.name == "ME": - tme[y['year']] = tme[y['year']]+1 - r['placement_type__count'] = r['placement_type__count']+1 - tadd[y['year']] = tcse[y['year']]+tece[y['year']]+tme[y['year']] - y['year__count'] = [tadd[y['year']], tcse[y['year']], tece[y['year']], tme[y['year']]] - - form2 = SearchPlacementRecord(initial={}) - form3 = SearchPbiRecord(initial={}) - form4 = SearchHigherRecord(initial={}) - - - current1 = HoldsDesignation.objects.filter(Q(working=user, designation__name="placement chairman")) - current2 = HoldsDesignation.objects.filter(Q(working=user, designation__name="placement officer")) - current = HoldsDesignation.objects.filter(Q(working=user, designation__name="student")) - print(current) - - if len(current1)!=0 or len(current2)!=0: - delete_operation = 1 - if len(current) == 0: - current = None - pbirecord= '' - placementrecord= '' - higherrecord= '' - total_query=0 - total_query1 = 0 - total_query2= 0 - p="" - p1="" - p2="" - placement_search_record=" " - pbi_search_record=" " - higher_search_record=" " - # results of the searched query under placement tab - if 'studentplacementrecordsubmit' in request.POST: - officer_statistics_past = 1 - form = SearchPlacementRecord(request.POST) - if form.is_valid(): - - - - - print("IS VALID") - - - - #for student name - if form.cleaned_data['stuname']: - stuname = form.cleaned_data['stuname'] - try: - first_name = stuname.split(" ")[0] - last_name = stuname.split(" ")[1] - except Exception as e: - print("Error") - print(e) - first_name = stuname - last_name = '' - else: - stuname = '' - first_name = '' - last_name = '' - - - # for student CTC - if form.cleaned_data['ctc']: - ctc = form.cleaned_data['ctc'] - else: - ctc = 0 - - #for company name - if form.cleaned_data['cname']: - cname = form.cleaned_data['cname'] - else: - cname = '' - - #for student roll - if form.cleaned_data['roll']: - rollno = form.cleaned_data['roll'] - else: - rollno = '' - - #for admission year - if form.cleaned_data['year']: - year = form.cleaned_data['year'] - s = Student.objects.filter((Q(id__in=ExtraInfo.objects.filter( - Q(user__in=User.objects.filter( - first_name__icontains=first_name, - last_name__icontains=last_name), - id__icontains=rollno)) - ))) - - p = PlacementRecord.objects.filter(Q(placement_type="PLACEMENT", name__icontains=stuname, ctc__icontains=ctc, year__icontains=year)) - - - - - """placementrecord = StudentRecord.objects.select_related('unique_id','record_id').filter( - Q(record_id__in=PlacementRecord.objects.filter( - Q(placement_type="PLACEMENT", name__icontains=cname, ctc__gte=ctc, year=year)), - unique_id__in=Student.objects.filter( - (Q(id__in=ExtraInfo.objects.filter( - Q(user__in=User.objects.filter( - first_name__icontains=first_name, - last_name__icontains=last_name, - id__icontains=rollno)))))))) - #print("In if:", placementrecord) - else: - s = Student.objects.filter((Q(id__in=ExtraInfo.objects.filter( - Q(user__in=User.objects.filter( - first_name__icontains=first_name, - last_name__icontains=last_name), - id__icontains=rollno)) - ))) - - p = PlacementRecord.objects.filter(Q(placement_type="PLACEMENT", name__icontains=cname, ctc__gte=ctc)) - print("Agein p:",p) - placementrecord = StudentRecord.objects.select_related('unique_id','record_id').filter( - Q(record_id__in=PlacementRecord.objects.filter( - Q(placement_type="PLACEMENT", name__icontains=cname, ctc__gte=ctc)), - unique_id__in=Student.objects.filter( - (Q(id__in=ExtraInfo.objects.filter( - Q(user__in=User.objects.filter( - first_name__icontains=first_name, - last_name__icontains=last_name), - id__icontains=rollno))))))) - - request.session['first_name'] = first_name - request.session['last_name'] = last_name - request.session['ctc'] = ctc - request.session['cname'] = cname - request.session['rollno'] = rollno - request.session['year'] = form.cleaned_data['year']""" - - print(p) - - - total_query = p.count() - - if total_query > 30: - pagination_placement = 1 - paginator = Paginator(placementrecord, 30) - page = request.GET.get('page', 1) - placementrecord = paginator.page(page) - page = int(page) - total_page = int(page + 3) - - if page<(paginator.num_pages-3): - if total_query > 30 and total_query <=60: - page_range = range(1, 3) - else: - page_range = range(1, total_page+1) - - if page >= 5: - is_disabled = 1 - page_range = range(page-2, total_page) - else: - if page >= 5: - is_disabled = 1 - page_range = range(page-2, paginator.num_pages+1) - else: - page_range = range(1, paginator.num_pages+1) - else: - pagination_placement = 0 - else: - if request.GET.get('page') != None: - try: - if request.session['year']: - s = Student.objects.filter( - (Q(id__in=ExtraInfo.objects.filter( - Q(user__in=User.objects.filter( - Q(first_name__icontains=request.session['first_name'], - last_name__icontains=request.session['last_name'])), - id__icontains=request.session['rollno'])) - ))) - - p = PlacementRecord.objects.filter( - Q(placement_type="PLACEMENT", - name__icontains=request.session['cname'], - ctc__gte=request.session['ctc'], - year=request.session['year'])) - - - placementrecord = StudentRecord.objects.select_related('unique_id','record_id').filter( - Q(record_id__in=PlacementRecord.objects.filter( - Q(placement_type="PLACEMENT", - name__icontains=request.session['cname'], - ctc__gte=request.session['ctc'], - year=request.session['year'])), - unique_id__in=Student.objects.filter( - (Q(id__in=ExtraInfo.objects.filter( - Q(user__in=User.objects.filter( - Q(first_name__icontains=request.session['first_name'], - last_name__icontains=request.session['last_name'])), - id__icontains=request.session['rollno']))))))) - else: - s = Student.objects.filter((Q(id__in=ExtraInfo.objects.filter - (Q(user__in=User.objects.filter - (Q(first_name__icontains=request.session['first_name'], - last_name__icontains=request.session['last_name'])), - id__icontains=request.session['rollno'])) - ))) - - p = PlacementRecord.objects.filter( - Q(placement_type="PLACEMENT", - name__icontains=request.session['cname'], - ctc__gte=request.session['ctc'])) - - placementrecord = StudentRecord.objects.select_related('unique_id','record_id').filter( - Q(record_id__in=PlacementRecord.objects.filter( - Q(placement_type="PLACEMENT", - name__icontains=request.session['cname'], - ctc__gte=request.session['ctc'])), - unique_id__in=Student.objects.filter( - (Q(id__in=ExtraInfo.objects.filter( - Q(user__in=User.objects.filter( - Q(first_name__icontains=request.session['first_name'], - last_name__icontains=request.session['last_name'])), - id__icontains=request.session['rollno']))))))) - except Exception as e: - print(e) - placementrecord = '' - - if placementrecord != '': - total_query = placementrecord.count() - else: - total_query = 0 - no_records=1 - print(placementrecord) - if total_query > 30: - pagination_placement = 1 - paginator = Paginator(placementrecord, 30) - page = request.GET.get('page', 1) - placementrecord = paginator.page(page) - page = int(page) - total_page = int(page + 3) - - if page<(paginator.num_pages-3): - if total_query > 30 and total_query <=60: - page_range = range(1, 3) - else: - page_range = range(1, total_page+1) - - if page >= 5: - is_disabled = 1 - page_range = range(page-2, total_page) - else: - if page >= 5: - is_disabled = 1 - page_range = range(page-2, paginator.num_pages+1) - else: - page_range = range(1, paginator.num_pages+1) - else: - pagination_placement = 0 - else: - placementrecord = '' - - if total_query!=0: - placement_search_record=p - # results of the searched query under pbi tab - if 'studentpbirecordsubmit' in request.POST: - officer_statistics_past_pbi_search = 1 - form = SearchPbiRecord(request.POST) - if form.is_valid(): - if form.cleaned_data['stuname']: - stuname = form.cleaned_data['stuname'] - try: - first_name = stuname.split(" ")[0] - last_name = stuname.split(" ")[1] - except: - first_name = stuname - last_name = '' - else: - stuname = '' - first_name = '' - last_name = '' - if form.cleaned_data['ctc']: - ctc = form.cleaned_data['ctc'] - else: - ctc = 0 - if form.cleaned_data['cname']: - cname = form.cleaned_data['cname'] - else: - cname = '' - if form.cleaned_data['roll']: - rollno = form.cleaned_data['roll'] - else: - rollno = '' - if form.cleaned_data['year']: - year = form.cleaned_data['year'] - pbirecord = StudentRecord.objects.select_related('unique_id','record_id').filter(Q(record_id__in=PlacementRecord.objects.filter - (Q(placement_type="PBI", - name__icontains=cname, - ctc__gte=ctc, year=year)), - unique_id__in=Student.objects.filter - ((Q(id__in=ExtraInfo.objects.filter - (Q(user__in=User.objects.filter - (Q(first_name__icontains=first_name, - last_name__icontains=last_name)), - id__icontains=rollno)) - ))))) - p1 = PlacementRecord.objects.filter( - Q(placement_type="PBI", name__icontains=stuname, ctc__icontains=ctc, year__icontains=year)) - """else: - pbirecord = StudentRecord.objects.select_related('unique_id','record_id').filter(Q(record_id__in=PlacementRecord.objects.filter - (Q(placement_type="PBI", - name__icontains=cname, - ctc__gte=ctc)), - unique_id__in=Student.objects.filter - ((Q(id__in=ExtraInfo.objects.filter - (Q(user__in=User.objects.filter - (Q(first_name__icontains=first_name, - last_name__icontains=last_name)), - id__icontains=rollno)) - ))))) - request.session['first_name'] = first_name - request.session['last_name'] = last_name - request.session['ctc'] = ctc - request.session['cname'] = cname - request.session['rollno'] = rollno - request.session['year'] = form.cleaned_data['year'] -""" - total_query1 = p1.count() - - if total_query1 > 30: - pagination_pbi = 1 - paginator = Paginator(pbirecord, 30) - page = request.GET.get('page', 1) - pbirecord = paginator.page(page) - page = int(page) - total_page = int(page + 3) - - if page<(paginator.num_pages-3): - if total_query1 > 30 and total_query1 <=60: - page_range = range(1, 3) - else: - page_range = range(1, total_page+1) - - if page >= 5: - is_disabled = 1 - page_range = range(page-2, total_page) - else: - if page >= 5: - is_disabled = 1 - page_range = range(page-2, paginator.num_pages+1) - else: - page_range = range(1, paginator.num_pages+1) - else: - pagination_pbi = 0 - else: - if request.GET.get('page') != None: - try: - if request.session['year']: - pbirecord = StudentRecord.objects.select_related('unique_id','record_id').filter( - Q(record_id__in=PlacementRecord.objects.filter( - Q(placement_type="PBI", - name__icontains=request.session['cname'], - ctc__gte=ctc, year=request.session['year'])), - unique_id__in=Student.objects.filter(( - Q(id__in=ExtraInfo.objects.filter( - Q(user__in=User.objects.filter( - Q(first_name__icontains=request.session['first_name'], - last_name__icontains=request.session['last_name'])), - id__icontains=request.session['rollno']))))))) - else: - pbirecord = StudentRecord.objects.select_related('unique_id','record_id').filter( - Q(record_id__in=PlacementRecord.objects.filter(Q(placement_type="PBI", - name__icontains=request.session['cname'], - ctc__gte=request.session['ctc'])), - unique_id__in=Student.objects.filter( - (Q(id__in=ExtraInfo.objects.filter( - Q(user__in=User.objects.filter( - Q(first_name__icontains=request.session['first_name'], - last_name__icontains=request.session['last_name'])), - id__icontains=request.session['rollno']))))))) - except: - print('except') - pbirecord = '' - - if pbirecord != '': - total_query = pbirecord.count() - else: - total_query = 0 - - if total_query > 30: - pagination_pbi = 1 - paginator = Paginator(pbirecord, 30) - page = request.GET.get('page', 1) - pbirecord = paginator.page(page) - page = int(page) - total_page = int(page + 3) - - if page<(paginator.num_pages-3): - if total_query > 30 and total_query <=60: - page_range = range(1, 3) - else: - page_range = range(1, total_page+1) - - if page >= 5: - is_disabled = 1 - page_range = range(page-2, total_page) - else: - if page >= 5: - is_disabled = 1 - page_range = range(page-2, paginator.num_pages+1) - else: - page_range = range(1, paginator.num_pages+1) - else: - pagination_pbi = 0 - else: - pbirecord = '' - if total_query1!=0: - pbi_search_record=p1 - - # results of the searched query under higher studies tab - if 'studenthigherrecordsubmit' in request.POST: - officer_statistics_past_higher_search = 1 - form = SearchHigherRecord(request.POST) - if form.is_valid(): - # getting all the variables send through form - if form.cleaned_data['stuname']: - stuname = form.cleaned_data['stuname'] - try: - first_name = stuname.split(" ")[0] - last_name = stuname.split(" ")[1] - except: - first_name = stuname - last_name = '' - else: - stuname = '' - first_name = '' - last_name = '' - if form.cleaned_data['test_type']: - test_type = form.cleaned_data['test_type'] - else: - test_type = '' - if form.cleaned_data['uname']: - uname = form.cleaned_data['uname'] - else: - uname = '' - if form.cleaned_data['test_score']: - test_score = form.cleaned_data['test_score'] - else: - test_score = 0 - if form.cleaned_data['roll']: - rollno = form.cleaned_data['roll'] - else: - rollno = '' - if form.cleaned_data['year']: - year = form.cleaned_data['year'] - # result of the query when year is given - higherrecord = StudentRecord.objects.select_related('unique_id','record_id').filter(Q(record_id__in=PlacementRecord.objects.filter - (Q(placement_type="HIGHER STUDIES", - test_type__icontains=test_type, - name__icontains=uname, year=year, - test_score__gte=test_score)), - unique_id__in=Student.objects.filter - ((Q(id__in=ExtraInfo.objects.filter - (Q(user__in=User.objects.filter - (Q(first_name__icontains=first_name, - last_name__icontains=last_name)), - id__icontains=rollno)) - ))))) - - p2 = PlacementRecord.objects.filter( - Q(placement_type="HIGHER STUDIES", name__icontains=stuname, year__icontains=year)) - - """else: - # result of the query when year is not given - higherrecord = StudentRecord.objects.select_related('unique_id','record_id').filter( - Q(record_id__in=PlacementRecord.objects.filter - (Q(placement_type="HIGHER STUDIES", - test_type__icontains=test_type, - name__icontains=uname, - test_score__gte=test_score)), - unique_id__in=Student.objects.filter - ((Q(id__in=ExtraInfo.objects.filter - (Q(user__in=User.objects.filter - (Q(first_name__icontains=first_name, - last_name__icontains=last_name)), - id__icontains=rollno)) - ))))) - request.session['first_name'] = first_name - request.session['last_name'] = last_name - request.session['test_score'] = test_score - request.session['uname'] = uname - request.session['test_type'] = test_type - request.session['rollno'] = rollno - request.session['year'] = form.cleaned_data['year']""" - - total_query2 = p2.count() - - if total_query2 > 30: - pagination_higher = 1 - paginator = Paginator(p2, 30) - page = request.GET.get('page', 1) - p2 = paginator.page(page) - page = int(page) - total_page = int(page+3) - - if page < (paginator.num_pages-3): - if total_query2 > 30 and total_query2 <= 60: - page_range = range(1, 3) - else: - page_range = range(1, total_page+1) - - if page >= 5: - is_disabled = 1 - page_range = range(page-2, total_page) - else: - if page >= 5: - is_disabled = 1 - page_range = range(page-2, paginator.num_pages+1) - else: - page_range = range(page-2, paginator.num_pages+1) - else: - pagination_higher = 0 - else: - if request.GET.get('page') != None: - try: - if request.session['year']: - higherrecord = StudentRecord.objects.select_related('unique_id','record_id').filter( - Q(record_id__in=PlacementRecord.objects.filter( - Q(placement_type="HIGHER STUDIES", - test_type__icontains=request.session['test_type'], - name__icontains=request.session['uname'], - year=request.session['year'], - test_score__gte=request.session['test_score'])), - unique_id__in=Student.objects.filter( - (Q(id__in=ExtraInfo.objects.filter( - Q(user__in=User.objects.filter( - Q(first_name__icontains=request.session['first_name'], - last_name__icontains=request.session['last_name'])), - id__icontains=request.session['rollno'])) - ))))) - else: - higherrecord = StudentRecord.objects.select_related('unique_id','record_id').filter( - Q(record_id__in=PlacementRecord.objects.filter( - Q(placement_type="HIGHER STUDIES", - test_type__icontains=request.session['test_type'], - name__icontains=request.session['uname'], - test_score__gte=request.session['test_score'])), - unique_id__in=Student.objects.filter - ((Q(id__in=ExtraInfo.objects.filter( - Q(user__in=User.objects.filter( - Q(first_name__icontains=request.session['first_name'], - last_name__icontains=request.session['last_name'])), - id__icontains=request.session['rollno'])) - ))))) - except: - higherrecord = '' - - if higherrecord != '': - total_query = higherrecord.count() - else: - total_query = 0 - - if total_query > 30: - no_pagination = 1 - paginator = Paginator(higherrecord, 30) - page = request.GET.get('page', 1) - higherrecord = paginator.page(page) - page = int(page) - total_page = int(page + 3) - - if page<(paginator.num_pages-3): - if total_query > 30 and total_query <=60: - page_range = range(1, 3) - else: - page_range = range(1, total_page+1) - - if page >= 5: - is_disabled = 1 - page_range = range(page-2, total_page) - else: - if page >= 5: - is_disabled = 1 - page_range = range(page-2, paginator.num_pages+1) - else: - page_range = range(1, paginator.num_pages+1) - else: - no_pagination = 0 - else: - higherrecord = '' - if total_query2!=0: - higher_search_record=p2 - - context = { - 'form2' : form2, - 'form3' : form3, - 'form4' : form4, - 'current' : current, - 'current1' : current1, - 'current2' : current2, - - - 'all_records': all_records, #for flashing all placement Schedule - - 'placement_search_record': placement_search_record, - 'pbi_search_record': pbi_search_record, - 'higher_search_record': higher_search_record, - - - - 'statistics_tab' : statistics_tab, - 'pbirecord' : pbirecord, - 'placementrecord' : placementrecord, - 'higherrecord' : higherrecord, - 'years' : years, - 'records' : records, - 'delete_operation' : delete_operation, - 'page_range': page_range, - 'paginator': paginator, - 'pagination_placement': pagination_placement, - 'pagination_pbi': pagination_pbi, - 'pagination_higher': pagination_higher, - 'is_disabled': is_disabled, - 'officer_statistics_past_pbi_search': officer_statistics_past_pbi_search, - 'officer_statistics_past_higher_search': officer_statistics_past_higher_search - } - - return render(request, 'placementModule/placementstatistics.html', context) - - - -def get_reference_list(request): - if request.method == 'POST': - - user = request.user - profile = get_object_or_404(ExtraInfo, Q(user=user)) - student = get_object_or_404(Student, Q(id=profile.id)) - print(student) - reference_objects = Reference.select_related('unique_id').objects.filter(unique_id=student) - reference_objects = serializers.serialize('json', list(reference_objects)) - - context = { - 'reference_objs': reference_objects - } - return JsonResponse(context) - - -# Ajax for the company name dropdown for CompanyName when filling AddSchedule -def company_name_dropdown(request): - if request.method == 'POST': - current_value = request.POST.get('current_value') - company_names = CompanyDetails.objects.filter(Q(company_name__startswith=current_value)) - company_name = [] - for name in company_names: - company_name.append(name.company_name) - - context = { - 'company_names': company_name - } - - return JsonResponse(context) - - -# Ajax for all the roles in the dropdown -def checking_roles(request): - if request.method == 'POST': - current_value = request.POST.get('current_value') - all_roles = Role.objects.filter(Q(role__startswith=current_value)) - role_name = [] - for role in all_roles: - role_name.append(role.role) - return JsonResponse({'all_roles': role_name}) - -@login_required -def Placement__Schedule(request): - ''' - function include the functionality of first tab of UI - for student, placement officer & placement chairman - - placement officer & placement chairman - - can add schedule - - can delete schedule - student - - accepted or declined schedule - - ''' - user = request.user - profile = get_object_or_404(ExtraInfo, Q(user=user)) - schedule_tab = 1 - placementstatus = '' - - - form5 = AddSchedule(initial={}) - current1 = HoldsDesignation.objects.filter(Q(working=user, designation__name="placement chairman")) - current2 = HoldsDesignation.objects.filter(Q(working=user, designation__name="placement officer")) - current = HoldsDesignation.objects.filter(Q(working=user, designation__name="student")) - print(current) - - # If the user is Student - if current: - student = get_object_or_404(Student, Q(id=profile.id)) - - # Student view for showing accepted or declined schedule - if request.method == 'POST': - if 'studentapprovesubmit' in request.POST: - status = PlacementStatus.objects.select_related('unique_id','notify_id').filter( - pk=request.POST['studentapprovesubmit']).update( - invitation='ACCEPTED', - timestamp=timezone.now()) - if 'studentdeclinesubmit' in request.POST: - status = PlacementStatus.objects.select_related('unique_id','notify_id').filter( - Q(pk=request.POST['studentdeclinesubmit'])).update( - invitation='REJECTED', - timestamp=timezone.now()) - - if 'educationsubmit' in request.POST: - form = AddEducation(request.POST) - if form.is_valid(): - institute = form.cleaned_data['institute'] - degree = form.cleaned_data['degree'] - grade = form.cleaned_data['grade'] - stream = form.cleaned_data['stream'] - sdate = form.cleaned_data['sdate'] - edate = form.cleaned_data['edate'] - education_obj = Education.objects.select_related('unique_id').create( - unique_id=student, degree=degree, - grade=grade, institute=institute, - stream=stream, sdate=sdate, edate=edate) - education_obj.save() - if 'profilesubmit' in request.POST: - about_me = request.POST.get('about') - age = request.POST.get('age') - address = request.POST.get('address') - contact = request.POST.get('contact') - pic = request.POST.get('pic') - # futu = request.POST.get('futu') - # print(studentplacement_obj.future_aspect) - # print('fut=', fut) - # print('futu=', futu) - # if studentplacement_obj.future_aspect == "HIGHER STUDIES": - # if futu == 2: - # studentplacement_obj.future_aspect = "PLACEMENT" - # elif studentplacement_obj.future_aspect == "PLACEMENT": - # if futu == None: - # studentplacement_obj.future_aspect = "HIGHER STUDIES" - extrainfo_obj = ExtraInfo.objects.get(user=user) - extrainfo_obj.about_me = about_me - extrainfo_obj.age = age - extrainfo_obj.address = address - extrainfo_obj.phone_no = contact - extrainfo_obj.profile_picture = pic - extrainfo_obj.save() - profile = get_object_or_404(ExtraInfo, Q(user=user)) - if 'skillsubmit' in request.POST: - form = AddSkill(request.POST) - if form.is_valid(): - skill = form.cleaned_data['skill'] - skill_rating = form.cleaned_data['skill_rating'] - has_obj = Has.objects.select_related('skill_id','unique_id').create(unique_id=student, - skill_id=Skill.objects.get(skill=skill), - skill_rating = skill_rating) - has_obj.save() - if 'achievementsubmit' in request.POST: - form = AddAchievement(request.POST) - if form.is_valid(): - achievement = form.cleaned_data['achievement'] - achievement_type = form.cleaned_data['achievement_type'] - description = form.cleaned_data['description'] - issuer = form.cleaned_data['issuer'] - date_earned = form.cleaned_data['date_earned'] - achievement_obj = Achievement.objects.select_related('unique_id').create(unique_id=student, - achievement=achievement, - achievement_type=achievement_type, - description=description, - issuer=issuer, - date_earned=date_earned) - achievement_obj.save() - if 'publicationsubmit' in request.POST: - form = AddPublication(request.POST) - if form.is_valid(): - publication_title = form.cleaned_data['publication_title'] - description = form.cleaned_data['description'] - publisher = form.cleaned_data['publisher'] - publication_date = form.cleaned_data['publication_date'] - publication_obj = Publication.objects.select_related('unique_id').create(unique_id=student, - publication_title= - publication_title, - publisher=publisher, - description=description, - publication_date=publication_date) - publication_obj.save() - if 'patentsubmit' in request.POST: - form = AddPatent(request.POST) - if form.is_valid(): - patent_name = form.cleaned_data['patent_name'] - description = form.cleaned_data['description'] - patent_office = form.cleaned_data['patent_office'] - patent_date = form.cleaned_data['patent_date'] - patent_obj = Patent.objects.select_related('unique_id').create(unique_id=student, patent_name=patent_name, - patent_office=patent_office, - description=description, - patent_date=patent_date) - patent_obj.save() - if 'coursesubmit' in request.POST: - form = AddCourse(request.POST) - if form.is_valid(): - course_name = form.cleaned_data['course_name'] - description = form.cleaned_data['description'] - license_no = form.cleaned_data['license_no'] - sdate = form.cleaned_data['sdate'] - edate = form.cleaned_data['edate'] - course_obj = Course.objects.select_related('unique_id').create(unique_id=student, course_name=course_name, - license_no=license_no, - description=description, - sdate=sdate, edate=edate) - course_obj.save() - if 'projectsubmit' in request.POST: - form = AddProject(request.POST) - if form.is_valid(): - project_name = form.cleaned_data['project_name'] - project_status = form.cleaned_data['project_status'] - summary = form.cleaned_data['summary'] - project_link = form.cleaned_data['project_link'] - sdate = form.cleaned_data['sdate'] - edate = form.cleaned_data['edate'] - project_obj = Project.objects.create(unique_id=student, summary=summary, - project_name=project_name, - project_status=project_status, - project_link=project_link, - sdate=sdate, edate=edate) - project_obj.save() - if 'experiencesubmit' in request.POST: - form = AddExperience(request.POST) - if form.is_valid(): - title = form.cleaned_data['title'] - status = form.cleaned_data['status'] - company = form.cleaned_data['company'] - location = form.cleaned_data['location'] - description = form.cleaned_data['description'] - sdate = form.cleaned_data['sdate'] - edate = form.cleaned_data['edate'] - experience_obj = Experience.objects.select_related('unique_id').create(unique_id=student, title=title, - company=company, location=location, - status=status, - description=description, - sdate=sdate, edate=edate) - experience_obj.save() - - if 'deleteskill' in request.POST: - hid = request.POST['deleteskill'] - hs = Has.objects.select_related('skill_id','unique_id').get(Q(pk=hid)) - hs.delete() - if 'deleteedu' in request.POST: - hid = request.POST['deleteedu'] - hs = Education.objects.select_related('unique_id').get(Q(pk=hid)) - hs.delete() - if 'deletecourse' in request.POST: - hid = request.POST['deletecourse'] - hs = Course.objects.get(Q(pk=hid)) - hs.delete() - if 'deleteexp' in request.POST: - hid = request.POST['deleteexp'] - hs = Experience.objects.get(Q(pk=hid)) - hs.delete() - if 'deletepro' in request.POST: - hid = request.POST['deletepro'] - hs = Project.objects.get(Q(pk=hid)) - hs.delete() - if 'deleteach' in request.POST: - hid = request.POST['deleteach'] - hs = Achievement.objects.get(Q(pk=hid)) - hs.delete() - if 'deletepub' in request.POST: - hid = request.POST['deletepub'] - hs = Publication.objects.select_related('unique_id').get(Q(pk=hid)) - hs.delete() - if 'deletepat' in request.POST: - hid = request.POST['deletepat'] - hs = Patent.objects.get(Q(pk=hid)) - hs.delete() - - placementschedule = PlacementSchedule.objects.select_related('notify_id').filter( - Q(placement_date__gte=date.today())).values_list('notify_id', flat=True) - - placementstatus = PlacementStatus.objects.select_related('unique_id','notify_id').filter( - Q(unique_id=student, - notify_id__in=placementschedule)).order_by('-timestamp') - - - check_invitation_date(placementstatus) - - # facult and other staff view only statistics - if not (current or current1 or current2): - return redirect('/placement/statistics/') - - # delete the schedule - if 'deletesch' in request.POST: - delete_sch_key = request.POST['delete_sch_key'] - try: - placement_schedule = PlacementSchedule.objects.select_related('notify_id').get(pk = delete_sch_key) - NotifyStudent.objects.get(pk=placement_schedule.notify_id.id).delete() - placement_schedule.delete() - messages.success(request, 'Schedule Deleted Successfully') - except Exception as e: - messages.error(request, 'Problem Occurred for Schedule Delete!!!') - - # saving all the schedule details - if 'schedulesubmit' in request.POST: - form5 = AddSchedule(request.POST, request.FILES) - if form5.is_valid(): - company_name = form5.cleaned_data['company_name'] - placement_date = form5.cleaned_data['placement_date'] - location = form5.cleaned_data['location'] - ctc = form5.cleaned_data['ctc'] - time = form5.cleaned_data['time'] - attached_file = form5.cleaned_data['attached_file'] - placement_type = form5.cleaned_data['placement_type'] - role_offered = request.POST.get('role') - description = form5.cleaned_data['description'] - - try: - comp_name = CompanyDetails.objects.filter(company_name=company_name)[0] - except: - CompanyDetails.objects.create(company_name=company_name) - - try: - role = Role.objects.filter(role=role_offered)[0] - except: - role = Role.objects.create(role=role_offered) - role.save() - - - notify = NotifyStudent.objects.create(placement_type=placement_type, - company_name=company_name, - description=description, - ctc=ctc, - timestamp=timezone.now()) - - schedule = PlacementSchedule.objects.select_related('notify_id').create(notify_id=notify, - title=company_name, - description=description, - placement_date=placement_date, - attached_file = attached_file, - role=role, - location=location, time=time) - - notify.save() - schedule.save() - messages.success(request, "Schedule Added Successfull!!") - - - schedules = PlacementSchedule.objects.select_related('notify_id').all() - - - context = { - 'current': current, - 'current1': current1, - 'current2': current2, - 'schedule_tab': schedule_tab, - 'schedules': schedules, - 'placementstatus': placementstatus, - 'form5': form5, - } - - return render(request, 'placementModule/placement.html', context) - - - -def invite_status(request): - ''' - function to check the invitation status - ''' - user = request.user - strecord_tab = 1 - mnpbi_tab = 0 - mnplacement_post = 0 - mnpbi_post = 0 - invitation_status_tab = 1 - placementstatus_placement = [] - placementstatus_pbi = [] - mnplacement_tab = 1 - - no_pagination = 1 - is_disabled = 0 - paginator = '' - page_range = '' - placement_get_request = False - pbi_get_request = False - - # invitation status for placement - if 'studentplacementsearchsubmit' in request.POST: - mnplacement_post = 1 - mnpbi_post = 0 - form = ManagePlacementRecord(request.POST) - - if form.is_valid(): - if form.cleaned_data['stuname']: - stuname = form.cleaned_data['stuname'] - else: - stuname = '' - if form.cleaned_data['ctc']: - ctc = form.cleaned_data['ctc'] - else: - ctc = 0 - if form.cleaned_data['company']: - cname = form.cleaned_data['company'] - else: - cname = '' - if form.cleaned_data['roll']: - rollno = form.cleaned_data['roll'] - else: - rollno = '' - - request.session['mn_stuname'] = stuname - request.session['mn_ctc'] = ctc - request.session['mn_cname'] = cname - request.session['mn_rollno'] = rollno - - placementstatus_placement = PlacementStatus.objects.select_related('unique_id','notify_id').filter(Q(notify_id__in=NotifyStudent.objects.filter - (Q(placement_type="PLACEMENT", - company_name__icontains=cname, - ctc__gte=ctc)), - unique_id__in=Student.objects.filter - ((Q(id__in=ExtraInfo.objects.filter - (Q(user__in=User.objects.filter - (Q(first_name__icontains=stuname)), - id__icontains=rollno)) - ))))) - # pagination stuff starts from here - total_query = placementstatus_placement.count() - - if total_query > 30: - no_pagination = 1 - paginator = Paginator(placementstatus_placement, 30) - page = request.GET.get('page', 1) - placementstatus_placement = paginator.page(page) - page = int(page) - total_page = int(page + 3) - - if page<(paginator.num_pages-3): - if total_query > 30 and total_query <=60: - page_range = range(1, 3) - else: - page_range = range(1, total_page+1) - - if page >= 5: - is_disabled = 1 - page_range = range(page-2, total_page) - else: - if page >= 5: - is_disabled = 1 - page_range = range(page-2, paginator.num_pages+1) - else: - page_range = range(1, paginator.num_pages+1) - else: - no_pagination = 0 - else: - # when the request from pagination with some page number - if request.GET.get('placement_page') != None: - mnplacement_post = 1 - mnpbi_post = 0 - no_pagination = 1 - try: - placementstatus_placement = PlacementStatus.objects.select_related('unique_id','notify_id').filter(Q(notify_id__in=NotifyStudent.objects.filter - (Q(placement_type="PLACEMENT", - company_name__icontains=request.session['mn_cname'], - ctc__gte=request.session['mn_ctc'])), - unique_id__in=Student.objects.filter - ((Q(id__in=ExtraInfo.objects.filter - (Q(user__in=User.objects.filter - (Q(first_name__icontains=request.session['mn_stuname'])), - id__icontains=request.session['mn_rollno'])) - ))))) - except: - placementstatus_placement = [] - - if placementstatus_placement != '': - total_query = placementstatus_placement.count() - else: - total_query = 0 - - if total_query > 30: - paginator = Paginator(placementstatus_placement, 30) - page = request.GET.get('placement_page', 1) - placementstatus_placement = paginator.page(page) - page = int(page) - total_page = int(page + 3) - - if page<(paginator.num_pages-3): - if total_query > 30 and total_query <=60: - page_range = range(1, 3) - else: - page_range = range(1, total_page+1) - - if page >= 5: - is_disabled = 1 - page_range = range(page-2, total_page) - else: - if page >= 5: - is_disabled = 1 - page_range = range(page-2, paginator.num_pages+1) - else: - page_range = range(1, paginator.num_pages+1) - else: - no_pagination = 0 - - # invitation status for pbi - if 'studentpbisearchsubmit' in request.POST: - mnpbi_tab = 1 - mnpbi_post = 1 - mnplacement_post = 0 - form = ManagePbiRecord(request.POST) - if form.is_valid(): - if form.cleaned_data['stuname']: - stuname = form.cleaned_data['stuname'] - else: - stuname = '' - if form.cleaned_data['ctc']: - ctc = form.cleaned_data['ctc'] - else: - ctc = 0 - if form.cleaned_data['company']: - cname = form.cleaned_data['company'] - else: - cname = '' - if form.cleaned_data['roll']: - rollno = form.cleaned_data['roll'] - else: - rollno = '' - request.session['mn_pbi_stuname'] = stuname - request.session['mn_pbi_ctc'] = ctc - request.session['mn_pbi_cname'] = cname - request.session['mn_pbi_rollno'] = rollno - placementstatus_pbi = PlacementStatus.objects.select_related('unique_id','notify_id').filter( - Q(notify_id__in=NotifyStudent.objects.filter( - Q(placement_type="PBI", - company_name__icontains=cname, - ctc__gte=ctc)), - unique_id__in=Student.objects.filter( - (Q(id__in=ExtraInfo.objects.filter( - Q(user__in=User.objects.filter( - Q(first_name__icontains=stuname)), - id__icontains=rollno))))))).order_by('id') - - total_query = placementstatus_pbi.count() - - if total_query > 30: - no_pagination = 1 - paginator = Paginator(placementstatus_pbi, 30) - page = request.GET.get('pbi_page', 1) - placementstatus_pbi = paginator.page(page) - page = int(page) - total_page = int(page + 3) - - if page<(paginator.num_pages-3): - if total_query > 30 and total_query <=60: - page_range = range(1, 3) - else: - page_range = range(1, total_page+1) - - if page >= 5: - is_disabled = 1 - page_range = range(page-2, total_page) - else: - if page >= 5: - is_disabled = 1 - page_range = range(page-2, paginator.num_pages+1) - else: - page_range = range(1, paginator.num_pages+1) - else: - no_pagination = 0 - else: - if request.GET.get('pbi_page') != None: - mnpbi_tab = 1 - mnpbi_post = 1 - no_pagination = 1 - try: - placementstatus_pbi = PlacementStatus.objects.select_related('unique_id','notify_id').filter( - Q(notify_id__in=NotifyStudent.objects.filter( - Q(placement_type="PBI", - company_name__icontains=request.session['mn_pbi_cname'], - ctc__gte=request.session['mn_pbi_ctc'])), - unique_id__in=Student.objects.filter( - (Q(id__in=ExtraInfo.objects.filter( - Q(user__in=User.objects.filter( - Q(first_name__icontains=request.session['mn_pbi_stuname'])), - id__icontains=request.session['mn_pbi_rollno'])) - ))))) - except: - placementstatus_pbi = '' - - if placementstatus_pbi != '': - total_query = placementstatus_pbi.count() - else: - total_query = 0 - if total_query > 30: - paginator = Paginator(placementstatus_pbi, 30) - page = request.GET.get('pbi_page', 1) - placementstatus_pbi = paginator.page(page) - page = int(page) - total_page = int(page + 3) - - if page<(paginator.num_pages-3): - if total_query > 30 and total_query <=60: - page_range = range(1, 3) - else: - page_range = range(1, total_page+1) - - if page >= 5: - is_disabled = 1 - page_range = range(page-2, total_page) - else: - if page >= 5: - is_disabled = 1 - page_range = range(page-2, paginator.num_pages+1) - else: - page_range = range(1, paginator.num_pages+1) - else: - no_pagination = 0 - - - if 'pdf_gen_invitation_status' in request.POST: - - placementstatus = None - if 'pdf_gen_invitation_status_placement' in request.POST: - stuname = request.session['mn_stuname'] - ctc = request.session['mn_ctc'] - cname = request.session['mn_cname'] - rollno = request.session['mn_rollno'] - - placementstatus = PlacementStatus.objects.select_related('unique_id','notify_id').filter(Q(notify_id__in=NotifyStudent.objects.filter - (Q(placement_type="PLACEMENT", - company_name__icontains=cname, - ctc__gte=ctc)), - unique_id__in=Student.objects.filter - ((Q(id__in=ExtraInfo.objects.filter - (Q(user__in=User.objects.filter - (Q(first_name__icontains=stuname)), - id__icontains=rollno)) - ))))) - - if 'pdf_gen_invitation_status_pbi' in request.POST: - stuname = request.session['mn_pbi_stuname'] - ctc = request.session['mn_pbi_ctc'] - cname = request.session['mn_pbi_cname'] - rollno = request.session['mn_pbi_rollno'] - - placementstatus = PlacementStatus.objects.select_related('unique_id','notify_id').filter( - Q(notify_id__in=NotifyStudent.objects.filter( - Q(placement_type="PBI", - company_name__icontains=cname, - ctc__gte=ctc)), - unique_id__in=Student.objects.filter( - (Q(id__in=ExtraInfo.objects.filter( - Q(user__in=User.objects.filter( - Q(first_name__icontains=stuname)), - id__icontains=rollno))))))).order_by('id') - - context = { - 'placementstatus' : placementstatus - } - - return render_to_pdf('placementModule/pdf_invitation_status.html', context) - - if 'excel_gen_invitation_status' in request.POST: - - placementstatus = None - if 'excel_gen_invitation_status_placement' in request.POST: - stuname = request.session['mn_stuname'] - ctc = request.session['mn_ctc'] - cname = request.session['mn_cname'] - rollno = request.session['mn_rollno'] - - placementstatus = PlacementStatus.objects.select_related('unique_id','notify_id').filter(Q(notify_id__in=NotifyStudent.objects.filter - (Q(placement_type="PLACEMENT", - company_name__icontains=cname, - ctc__gte=ctc)), - unique_id__in=Student.objects.filter - ((Q(id__in=ExtraInfo.objects.filter - (Q(user__in=User.objects.filter - (Q(first_name__icontains=stuname)), - id__icontains=rollno)) - ))))) - - if 'excel_gen_invitation_status_pbi' in request.POST: - stuname = request.session['mn_pbi_stuname'] - ctc = request.session['mn_pbi_ctc'] - cname = request.session['mn_pbi_cname'] - rollno = request.session['mn_pbi_rollno'] - - placementstatus = PlacementStatus.objects.select_related('unique_id','notify_id').filter( - Q(notify_id__in=NotifyStudent.objects.filter( - Q(placement_type="PBI", - company_name__icontains=cname, - ctc__gte=ctc)), - unique_id__in=Student.objects.filter( - (Q(id__in=ExtraInfo.objects.filter( - Q(user__in=User.objects.filter( - Q(first_name__icontains=stuname)), - id__icontains=rollno))))))).order_by('id') - - context = { - 'placementstatus' : placementstatus - } - - - return export_to_xls_invitation_status(placementstatus) - - form1 = SearchStudentRecord(initial={}) - form9 = ManagePbiRecord(initial={}) - form11 = ManagePlacementRecord(initial={}) - form13 = SendInvite(initial={}) - current1 = HoldsDesignation.objects.filter(Q(working=user, designation__name="placement chairman")) - current2 = HoldsDesignation.objects.filter(Q(working=user, designation__name="placement officer")) - - context = { - 'form1': form1, - 'form9': form9, - 'form11': form11, - 'form13': form13, - 'invitation_status_tab': invitation_status_tab, - 'mnplacement_post': mnplacement_post, - 'mnpbi_tab': mnpbi_tab, - 'mnplacement_tab': mnplacement_tab, - 'placementstatus_placement': placementstatus_placement, - 'placementstatus_pbi': placementstatus_pbi, - 'current1': current1, - 'current2': current2, - 'strecord_tab': strecord_tab, - 'mnpbi_post': mnpbi_post, - 'page_range': page_range, - 'paginator': paginator, - 'no_pagination': no_pagination, - 'is_disabled': is_disabled, - } - - return render(request, 'placementModule/studentrecords.html', context) - - - -@login_required -def placement(request): - ''' - function include the functionality of first tab of UI - for student, placement officer & placement chairman - - placement officer & placement chairman - - can add schedule - - can delete schedule - student - - accepted or declined schedule - - ''' - user = request.user - profile = get_object_or_404(ExtraInfo, Q(user=user)) - schedule_tab = 1 - placementstatus = '' - - - form5 = AddSchedule(initial={}) - current1 = HoldsDesignation.objects.filter(Q(working=user, designation__name="placement chairman")) - current2 = HoldsDesignation.objects.filter(Q(working=user, designation__name="placement officer")) - current = HoldsDesignation.objects.filter(Q(working=user, designation__name="student")) - print(current) - - # If the user is Student - if current: - student = get_object_or_404(Student, Q(id=profile.id)) - - # Student view for showing accepted or declined schedule - if request.method == 'POST': - if 'studentapprovesubmit' in request.POST: - status = PlacementStatus.objects.select_related('unique_id','notify_id').filter( - pk=request.POST['studentapprovesubmit']).update( - invitation='ACCEPTED', - timestamp=timezone.now()) - if 'studentdeclinesubmit' in request.POST: - status = PlacementStatus.objects.select_related('unique_id','notify_id').filter( - Q(pk=request.POST['studentdeclinesubmit'])).update( - invitation='REJECTED', - timestamp=timezone.now()) - - if 'educationsubmit' in request.POST: - form = AddEducation(request.POST) - if form.is_valid(): - institute = form.cleaned_data['institute'] - degree = form.cleaned_data['degree'] - grade = form.cleaned_data['grade'] - stream = form.cleaned_data['stream'] - sdate = form.cleaned_data['sdate'] - edate = form.cleaned_data['edate'] - education_obj = Education.objects.select_related('unique_id').create( - unique_id=student, degree=degree, - grade=grade, institute=institute, - stream=stream, sdate=sdate, edate=edate) - education_obj.save() - if 'profilesubmit' in request.POST: - about_me = request.POST.get('about') - age = request.POST.get('age') - address = request.POST.get('address') - contact = request.POST.get('contact') - pic = request.POST.get('pic') - - extrainfo_obj = ExtraInfo.objects.get(user=user) - extrainfo_obj.about_me = about_me - extrainfo_obj.age = age - extrainfo_obj.address = address - extrainfo_obj.phone_no = contact - extrainfo_obj.profile_picture = pic - extrainfo_obj.save() - profile = get_object_or_404(ExtraInfo, Q(user=user)) - if 'skillsubmit' in request.POST: - form = AddSkill(request.POST) - if form.is_valid(): - skill = form.cleaned_data['skill'] - skill_rating = form.cleaned_data['skill_rating'] - has_obj = Has.objects.select_related('skill_id','unique_id').create(unique_id=student, - skill_id=Skill.objects.get(skill=skill), - skill_rating = skill_rating) - has_obj.save() - if 'achievementsubmit' in request.POST: - form = AddAchievement(request.POST) - if form.is_valid(): - achievement = form.cleaned_data['achievement'] - achievement_type = form.cleaned_data['achievement_type'] - description = form.cleaned_data['description'] - issuer = form.cleaned_data['issuer'] - date_earned = form.cleaned_data['date_earned'] - achievement_obj = Achievement.objects.select_related('unique_id').create(unique_id=student, - achievement=achievement, - achievement_type=achievement_type, - description=description, - issuer=issuer, - date_earned=date_earned) - achievement_obj.save() - if 'publicationsubmit' in request.POST: - form = AddPublication(request.POST) - if form.is_valid(): - publication_title = form.cleaned_data['publication_title'] - description = form.cleaned_data['description'] - publisher = form.cleaned_data['publisher'] - publication_date = form.cleaned_data['publication_date'] - publication_obj = Publication.objects.select_related('unique_id').create(unique_id=student, - publication_title= - publication_title, - publisher=publisher, - description=description, - publication_date=publication_date) - publication_obj.save() - if 'patentsubmit' in request.POST: - form = AddPatent(request.POST) - if form.is_valid(): - patent_name = form.cleaned_data['patent_name'] - description = form.cleaned_data['description'] - patent_office = form.cleaned_data['patent_office'] - patent_date = form.cleaned_data['patent_date'] - patent_obj = Patent.objects.select_related('unique_id').create(unique_id=student, patent_name=patent_name, - patent_office=patent_office, - description=description, - patent_date=patent_date) - patent_obj.save() - if 'coursesubmit' in request.POST: - form = AddCourse(request.POST) - if form.is_valid(): - course_name = form.cleaned_data['course_name'] - description = form.cleaned_data['description'] - license_no = form.cleaned_data['license_no'] - sdate = form.cleaned_data['sdate'] - edate = form.cleaned_data['edate'] - course_obj = Course.objects.select_related('unique_id').create(unique_id=student, course_name=course_name, - license_no=license_no, - description=description, - sdate=sdate, edate=edate) - course_obj.save() - if 'projectsubmit' in request.POST: - form = AddProject(request.POST) - if form.is_valid(): - project_name = form.cleaned_data['project_name'] - project_status = form.cleaned_data['project_status'] - summary = form.cleaned_data['summary'] - project_link = form.cleaned_data['project_link'] - sdate = form.cleaned_data['sdate'] - edate = form.cleaned_data['edate'] - project_obj = Project.objects.create(unique_id=student, summary=summary, - project_name=project_name, - project_status=project_status, - project_link=project_link, - sdate=sdate, edate=edate) - project_obj.save() - if 'experiencesubmit' in request.POST: - form = AddExperience(request.POST) - if form.is_valid(): - title = form.cleaned_data['title'] - status = form.cleaned_data['status'] - company = form.cleaned_data['company'] - location = form.cleaned_data['location'] - description = form.cleaned_data['description'] - sdate = form.cleaned_data['sdate'] - edate = form.cleaned_data['edate'] - experience_obj = Experience.objects.select_related('unique_id').create(unique_id=student, title=title, - company=company, location=location, - status=status, - description=description, - sdate=sdate, edate=edate) - experience_obj.save() - - if 'deleteskill' in request.POST: - hid = request.POST['deleteskill'] - hs = Has.objects.select_related('skill_id','unique_id').get(Q(pk=hid)) - hs.delete() - if 'deleteedu' in request.POST: - hid = request.POST['deleteedu'] - hs = Education.objects.select_related('unique_id').get(Q(pk=hid)) - hs.delete() - if 'deletecourse' in request.POST: - hid = request.POST['deletecourse'] - hs = Course.objects.get(Q(pk=hid)) - hs.delete() - if 'deleteexp' in request.POST: - hid = request.POST['deleteexp'] - hs = Experience.objects.get(Q(pk=hid)) - hs.delete() - if 'deletepro' in request.POST: - hid = request.POST['deletepro'] - hs = Project.objects.get(Q(pk=hid)) - hs.delete() - if 'deleteach' in request.POST: - hid = request.POST['deleteach'] - hs = Achievement.objects.get(Q(pk=hid)) - hs.delete() - if 'deletepub' in request.POST: - hid = request.POST['deletepub'] - hs = Publication.objects.select_related('unique_id').get(Q(pk=hid)) - hs.delete() - if 'deletepat' in request.POST: - hid = request.POST['deletepat'] - hs = Patent.objects.get(Q(pk=hid)) - hs.delete() - - placementschedule = PlacementSchedule.objects.select_related('notify_id').filter( - Q(placement_date__gte=date.today())).values_list('notify_id', flat=True) - - placementstatus = PlacementStatus.objects.select_related('unique_id','notify_id').filter( - Q(unique_id=student, - notify_id__in=placementschedule)).order_by('-timestamp') - - - check_invitation_date(placementstatus) - - # facult and other staff view only statistics - if not (current or current1 or current2): - return redirect('/placement/statistics/') - - # delete the schedule - if 'deletesch' in request.POST: - delete_sch_key = request.POST['delete_sch_key'] - try: - placement_schedule = PlacementSchedule.objects.select_related('notify_id').get(pk = delete_sch_key) - NotifyStudent.objects.get(pk=placement_schedule.notify_id.id).delete() - placement_schedule.delete() - messages.success(request, 'Schedule Deleted Successfully') - except Exception as e: - messages.error(request, 'Problem Occurred for Schedule Delete!!!') - - # saving all the schedule details - if 'schedulesubmit' in request.POST: - form5 = AddSchedule(request.POST, request.FILES) - if form5.is_valid(): - company_name = form5.cleaned_data['company_name'] - placement_date = form5.cleaned_data['placement_date'] - location = form5.cleaned_data['location'] - ctc = form5.cleaned_data['ctc'] - time = form5.cleaned_data['time'] - attached_file = form5.cleaned_data['attached_file'] - placement_type = form5.cleaned_data['placement_type'] - role_offered = request.POST.get('role') - description = form5.cleaned_data['description'] - - try: - comp_name = CompanyDetails.objects.filter(company_name=company_name)[0] - except: - CompanyDetails.objects.create(company_name=company_name) - - try: - role = Role.objects.filter(role=role_offered)[0] - except: - role = Role.objects.create(role=role_offered) - role.save() - - - notify = NotifyStudent.objects.create(placement_type=placement_type, - company_name=company_name, - description=description, - ctc=ctc, - timestamp=timezone.now()) - - schedule = PlacementSchedule.objects.select_related('notify_id').create(notify_id=notify, - title=company_name, - description=description, - placement_date=placement_date, - attached_file = attached_file, - role=role, - location=location, time=time) - - notify.save() - schedule.save() - messages.success(request, "Schedule Added Successfull!!") - - - schedules = PlacementSchedule.objects.select_related('notify_id').all() - - - context = { - 'current': current, - 'current1': current1, - 'current2': current2, - 'schedule_tab': schedule_tab, - 'schedules': schedules, - 'placementstatus': placementstatus, - 'form5': form5, - } - - return render(request, 'placementModule/placement.html', context) - - -@login_required -def delete_invitation_status(request): - ''' - function to delete the invitation that has been sent to the students - ''' - user = request.user - strecord_tab = 1 - mnpbi_tab = 0 - mnplacement_post = 0 - mnpbi_post = 0 - invitation_status_tab = 1 - placementstatus = [] - - no_pagination = 1 - is_disabled = 0 - paginator = '' - page_range = '' - - if 'deleteinvitationstatus' in request.POST: - delete_invit_status_key = request.POST['deleteinvitationstatus'] - - try: - PlacementStatus.objects.select_related('unique_id','notify_id').get(pk=delete_invit_status_key).delete() - messages.success(request, 'Invitation Deleted Successfully') - except Exception as e: - logger.error(e) - - if 'pbi_tab_active' in request.POST: - mnpbi_tab = 1 - else: - mnplacement_tab = 1 - - form1 = SearchStudentRecord(initial={}) - form9 = ManagePbiRecord(initial={}) - form11 = ManagePlacementRecord(initial={}) - form13 = SendInvite(initial={}) - current1 = HoldsDesignation.objects.filter(Q(working=user, designation__name="placement chairman")) - current2 = HoldsDesignation.objects.filter(Q(working=user, designation__name="placement officer")) - - context = { - 'form1': form1, - 'form9': form9, - 'form11': form11, - 'form13': form13, - 'invitation_status_tab': invitation_status_tab, - 'mnplacement_post': mnplacement_post, - 'mnpbi_tab': mnpbi_tab, - 'placementstatus': placementstatus, - # 'current':current, - 'current1': current1, - 'current2': current2, - 'strecord_tab': strecord_tab, - 'mnpbi_post': mnpbi_post, - 'page_range': page_range, - 'paginator': paginator, - 'no_pagination': no_pagination, - 'is_disabled': is_disabled, - } - - return render(request, 'placementModule/studentrecords.html', context) - - -def invitation_status(request): - ''' - function to check the invitation status - ''' - user = request.user - strecord_tab = 1 - mnpbi_tab = 0 - mnplacement_post = 0 - mnpbi_post = 0 - invitation_status_tab = 1 - placementstatus_placement = [] - placementstatus_pbi = [] - mnplacement_tab = 1 - - no_pagination = 1 - is_disabled = 0 - paginator = '' - page_range = '' - placement_get_request = False - pbi_get_request = False - - # invitation status for placement - if 'studentplacementsearchsubmit' in request.POST: - mnplacement_post = 1 - mnpbi_post = 0 - form = ManagePlacementRecord(request.POST) - - if form.is_valid(): - if form.cleaned_data['stuname']: - stuname = form.cleaned_data['stuname'] - else: - stuname = '' - if form.cleaned_data['ctc']: - ctc = form.cleaned_data['ctc'] - else: - ctc = 0 - if form.cleaned_data['company']: - cname = form.cleaned_data['company'] - else: - cname = '' - if form.cleaned_data['roll']: - rollno = form.cleaned_data['roll'] - else: - rollno = '' - - request.session['mn_stuname'] = stuname - request.session['mn_ctc'] = ctc - request.session['mn_cname'] = cname - request.session['mn_rollno'] = rollno - - placementstatus_placement = PlacementStatus.objects.select_related('unique_id','notify_id').filter(Q(notify_id__in=NotifyStudent.objects.filter - (Q(placement_type="PLACEMENT", - company_name__icontains=cname, - ctc__gte=ctc)), - unique_id__in=Student.objects.filter - ((Q(id__in=ExtraInfo.objects.filter - (Q(user__in=User.objects.filter - (Q(first_name__icontains=stuname)), - id__icontains=rollno)) - ))))) - # pagination stuff starts from here - total_query = placementstatus_placement.count() - - if total_query > 30: - no_pagination = 1 - paginator = Paginator(placementstatus_placement, 30) - page = request.GET.get('page', 1) - placementstatus_placement = paginator.page(page) - page = int(page) - total_page = int(page + 3) - - if page<(paginator.num_pages-3): - if total_query > 30 and total_query <=60: - page_range = range(1, 3) - else: - page_range = range(1, total_page+1) - - if page >= 5: - is_disabled = 1 - page_range = range(page-2, total_page) - else: - if page >= 5: - is_disabled = 1 - page_range = range(page-2, paginator.num_pages+1) - else: - page_range = range(1, paginator.num_pages+1) - else: - no_pagination = 0 - else: - # when the request from pagination with some page number - if request.GET.get('placement_page') != None: - mnplacement_post = 1 - mnpbi_post = 0 - no_pagination = 1 - try: - placementstatus_placement = PlacementStatus.objects.select_related('unique_id','notify_id').filter(Q(notify_id__in=NotifyStudent.objects.filter - (Q(placement_type="PLACEMENT", - company_name__icontains=request.session['mn_cname'], - ctc__gte=request.session['mn_ctc'])), - unique_id__in=Student.objects.filter - ((Q(id__in=ExtraInfo.objects.filter - (Q(user__in=User.objects.filter - (Q(first_name__icontains=request.session['mn_stuname'])), - id__icontains=request.session['mn_rollno'])) - ))))) - except: - placementstatus_placement = [] - - if placementstatus_placement != '': - total_query = placementstatus_placement.count() - else: - total_query = 0 - - if total_query > 30: - paginator = Paginator(placementstatus_placement, 30) - page = request.GET.get('placement_page', 1) - placementstatus_placement = paginator.page(page) - page = int(page) - total_page = int(page + 3) - - if page<(paginator.num_pages-3): - if total_query > 30 and total_query <=60: - page_range = range(1, 3) - else: - page_range = range(1, total_page+1) - - if page >= 5: - is_disabled = 1 - page_range = range(page-2, total_page) - else: - if page >= 5: - is_disabled = 1 - page_range = range(page-2, paginator.num_pages+1) - else: - page_range = range(1, paginator.num_pages+1) - else: - no_pagination = 0 - - # invitation status for pbi - if 'studentpbisearchsubmit' in request.POST: - mnpbi_tab = 1 - mnpbi_post = 1 - mnplacement_post = 0 - form = ManagePbiRecord(request.POST) - if form.is_valid(): - if form.cleaned_data['stuname']: - stuname = form.cleaned_data['stuname'] - else: - stuname = '' - if form.cleaned_data['ctc']: - ctc = form.cleaned_data['ctc'] - else: - ctc = 0 - if form.cleaned_data['company']: - cname = form.cleaned_data['company'] - else: - cname = '' - if form.cleaned_data['roll']: - rollno = form.cleaned_data['roll'] - else: - rollno = '' - request.session['mn_pbi_stuname'] = stuname - request.session['mn_pbi_ctc'] = ctc - request.session['mn_pbi_cname'] = cname - request.session['mn_pbi_rollno'] = rollno - placementstatus_pbi = PlacementStatus.objects.select_related('unique_id','notify_id').filter( - Q(notify_id__in=NotifyStudent.objects.filter( - Q(placement_type="PBI", - company_name__icontains=cname, - ctc__gte=ctc)), - unique_id__in=Student.objects.filter( - (Q(id__in=ExtraInfo.objects.filter( - Q(user__in=User.objects.filter( - Q(first_name__icontains=stuname)), - id__icontains=rollno))))))).order_by('id') - - total_query = placementstatus_pbi.count() - - if total_query > 30: - no_pagination = 1 - paginator = Paginator(placementstatus_pbi, 30) - page = request.GET.get('pbi_page', 1) - placementstatus_pbi = paginator.page(page) - page = int(page) - total_page = int(page + 3) - - if page<(paginator.num_pages-3): - if total_query > 30 and total_query <=60: - page_range = range(1, 3) - else: - page_range = range(1, total_page+1) - - if page >= 5: - is_disabled = 1 - page_range = range(page-2, total_page) - else: - if page >= 5: - is_disabled = 1 - page_range = range(page-2, paginator.num_pages+1) - else: - page_range = range(1, paginator.num_pages+1) - else: - no_pagination = 0 - else: - if request.GET.get('pbi_page') != None: - mnpbi_tab = 1 - mnpbi_post = 1 - no_pagination = 1 - try: - placementstatus_pbi = PlacementStatus.objects.select_related('unique_id','notify_id').filter( - Q(notify_id__in=NotifyStudent.objects.filter( - Q(placement_type="PBI", - company_name__icontains=request.session['mn_pbi_cname'], - ctc__gte=request.session['mn_pbi_ctc'])), - unique_id__in=Student.objects.filter( - (Q(id__in=ExtraInfo.objects.filter( - Q(user__in=User.objects.filter( - Q(first_name__icontains=request.session['mn_pbi_stuname'])), - id__icontains=request.session['mn_pbi_rollno'])) - ))))) - except: - placementstatus_pbi = '' - - if placementstatus_pbi != '': - total_query = placementstatus_pbi.count() - else: - total_query = 0 - if total_query > 30: - paginator = Paginator(placementstatus_pbi, 30) - page = request.GET.get('pbi_page', 1) - placementstatus_pbi = paginator.page(page) - page = int(page) - total_page = int(page + 3) - - if page<(paginator.num_pages-3): - if total_query > 30 and total_query <=60: - page_range = range(1, 3) - else: - page_range = range(1, total_page+1) - - if page >= 5: - is_disabled = 1 - page_range = range(page-2, total_page) - else: - if page >= 5: - is_disabled = 1 - page_range = range(page-2, paginator.num_pages+1) - else: - page_range = range(1, paginator.num_pages+1) - else: - no_pagination = 0 - - - if 'pdf_gen_invitation_status' in request.POST: - - placementstatus = None - if 'pdf_gen_invitation_status_placement' in request.POST: - stuname = request.session['mn_stuname'] - ctc = request.session['mn_ctc'] - cname = request.session['mn_cname'] - rollno = request.session['mn_rollno'] - - placementstatus = PlacementStatus.objects.select_related('unique_id','notify_id').filter(Q(notify_id__in=NotifyStudent.objects.filter - (Q(placement_type="PLACEMENT", - company_name__icontains=cname, - ctc__gte=ctc)), - unique_id__in=Student.objects.filter - ((Q(id__in=ExtraInfo.objects.filter - (Q(user__in=User.objects.filter - (Q(first_name__icontains=stuname)), - id__icontains=rollno)) - ))))) - - if 'pdf_gen_invitation_status_pbi' in request.POST: - stuname = request.session['mn_pbi_stuname'] - ctc = request.session['mn_pbi_ctc'] - cname = request.session['mn_pbi_cname'] - rollno = request.session['mn_pbi_rollno'] - - placementstatus = PlacementStatus.objects.select_related('unique_id','notify_id').filter( - Q(notify_id__in=NotifyStudent.objects.filter( - Q(placement_type="PBI", - company_name__icontains=cname, - ctc__gte=ctc)), - unique_id__in=Student.objects.filter( - (Q(id__in=ExtraInfo.objects.filter( - Q(user__in=User.objects.filter( - Q(first_name__icontains=stuname)), - id__icontains=rollno))))))).order_by('id') - - context = { - 'placementstatus' : placementstatus - } - - return render_to_pdf('placementModule/pdf_invitation_status.html', context) - - if 'excel_gen_invitation_status' in request.POST: - - placementstatus = None - if 'excel_gen_invitation_status_placement' in request.POST: - stuname = request.session['mn_stuname'] - ctc = request.session['mn_ctc'] - cname = request.session['mn_cname'] - rollno = request.session['mn_rollno'] - - placementstatus = PlacementStatus.objects.select_related('unique_id','notify_id').filter(Q(notify_id__in=NotifyStudent.objects.filter - (Q(placement_type="PLACEMENT", - company_name__icontains=cname, - ctc__gte=ctc)), - unique_id__in=Student.objects.filter - ((Q(id__in=ExtraInfo.objects.filter - (Q(user__in=User.objects.filter - (Q(first_name__icontains=stuname)), - id__icontains=rollno)) - ))))) - - if 'excel_gen_invitation_status_pbi' in request.POST: - stuname = request.session['mn_pbi_stuname'] - ctc = request.session['mn_pbi_ctc'] - cname = request.session['mn_pbi_cname'] - rollno = request.session['mn_pbi_rollno'] - - placementstatus = PlacementStatus.objects.select_related('unique_id','notify_id').filter( - Q(notify_id__in=NotifyStudent.objects.filter( - Q(placement_type="PBI", - company_name__icontains=cname, - ctc__gte=ctc)), - unique_id__in=Student.objects.filter( - (Q(id__in=ExtraInfo.objects.filter( - Q(user__in=User.objects.filter( - Q(first_name__icontains=stuname)), - id__icontains=rollno))))))).order_by('id') - - context = { - 'placementstatus' : placementstatus - } - - - return export_to_xls_invitation_status(placementstatus) - - form1 = SearchStudentRecord(initial={}) - form9 = ManagePbiRecord(initial={}) - form11 = ManagePlacementRecord(initial={}) - form13 = SendInvite(initial={}) - current1 = HoldsDesignation.objects.filter(Q(working=user, designation__name="placement chairman")) - current2 = HoldsDesignation.objects.filter(Q(working=user, designation__name="placement officer")) - - context = { - 'form1': form1, - 'form9': form9, - 'form11': form11, - 'form13': form13, - 'invitation_status_tab': invitation_status_tab, - 'mnplacement_post': mnplacement_post, - 'mnpbi_tab': mnpbi_tab, - 'mnplacement_tab': mnplacement_tab, - 'placementstatus_placement': placementstatus_placement, - 'placementstatus_pbi': placementstatus_pbi, - 'current1': current1, - 'current2': current2, - 'strecord_tab': strecord_tab, - 'mnpbi_post': mnpbi_post, - 'page_range': page_range, - 'paginator': paginator, - 'no_pagination': no_pagination, - 'is_disabled': is_disabled, - } - - return render(request, 'placementModule/studentrecords.html', context) - - -@login_required -def student_records(request): - ''' - function for searching the records of student - ''' - if request.user.is_staff==True: - user = request.user - strecord_tab = 1 - no_pagination = 0 - is_disabled = 0 - paginator = '' - page_range = '' - mnplacement_tab = 1 - - form1 = SearchStudentRecord(initial={}) - form9 = ManagePbiRecord(initial={}) - form11 = ManagePlacementRecord(initial={}) - form13 = SendInvite(initial={}) - current1 = HoldsDesignation.objects.filter(Q(working=user, designation__name="placement chairman")) - current2 = HoldsDesignation.objects.filter(Q(working=user, designation__name="placement officer")) - - # querying the students details a/c to the input data - if 'recordsubmit' in request.POST: - student_record_check = 1 - form1 = SearchStudentRecord(request.POST) - if form1.is_valid(): - if form1.cleaned_data['name']: - name = form1.cleaned_data['name'] - else: - name = '' - if form1.cleaned_data['rollno']: - rollno = form1.cleaned_data['rollno'] - else: - rollno = '' - - programme = form1.cleaned_data['programme'] - - department = [] - if form1.cleaned_data['dep_btech']: - department.extend(form1.cleaned_data['dep_btech']) - if form1.cleaned_data['dep_mtech']: - department.extend(form1.cleaned_data['dep_mtech']) - if form1.cleaned_data['dep_bdes']: - department.extend(form1.cleaned_data['dep_bdes']) - if form1.cleaned_data['dep_mdes']: - department.extend(form1.cleaned_data['dep_mdes']) - if form1.cleaned_data['dep_phd']: - department.extend(form1.cleaned_data['dep_phd']) - - if form1.cleaned_data['cpi']: - cpi = form1.cleaned_data['cpi'] - else: - cpi = 0 - debar = form1.cleaned_data['debar'] - placed_type = form1.cleaned_data['placed_type'] - - request.session['name'] = name - request.session['rollno'] = rollno - request.session['programme'] = programme - request.session['department'] = department - request.session['cpi'] = str(cpi) - request.session['debar'] = debar - request.session['placed_type'] = placed_type - - - students = Student.objects.filter( - Q(id__in=ExtraInfo.objects.filter(Q( - user__in=User.objects.filter(Q(first_name__icontains=name)), - department__in=DepartmentInfo.objects.filter(Q(name__in=department)), - id__icontains=rollno)), - programme=programme, - cpi__gte=cpi)).filter(Q(pk__in=StudentPlacement.objects.filter( - Q(debar=debar, placed_type=placed_type)).values('unique_id_id'))).order_by('id') - - # pagination stuff starts from here - st = students - student_record_check= 1 - total_query = students.count() - - if total_query > 30: - no_pagination = 1 - paginator = Paginator(students, 30) - page = request.GET.get('page', 1) - students = paginator.page(page) - page = int(page) - total_page = int(page + 3) - - if page<(paginator.num_pages-3): - if total_query > 30 and total_query <=60: - page_range = range(1, 3) - else: - page_range = range(1, total_page+1) - - if page >= 5: - is_disabled = 1 - page_range = range(page-2, total_page) - else: - if page >= 5: - is_disabled = 1 - page_range = range(page-2, paginator.num_pages+1) - else: - page_range = range(1, paginator.num_pages+1) - else: - no_pagination = 0 - else: - # when the request came from pagintion with some page no. - if request.GET.get('page') != None: - try: - students = Student.objects.filter( - Q(id__in=ExtraInfo.objects.filter( - Q(user__in=User.objects.filter( - Q(first_name__icontains=request.session['name']) - ), - department__in=DepartmentInfo.objects.filter( - Q(name__in=request.session['department']) - ), - id__icontains=request.session['rollno'] - ) - ), - programme=request.session['programme'], - cpi__gte=decimal.Decimal(request.session['cpi']))).filter(Q(pk__in=StudentPlacement.objects.filter(Q(debar=request.session['debar'], - placed_type=request.session['placed_type'])).values('unique_id_id'))).order_by('id') - except: - students = '' - - if students != '': - total_query = students.count() - else: - total_query = 0 - - if total_query > 30: - no_pagination = 1 - paginator = Paginator(students, 30) - page = request.GET.get('page', 1) - students = paginator.page(page) - page = int(page) - total_page = int(page + 3) - - if page<(paginator.num_pages-3): - if total_query > 30 and total_query <=60: - page_range = range(1, 3) - else: - page_range = range(1, total_page+1) - - if page >= 5: - is_disabled = 1 - page_range = range(page-2, total_page) - else: - if page >= 5: - is_disabled = 1 - page_range = range(page-2, paginator.num_pages+1) - else: - page_range = range(1, paginator.num_pages+1) - else: - no_pagination = 0 - else: - students = '' - - if 'debar' in request.POST: - spid = request.POST['debar'] - sr = StudentPlacement.objects.get(Q(pk=spid)) - sr.debar = "DEBAR" - sr.save() - if 'undebar' in request.POST: - spid = request.POST['undebar'] - sr = StudentPlacement.objects.get(Q(pk=spid)) - sr.debar = "NOT DEBAR" - sr.save() - - # pdf generation logic - if 'pdf_gen_std_record' in request.POST: - - name = request.session['name'] - rollno = request.session['rollno'] - programme = request.session['programme'] - department = request.session['department'] - cpi = int(request.session['cpi']) - debar = request.session['debar'] - placed_type = request.session['placed_type'] - - students = Student.objects.filter( - Q(id__in=ExtraInfo.objects.filter(Q( - user__in=User.objects.filter(Q(first_name__icontains=name)), - department__in=DepartmentInfo.objects.filter(Q(name__in=department)), - id__icontains=rollno)), - programme=programme, - cpi__gte=cpi)).filter(Q(pk__in=StudentPlacement.objects.filter( - Q(debar=debar, placed_type=placed_type)).values('unique_id_id'))).order_by('id') - - context = { - 'students' : students - } - - - return render_to_pdf('placementModule/pdf_student_record.html', context) - - # excel generation logic - if 'excel_gen_std_record' in request.POST: - - name = request.session['name'] - rollno = request.session['rollno'] - programme = request.session['programme'] - department = request.session['department'] - cpi = int(request.session['cpi']) - debar = request.session['debar'] - placed_type = request.session['placed_type'] - - students = Student.objects.filter( - Q(id__in=ExtraInfo.objects.filter(Q( - user__in=User.objects.filter(Q(first_name__icontains=name)), - department__in=DepartmentInfo.objects.filter(Q(name__in=department)), - id__icontains=rollno)), - programme=programme, - cpi__gte=cpi)).filter(Q(pk__in=StudentPlacement.objects.filter( - Q(debar=debar, placed_type=placed_type)).values('unique_id_id'))).order_by('id') - - context = { - 'students' : students - } - - - return export_to_xls_std_records(students) - - - # for sending the invite to students for particular schedule - if 'sendinvite' in request.POST: - # invitecheck=1; - - form13 = SendInvite(request.POST) - - if form13.is_valid(): - if form13.cleaned_data['company']: - if form13.cleaned_data['rollno']: - rollno = form13.cleaned_data['rollno'] - else: - rollno = '' - - programme = form13.cleaned_data['programme'] - - department = [] - if form13.cleaned_data['dep_btech']: - department.extend(form13.cleaned_data['dep_btech']) - if form13.cleaned_data['dep_mtech']: - department.extend(form13.cleaned_data['dep_mtech']) - if form13.cleaned_data['dep_bdes']: - department.extend(form13.cleaned_data['dep_bdes']) - if form13.cleaned_data['dep_mdes']: - department.extend(form13.cleaned_data['dep_mdes']) - if form13.cleaned_data['dep_phd']: - department.extend(form13.cleaned_data['dep_phd']) - - - if form13.cleaned_data['cpi']: - cpi = form13.cleaned_data['cpi'] - else: - cpi = 0 - - if form13.cleaned_data['no_of_days']: - no_of_days = form13.cleaned_data['no_of_days'] - else: - no_of_days = 10 - - - comp = form13.cleaned_data['company'] - - notify = NotifyStudent.objects.get(company_name=comp.company_name, - placement_type=comp.placement_type) - - students = Student.objects.filter( - Q( - id__in = ExtraInfo.objects.filter( - Q( - department__in = DepartmentInfo.objects.filter(Q(name__in=department)), - id__icontains = rollno - ) - ), - programme = programme, - cpi__gte = cpi - ) - ).exclude(id__in = PlacementStatus.objects.select_related('unique_id','notify_id').filter( - notify_id=notify).values_list('unique_id', flat=True)) - - PlacementStatus.objects.bulk_create( [PlacementStatus(notify_id=notify, - unique_id=student, no_of_days=no_of_days) for student in students] ) - - for st in students: - placement_cell_notif(request.user, st.id.user, "") - - students = '' - messages.success(request, 'Notification Sent') - else: - messages.error(request, 'Problem Occurred!! Please Try Again!!') - - context = { - 'form1': form1, - 'form9': form9, - 'form11': form11, - 'form13': form13, - 'current1': current1, - 'current2': current2, - 'mnplacement_tab': mnplacement_tab, - 'strecord_tab': strecord_tab, - 'students': students, - 'page_range': page_range, - 'paginator': paginator, - 'no_pagination': no_pagination, - 'is_disabled': is_disabled, - } - - return render(request, 'placementModule/studentrecords.html', context) - return redirect('/placement') - - -@login_required -def manage_records(request): - ''' - function to manage the records - - can add the records under placement | pbi | higher studies - - can also search the records under placement | pbi | higher studies - ''' - user = request.user - mnrecord_tab = 1 - pagination_placement = 0 - pagination_pbi = 0 - pagination_higher = 0 - is_disabled = 0 - years = None - records = None - paginator = '' - page_range = '' - pbirecord = None - placementrecord = None - higherrecord = None - officer_statistics_past_pbi_search = 0 - officer_statistics_past_higher_search = 0 - - profile = get_object_or_404(ExtraInfo, Q(user=user)) - studentrecord = StudentRecord.objects.all() - - years = PlacementRecord.objects.filter(~Q(placement_type="HIGHER STUDIES")).values('year').annotate(Count('year')) - records = PlacementRecord.objects.values('name', 'year', 'ctc', 'placement_type').annotate(Count('name'), Count('year'), Count('placement_type'), Count('ctc')) - - - form2 = SearchPlacementRecord(initial={}) - form3 = SearchPbiRecord(initial={}) - form4 = SearchHigherRecord(initial={}) - current1 = HoldsDesignation.objects.filter(Q(working=user, designation__name="placement chairman")) - current2 = HoldsDesignation.objects.filter(Q(working=user, designation__name="placement officer")) - - current = HoldsDesignation.objects.filter(Q(working=user, designation__name="student")) - - if len(current) == 0: - current = None - - - try: - # for adding the new data for student under higher studies category - if 'studenthigheraddsubmit' in request.POST: - officer_statistics_past_higher_add = 1 - form = SearchHigherRecord(request.POST) - if form.is_valid(): - rollno = form.cleaned_data['roll'] - uname = form.cleaned_data['uname'] - test_score = form.cleaned_data['test_score'] - test_type = form.cleaned_data['test_type'] - year = form.cleaned_data['year'] - placementr = PlacementRecord.objects.create(year=year, name=uname, - placement_type="HIGHER STUDIES", - test_type=test_type, - test_score=test_score) - studentr = StudentRecord.objects.select_related('unique_id','record_id').create(record_id=placementr, - unique_id=Student.objects.get - ((Q(id=ExtraInfo.objects.get - (Q(id=rollno)))))) - studentr.save() - placementr.save() - messages.success(request, 'Record Added Successfully!!') - - # for adding the new data for student under pbi category - if 'studentpbiaddsubmit' in request.POST: - officer_statistics_past_pbi_add = 1 - form = SearchPbiRecord(request.POST) - if form.is_valid(): - rollno = form.cleaned_data['roll'] - ctc = form.cleaned_data['ctc'] - year = form.cleaned_data['year'] - cname = form.cleaned_data['cname'] - placementr = PlacementRecord.objects.create(year=year, ctc=ctc, - placement_type="PBI", - name=cname) - studentr = StudentRecord.objects.select_related('unique_id','record_id').create(record_id=placementr, - unique_id=Student.objects.get - ((Q(id=ExtraInfo.objects.get - (Q(id=rollno)))))) - studentr.save() - placementr.save() - messages.success(request, 'Record Added Successfully!!') - - # for adding the new data for student under placement category - if 'studentplacementaddsubmit' in request.POST: - officer_statistics_past_add = 1 - form = SearchPlacementRecord(request.POST) - if form.is_valid(): - rollno = form.cleaned_data['roll'] - ctc = form.cleaned_data['ctc'] - year = form.cleaned_data['year'] - cname = form.cleaned_data['cname'] - placementr = PlacementRecord.objects.create(year=year, ctc=ctc, - placement_type="PLACEMENT", - name=cname) - studentr = StudentRecord.objects.select_related('unique_id','record_id').create(record_id=placementr, - unique_id=Student.objects.get - ((Q(id=ExtraInfo.objects.get - (Q(id=rollno)))))) - studentr.save() - placementr.save() - messages.success(request, 'Record Added Successfully!!') - - # for searching the student details under placement category - if 'studentplacementrecordsubmit' in request.POST: - officer_statistics_past = 1 - form = SearchPlacementRecord(request.POST) - if form.is_valid(): - if form.cleaned_data['stuname']: - stuname = form.cleaned_data['stuname'] - else: - stuname = '' - if form.cleaned_data['ctc']: - ctc = form.cleaned_data['ctc'] - else: - ctc = 0 - if form.cleaned_data['cname']: - cname = form.cleaned_data['cname'] - else: - cname = '' - if form.cleaned_data['roll']: - rollno = form.cleaned_data['roll'] - else: - rollno = '' - if form.cleaned_data['year']: - year = form.cleaned_data['year'] - s = Student.objects.filter((Q(id__in=ExtraInfo.objects.filter - (Q(user__in=User.objects.filter - (Q(first_name__icontains=stuname)), - id__icontains=rollno)) - ))) - - p = PlacementRecord.objects.filter(Q(placement_type="PLACEMENT", name__icontains=cname, ctc__gte=ctc, year=year)) - - placementrecord = StudentRecord.objects.select_related('unique_id','record_id').select_related('unique_id','record_id').filter(Q(record_id__in=PlacementRecord.objects.filter(Q(placement_type="PLACEMENT", name__icontains=cname, ctc__gte=ctc, year=year)), unique_id__in=Student.objects.filter((Q(id__in=ExtraInfo.objects.filter(Q(user__in=User.objects.filter(Q(first_name__icontains=stuname)),id__icontains=rollno))))))) - else: - s = Student.objects.filter((Q(id__in=ExtraInfo.objects.filter - (Q(user__in=User.objects.filter - (Q(first_name__icontains=stuname)), - id__icontains=rollno)) - ))) - - p = PlacementRecord.objects.filter(Q(placement_type="PLACEMENT", name__icontains=cname, ctc__gte=ctc)) - - placementrecord = StudentRecord.objects.select_related('unique_id','record_id').filter( - Q(record_id__in=PlacementRecord.objects.filter( - Q(placement_type="PLACEMENT", name__icontains=cname, ctc__gte=ctc)), - unique_id__in=Student.objects.filter( - (Q(id__in=ExtraInfo.objects.filter( - Q(user__in=User.objects.filter(Q(first_name__icontains=stuname)), - id__icontains=rollno))))))) - - request.session['stuname'] = stuname - request.session['ctc'] = ctc - request.session['cname'] = cname - request.session['rollno'] = rollno - request.session['year'] = form.cleaned_data['year'] - - total_query = placementrecord.count() - - if total_query > 30: - pagination_placement = 1 - paginator = Paginator(placementrecord, 30) - page = request.GET.get('page', 1) - placementrecord = paginator.page(page) - page = int(page) - total_page = int(page + 3) - - if page<(paginator.num_pages-3): - if total_query > 30 and total_query <=60: - page_range = range(1, 3) - else: - page_range = range(1, total_page+1) - - if page >= 5: - is_disabled = 1 - page_range = range(page-2, total_page) - else: - if page >= 5: - is_disabled = 1 - page_range = range(page-2, paginator.num_pages+1) - else: - page_range = range(1, paginator.num_pages+1) - else: - pagination_placement = 0 - else: - if request.GET.get('page') != None: - try: - if request.session['year']: - s = Student.objects.filter( - (Q(id__in=ExtraInfo.objects.filter( - Q(user__in=User.objects.filter( - Q(first_name__icontains=request.session['stuname'])), - id__icontains=request.session['rollno'])) - ))) - - p = PlacementRecord.objects.filter( - Q(placement_type="PLACEMENT", - name__icontains=request.session['cname'], - ctc__gte=request.session['ctc'], - year=request.session['year'])) - - placementrecord = StudentRecord.objects.select_related('unique_id','record_id').filter( - Q(record_id__in=PlacementRecord.objects.filter( - Q(placement_type="PLACEMENT", - name__icontains=request.session['cname'], - ctc__gte=request.session['ctc'], - year=request.session['year'])), - unique_id__in=Student.objects.filter( - (Q(id__in=ExtraInfo.objects.filter( - Q(user__in=User.objects.filter( - Q(first_name__icontains=request.session['stuname'])), - id__icontains=request.session['rollno']))))))) - else: - s = Student.objects.filter((Q(id__in=ExtraInfo.objects.filter - (Q(user__in=User.objects.filter - (Q(first_name__icontains=request.session['stuname'])), - id__icontains=request.session['rollno'])) - ))) - - p = PlacementRecord.objects.filter( - Q(placement_type="PLACEMENT", - name__icontains=request.session['cname'], - ctc__gte=request.session['ctc'])) - - placementrecord = StudentRecord.objects.select_related('unique_id','record_id').filter( - Q(record_id__in=PlacementRecord.objects.filter( - Q(placement_type="PLACEMENT", - name__icontains=request.session['cname'], - ctc__gte=request.session['ctc'])), - unique_id__in=Student.objects.filter( - (Q(id__in=ExtraInfo.objects.filter( - Q(user__in=User.objects.filter( - Q(first_name__icontains=request.session['stuname'])), - id__icontains=request.session['rollno']))))))) - except: - placementrecord = '' - - if placementrecord != '': - total_query = placementrecord.count() - else: - total_query = 0 - - if total_query > 30: - pagination_placement = 1 - paginator = Paginator(placementrecord, 30) - page = request.GET.get('page', 1) - placementrecord = paginator.page(page) - page = int(page) - total_page = int(page + 3) - - if page<(paginator.num_pages-3): - if total_query > 30 and total_query <=60: - page_range = range(1, 3) - else: - page_range = range(1, total_page+1) - - if page >= 5: - is_disabled = 1 - page_range = range(page-2, total_page) - else: - if page >= 5: - is_disabled = 1 - page_range = range(page-2, paginator.num_pages+1) - else: - page_range = range(1, paginator.num_pages+1) - else: - pagination_placement = 0 - else: - placementrecord = '' - - # for searching the student details under pbi category - if 'studentpbirecordsubmit' in request.POST: - officer_statistics_past_pbi_search = 1 - form = SearchPbiRecord(request.POST) - if form.is_valid(): - if form.cleaned_data['stuname']: - stuname = form.cleaned_data['stuname'] - else: - stuname = '' - if form.cleaned_data['ctc']: - ctc = form.cleaned_data['ctc'] - else: - ctc = 0 - if form.cleaned_data['cname']: - cname = form.cleaned_data['cname'] - else: - cname = '' - if form.cleaned_data['roll']: - rollno = form.cleaned_data['roll'] - else: - rollno = '' - if form.cleaned_data['year']: - year = form.cleaned_data['year'] - pbirecord = StudentRecord.objects.select_related('unique_id','record_id').filter(Q(record_id__in=PlacementRecord.objects.filter - (Q(placement_type="PBI", - name__icontains=cname, - ctc__gte=ctc, year=year)), - unique_id__in=Student.objects.filter - ((Q(id__in=ExtraInfo.objects.filter - (Q(user__in=User.objects.filter - (Q(first_name__icontains=stuname)), - id__icontains=rollno)) - ))))) - else: - pbirecord = StudentRecord.objects.select_related('unique_id','record_id').filter(Q(record_id__in=PlacementRecord.objects.filter - (Q(placement_type="PBI", - name__icontains=cname, - ctc__gte=ctc)), - unique_id__in=Student.objects.filter - ((Q(id__in=ExtraInfo.objects.filter - (Q(user__in=User.objects.filter - (Q(first_name__icontains=stuname)), - id__icontains=rollno)) - ))))) - request.session['stuname'] = stuname - request.session['ctc'] = ctc - request.session['cname'] = cname - request.session['rollno'] = rollno - request.session['year'] = form.cleaned_data['year'] - - total_query = pbirecord.count() - - if total_query > 30: - pagination_pbi = 1 - paginator = Paginator(pbirecord, 30) - page = request.GET.get('page', 1) - pbirecord = paginator.page(page) - page = int(page) - total_page = int(page + 3) - - if page<(paginator.num_pages-3): - if total_query > 30 and total_query <=60: - page_range = range(1, 3) - else: - page_range = range(1, total_page+1) - - if page >= 5: - is_disabled = 1 - page_range = range(page-2, total_page) - else: - if page >= 5: - is_disabled = 1 - page_range = range(page-2, paginator.num_pages+1) - else: - page_range = range(1, paginator.num_pages+1) - else: - pagination_pbi = 0 - else: - if request.GET.get('page') != None: - try: - if request.session['year']: - pbirecord = StudentRecord.objects.select_related('unique_id','record_id').filter( - Q(record_id__in=PlacementRecord.objects.filter( - Q(placement_type="PBI", - name__icontains=request.session['cname'], - ctc__gte=ctc, year=request.session['year'])), - unique_id__in=Student.objects.filter(( - Q(id__in=ExtraInfo.objects.filter( - Q(user__in=User.objects.filter( - Q(first_name__icontains=request.session['stuname'])), - id__icontains=request.session['rollno']))))))) - else: - pbirecord = StudentRecord.objects.select_related('unique_id','record_id').filter( - Q(record_id__in=PlacementRecord.objects.filter(Q(placement_type="PBI", - name__icontains=request.session['cname'], - ctc__gte=request.session['ctc'])), - unique_id__in=Student.objects.filter( - (Q(id__in=ExtraInfo.objects.filter( - Q(user__in=User.objects.filter( - Q(first_name__icontains=request.session['stuname'])), - id__icontains=request.session['rollno']))))))) - except: - print('except') - pbirecord = '' - - if pbirecord != '': - total_query = pbirecord.count() - else: - total_query = 0 - - if total_query > 30: - pagination_pbi = 1 - paginator = Paginator(pbirecord, 30) - page = request.GET.get('page', 1) - pbirecord = paginator.page(page) - page = int(page) - total_page = int(page + 3) - - if page<(paginator.num_pages-3): - if total_query > 30 and total_query <=60: - page_range = range(1, 3) - else: - page_range = range(1, total_page+1) - - if page >= 5: - is_disabled = 1 - page_range = range(page-2, total_page) - else: - if page >= 5: - is_disabled = 1 - page_range = range(page-2, paginator.num_pages+1) - else: - page_range = range(1, paginator.num_pages+1) - else: - pagination_pbi = 0 - else: - pbirecord = '' - - # for searching the student details under higher studies category - if 'studenthigherrecordsubmit' in request.POST: - officer_statistics_past_higher_search = 1 - form = SearchHigherRecord(request.POST) - if form.is_valid(): - if form.cleaned_data['stuname']: - stuname = form.cleaned_data['stuname'] - else: - stuname = '' - if form.cleaned_data['test_type']: - test_type = form.cleaned_data['test_type'] - else: - test_type = '' - if form.cleaned_data['uname']: - uname = form.cleaned_data['uname'] - else: - uname = '' - if form.cleaned_data['test_score']: - test_score = form.cleaned_data['test_score'] - else: - test_score = 0 - if form.cleaned_data['roll']: - rollno = form.cleaned_data['roll'] - else: - rollno = '' - if form.cleaned_data['year']: - year = form.cleaned_data['year'] - higherrecord = StudentRecord.objects.select_related('unique_id','record_id').filter(Q(record_id__in=PlacementRecord.objects.filter - (Q(placement_type="HIGHER STUDIES", - test_type__icontains=test_type, - name__icontains=uname, year=year, - test_score__gte=test_score)), - unique_id__in=Student.objects.filter - ((Q(id__in=ExtraInfo.objects.filter - (Q(user__in=User.objects.filter - (Q(first_name__icontains=stuname)), - id__icontains=rollno)) - ))))) - else: - higherrecord = StudentRecord.objects.select_related('unique_id','record_id').filter( - Q(record_id__in=PlacementRecord.objects.filter - (Q(placement_type="HIGHER STUDIES", - test_type__icontains=test_type, - name__icontains=uname, - test_score__gte=test_score)), - unique_id__in=Student.objects.filter - ((Q(id__in=ExtraInfo.objects.filter - (Q(user__in=User.objects.filter - (Q(first_name__icontains=stuname)), - id__icontains=rollno)) - ))))) - request.session['stuname'] = stuname - request.session['test_score'] = test_score - request.session['uname'] = uname - request.session['test_type'] = test_type - request.session['rollno'] = rollno - request.session['year'] = form.cleaned_data['year'] - - total_query = higherrecord.count() - - if total_query > 30: - pagination_higher = 1 - paginator = Paginator(higherrecord, 30) - page = request.GET.get('page', 1) - higherrecord = paginator.page(page) - page = int(page) - total_page = int(page+3) - - if page < (paginator.num_pages-3): - if total_query > 30 and total_query <= 60: - page_range = range(1, 3) - else: - page_range = range(1, total_page+1) - - if page >= 5: - is_disabled = 1 - page_range = range(page-2, total_page) - else: - if page >= 5: - is_disabled = 1 - page_range = range(page-2, paginator.num_pages+1) - else: - page_range = range(page-2, paginator.num_pages+1) - else: - pagination_higher = 0 - else: - if request.GET.get('page') != None: - try: - if request.session['year']: - higherrecord = StudentRecord.objects.select_related('unique_id','record_id').filter( - Q(record_id__in=PlacementRecord.objects.filter( - Q(placement_type="HIGHER STUDIES", - test_type__icontains=request.session['test_type'], - name__icontains=request.session['uname'], - year=request.session['year'], - test_score__gte=request.session['test_score'])), - unique_id__in=Student.objects.filter( - (Q(id__in=ExtraInfo.objects.filter( - Q(user__in=User.objects.filter( - Q(first_name__icontains=request.session['stuname'])), - id__icontains=request.session['rollno'])) - ))))) - else: - higherrecord = StudentRecord.objects.select_related('unique_id','record_id').filter( - Q(record_id__in=PlacementRecord.objects.filter - (Q(placement_type="HIGHER STUDIES", - test_type__icontains=request.session['test_type'], - name__icontains=request.session['uname'], - test_score__gte=request.session['test_score'])), - unique_id__in=Student.objects.filter - ((Q(id__in=ExtraInfo.objects.filter - (Q(user__in=User.objects.filter - (Q(first_name__icontains=request.session['stuname'])), - id__icontains=request.session['rollno'])) - ))))) - except: - print('except') - higherrecord = '' - - if higherrecord != '': - total_query = higherrecord.count() - else: - total_query = 0 - - if total_query > 30: - no_pagination = 1 - paginator = Paginator(higherrecord, 30) - page = request.GET.get('page', 1) - higherrecord = paginator.page(page) - page = int(page) - total_page = int(page + 3) - - if page<(paginator.num_pages-3): - if total_query > 30 and total_query <=60: - page_range = range(1, 3) - else: - page_range = range(1, total_page+1) - - if page >= 5: - is_disabled = 1 - page_range = range(page-2, total_page) - else: - if page >= 5: - is_disabled = 1 - page_range = range(page-2, paginator.num_pages+1) - else: - page_range = range(1, paginator.num_pages+1) - else: - no_pagination = 0 - else: - higherrecord = '' - - - except Exception as e: - messages.error(request, "Problem Occurred!!! Please Try Again with Correct Info!!") - print(e) - - - context = { - 'form2' : form2, - 'form3' : form3, - 'form4' : form4, - 'current' : current, - 'current1' : current1, - 'current2' : current2, - 'mnrecord_tab' : mnrecord_tab, - 'pbirecord' : pbirecord, - 'placementrecord' : placementrecord, - 'higherrecord' : higherrecord, - 'years' : years, - 'records' : records, - 'page_range': page_range, - 'paginator': paginator, - 'pagination_placement': pagination_placement, - 'pagination_pbi': pagination_pbi, - 'pagination_higher': pagination_higher, - 'is_disabled': is_disabled, - 'officer_statistics_past_pbi_search': officer_statistics_past_pbi_search, - 'officer_statistics_past_higher_search': officer_statistics_past_higher_search - } - - return render(request, 'placementModule/managerecords.html', context) - - - -@login_required -def delete_invite_status(request): - ''' - function to delete the invitation that has been sent to the students - ''' - user = request.user - strecord_tab = 1 - mnpbi_tab = 0 - mnplacement_post = 0 - mnpbi_post = 0 - invitation_status_tab = 1 - placementstatus = [] - - no_pagination = 1 - is_disabled = 0 - paginator = '' - page_range = '' - - if 'deleteinvitationstatus' in request.POST: - delete_invit_status_key = request.POST['deleteinvitationstatus'] - - try: - PlacementStatus.objects.select_related('unique_id','notify_id').get(pk=delete_invit_status_key).delete() - messages.success(request, 'Invitation Deleted Successfully') - except Exception as e: - logger.error(e) - - if 'pbi_tab_active' in request.POST: - mnpbi_tab = 1 - else: - mnplacement_tab = 1 - - form1 = SearchStudentRecord(initial={}) - form9 = ManagePbiRecord(initial={}) - form11 = ManagePlacementRecord(initial={}) - form13 = SendInvite(initial={}) - current1 = HoldsDesignation.objects.filter(Q(working=user, designation__name="placement chairman")) - current2 = HoldsDesignation.objects.filter(Q(working=user, designation__name="placement officer")) - - context = { - 'form1': form1, - 'form9': form9, - 'form11': form11, - 'form13': form13, - 'invitation_status_tab': invitation_status_tab, - 'mnplacement_post': mnplacement_post, - 'mnpbi_tab': mnpbi_tab, - 'placementstatus': placementstatus, - # 'current':current, - 'current1': current1, - 'current2': current2, - 'strecord_tab': strecord_tab, - 'mnpbi_post': mnpbi_post, - 'page_range': page_range, - 'paginator': paginator, - 'no_pagination': no_pagination, - 'is_disabled': is_disabled, - } - - return render(request, 'placementModule/studentrecords.html', context) - - - -@login_required -def placement_statistics(request): - ''' - logic of the view shown under Placement Statistics tab - ''' - user = request.user - - statistics_tab = 1 - strecord_tab=1 - delete_operation = 0 - pagination_placement = 0 - pagination_pbi = 0 - pagination_higher = 0 - is_disabled = 0 - paginator = '' - page_range = '' - officer_statistics_past_pbi_search = 0 - officer_statistics_past_higher_search = 0 - - profile = get_object_or_404(ExtraInfo, Q(user=user)) - studentrecord = StudentRecord.objects.select_related('unique_id','record_id').all() - - years = PlacementRecord.objects.filter(~Q(placement_type="HIGHER STUDIES")).values('year').annotate(Count('year')) - records = PlacementRecord.objects.values('name', 'year', 'ctc', 'placement_type').annotate(Count('name'), Count('year'), Count('placement_type'), Count('ctc')) - - - - - #working here to fetch all placement record - all_records=PlacementRecord.objects.all() - print(all_records) - - - - - - - invitecheck=0 - for r in records: - r['name__count'] = 0 - r['year__count'] = 0 - r['placement_type__count'] = 0 - tcse = dict() - tece = dict() - tme = dict() - tadd = dict() - for y in years: - tcse[y['year']] = 0 - tece[y['year']] = 0 - tme[y['year']] = 0 - for r in records: - if r['year'] == y['year']: - if r['placement_type'] != "HIGHER STUDIES": - for z in studentrecord: - if z.record_id.name == r['name'] and z.record_id.year == r['year'] and z.unique_id.id.department.name == "CSE": - tcse[y['year']] = tcse[y['year']]+1 - r['name__count'] = r['name__count']+1 - if z.record_id.name == r['name'] and z.record_id.year == r['year'] and z.unique_id.id.department.name == "ECE": - tece[y['year']] = tece[y['year']]+1 - r['year__count'] = r['year__count']+1 - if z.record_id.name == r['name'] and z.record_id.year == r['year'] and z.unique_id.id.department.name == "ME": - tme[y['year']] = tme[y['year']]+1 - r['placement_type__count'] = r['placement_type__count']+1 - tadd[y['year']] = tcse[y['year']]+tece[y['year']]+tme[y['year']] - y['year__count'] = [tadd[y['year']], tcse[y['year']], tece[y['year']], tme[y['year']]] - - form2 = SearchPlacementRecord(initial={}) - form3 = SearchPbiRecord(initial={}) - form4 = SearchHigherRecord(initial={}) - - - current1 = HoldsDesignation.objects.filter(Q(working=user, designation__name="placement chairman")) - current2 = HoldsDesignation.objects.filter(Q(working=user, designation__name="placement officer")) - current = HoldsDesignation.objects.filter(Q(working=user, designation__name="student")) - - if len(current1)!=0 or len(current2)!=0: - delete_operation = 1 - if len(current) == 0: - current = None - pbirecord= '' - placementrecord= '' - higherrecord= '' - total_query=0 - total_query1 = 0 - total_query2= 0 - p="" - p1="" - p2="" - placement_search_record=" " - pbi_search_record=" " - higher_search_record=" " - # results of the searched query under placement tab - if 'studentplacementrecordsubmit' in request.POST: - officer_statistics_past = 1 - form = SearchPlacementRecord(request.POST) - if form.is_valid(): - - - - - print("IS VALID") - - - - #for student name - if form.cleaned_data['stuname']: - stuname = form.cleaned_data['stuname'] - try: - first_name = stuname.split(" ")[0] - last_name = stuname.split(" ")[1] - except Exception as e: - print("Error") - print(e) - first_name = stuname - last_name = '' - else: - stuname = '' - first_name = '' - last_name = '' - - - # for student CTC - if form.cleaned_data['ctc']: - ctc = form.cleaned_data['ctc'] - else: - ctc = 0 - - #for company name - if form.cleaned_data['cname']: - cname = form.cleaned_data['cname'] - else: - cname = '' - - #for student roll - if form.cleaned_data['roll']: - rollno = form.cleaned_data['roll'] - else: - rollno = '' - - #for admission year - if form.cleaned_data['year']: - year = form.cleaned_data['year'] - s = Student.objects.filter((Q(id__in=ExtraInfo.objects.filter( - Q(user__in=User.objects.filter( - first_name__icontains=first_name, - last_name__icontains=last_name), - id__icontains=rollno)) - ))) - - p = PlacementRecord.objects.filter(Q(placement_type="PLACEMENT",name__icontains=stuname, ctc__icontains=ctc, year__icontains=year)) - - print(p) - - - total_query = p.count() - - if total_query > 30: - pagination_placement = 1 - paginator = Paginator(placementrecord, 30) - page = request.GET.get('page', 1) - placementrecord = paginator.page(page) - page = int(page) - total_page = int(page + 3) - - if page<(paginator.num_pages-3): - if total_query > 30 and total_query <=60: - page_range = range(1, 3) - else: - page_range = range(1, total_page+1) - - if page >= 5: - is_disabled = 1 - page_range = range(page-2, total_page) - else: - if page >= 5: - is_disabled = 1 - page_range = range(page-2, paginator.num_pages+1) - else: - page_range = range(1, paginator.num_pages+1) - else: - pagination_placement = 0 - else: - if request.GET.get('page') != None: - try: - if request.session['year']: - s = Student.objects.filter( - (Q(id__in=ExtraInfo.objects.filter( - Q(user__in=User.objects.filter( - Q(first_name__icontains=request.session['first_name'], - last_name__icontains=request.session['last_name'])), - id__icontains=request.session['rollno'])) - ))) - - p = PlacementRecord.objects.filter( - Q(placement_type="PLACEMENT", - name__icontains=request.session['cname'], - ctc__gte=request.session['ctc'], - year=request.session['year'])) - - - placementrecord = StudentRecord.objects.select_related('unique_id','record_id').filter( - Q(record_id__in=PlacementRecord.objects.filter( - Q(placement_type="PLACEMENT", - name__icontains=request.session['cname'], - ctc__gte=request.session['ctc'], - year=request.session['year'])), - unique_id__in=Student.objects.filter( - (Q(id__in=ExtraInfo.objects.filter( - Q(user__in=User.objects.filter( - Q(first_name__icontains=request.session['first_name'], - last_name__icontains=request.session['last_name'])), - id__icontains=request.session['rollno']))))))) - else: - s = Student.objects.filter((Q(id__in=ExtraInfo.objects.filter - (Q(user__in=User.objects.filter - (Q(first_name__icontains=request.session['first_name'], - last_name__icontains=request.session['last_name'])), - id__icontains=request.session['rollno'])) - ))) - - p = PlacementRecord.objects.filter( - Q(placement_type="PLACEMENT", - name__icontains=request.session['cname'], - ctc__gte=request.session['ctc'])) - - placementrecord = StudentRecord.objects.select_related('unique_id','record_id').filter( - Q(record_id__in=PlacementRecord.objects.filter( - Q(placement_type="PLACEMENT", - name__icontains=request.session['cname'], - ctc__gte=request.session['ctc'])), - unique_id__in=Student.objects.filter( - (Q(id__in=ExtraInfo.objects.filter( - Q(user__in=User.objects.filter( - Q(first_name__icontains=request.session['first_name'], - last_name__icontains=request.session['last_name'])), - id__icontains=request.session['rollno']))))))) - except Exception as e: - print(e) - placementrecord = '' - - if placementrecord != '': - total_query = placementrecord.count() - else: - total_query = 0 - no_records=1 - print(placementrecord) - if total_query > 30: - pagination_placement = 1 - paginator = Paginator(placementrecord, 30) - page = request.GET.get('page', 1) - placementrecord = paginator.page(page) - page = int(page) - total_page = int(page + 3) - - if page<(paginator.num_pages-3): - if total_query > 30 and total_query <=60: - page_range = range(1, 3) - else: - page_range = range(1, total_page+1) - - if page >= 5: - is_disabled = 1 - page_range = range(page-2, total_page) - else: - if page >= 5: - is_disabled = 1 - page_range = range(page-2, paginator.num_pages+1) - else: - page_range = range(1, paginator.num_pages+1) - else: - pagination_placement = 0 - else: - placementrecord = '' - - if total_query!=0: - placement_search_record=p - # results of the searched query under pbi tab - if 'studentpbirecordsubmit' in request.POST: - officer_statistics_past_pbi_search = 1 - form = SearchPbiRecord(request.POST) - if form.is_valid(): - if form.cleaned_data['stuname']: - stuname = form.cleaned_data['stuname'] - try: - first_name = stuname.split(" ")[0] - last_name = stuname.split(" ")[1] - except: - first_name = stuname - last_name = '' - else: - stuname = '' - first_name = '' - last_name = '' - if form.cleaned_data['ctc']: - ctc = form.cleaned_data['ctc'] - else: - ctc = 0 - if form.cleaned_data['cname']: - cname = form.cleaned_data['cname'] - else: - cname = '' - if form.cleaned_data['roll']: - rollno = form.cleaned_data['roll'] - else: - rollno = '' - if form.cleaned_data['year']: - year = form.cleaned_data['year'] - pbirecord = StudentRecord.objects.select_related('unique_id','record_id').filter(Q(record_id__in=PlacementRecord.objects.filter - (Q(placement_type="PBI", - name__icontains=cname, - ctc__gte=ctc, year=year)), - unique_id__in=Student.objects.filter - ((Q(id__in=ExtraInfo.objects.filter - (Q(user__in=User.objects.filter - (Q(first_name__icontains=first_name, - last_name__icontains=last_name)), - id__icontains=rollno)) - ))))) - p1 = PlacementRecord.objects.filter( - Q(placement_type="PBI", name__icontains=stuname, ctc__icontains=ctc, year__icontains=year)) - """else: - pbirecord = StudentRecord.objects.select_related('unique_id','record_id').filter(Q(record_id__in=PlacementRecord.objects.filter - (Q(placement_type="PBI", - name__icontains=cname, - ctc__gte=ctc)), - unique_id__in=Student.objects.filter - ((Q(id__in=ExtraInfo.objects.filter - (Q(user__in=User.objects.filter - (Q(first_name__icontains=first_name, - last_name__icontains=last_name)), - id__icontains=rollno)) - ))))) - request.session['first_name'] = first_name - request.session['last_name'] = last_name - request.session['ctc'] = ctc - request.session['cname'] = cname - request.session['rollno'] = rollno - request.session['year'] = form.cleaned_data['year'] -""" - total_query1 = p1.count() - - if total_query1 > 30: - pagination_pbi = 1 - paginator = Paginator(pbirecord, 30) - page = request.GET.get('page', 1) - pbirecord = paginator.page(page) - page = int(page) - total_page = int(page + 3) - - if page<(paginator.num_pages-3): - if total_query1 > 30 and total_query1 <=60: - page_range = range(1, 3) - else: - page_range = range(1, total_page+1) - - if page >= 5: - is_disabled = 1 - page_range = range(page-2, total_page) - else: - if page >= 5: - is_disabled = 1 - page_range = range(page-2, paginator.num_pages+1) - else: - page_range = range(1, paginator.num_pages+1) - else: - pagination_pbi = 0 - else: - if request.GET.get('page') != None: - try: - if request.session['year']: - pbirecord = StudentRecord.objects.select_related('unique_id','record_id').filter( - Q(record_id__in=PlacementRecord.objects.filter( - Q(placement_type="PBI", - name__icontains=request.session['cname'], - ctc__gte=ctc, year=request.session['year'])), - unique_id__in=Student.objects.filter(( - Q(id__in=ExtraInfo.objects.filter( - Q(user__in=User.objects.filter( - Q(first_name__icontains=request.session['first_name'], - last_name__icontains=request.session['last_name'])), - id__icontains=request.session['rollno']))))))) - else: - pbirecord = StudentRecord.objects.select_related('unique_id','record_id').filter( - Q(record_id__in=PlacementRecord.objects.filter(Q(placement_type="PBI", - name__icontains=request.session['cname'], - ctc__gte=request.session['ctc'])), - unique_id__in=Student.objects.filter( - (Q(id__in=ExtraInfo.objects.filter( - Q(user__in=User.objects.filter( - Q(first_name__icontains=request.session['first_name'], - last_name__icontains=request.session['last_name'])), - id__icontains=request.session['rollno']))))))) - except: - print('except') - pbirecord = '' - - if pbirecord != '': - total_query = pbirecord.count() - else: - total_query = 0 - - if total_query > 30: - pagination_pbi = 1 - paginator = Paginator(pbirecord, 30) - page = request.GET.get('page', 1) - pbirecord = paginator.page(page) - page = int(page) - total_page = int(page + 3) - - if page<(paginator.num_pages-3): - if total_query > 30 and total_query <=60: - page_range = range(1, 3) - else: - page_range = range(1, total_page+1) - - if page >= 5: - is_disabled = 1 - page_range = range(page-2, total_page) - else: - if page >= 5: - is_disabled = 1 - page_range = range(page-2, paginator.num_pages+1) - else: - page_range = range(1, paginator.num_pages+1) - else: - pagination_pbi = 0 - else: - pbirecord = '' - if total_query1!=0: - pbi_search_record=p1 - - # results of the searched query under higher studies tab - if 'studenthigherrecordsubmit' in request.POST: - officer_statistics_past_higher_search = 1 - form = SearchHigherRecord(request.POST) - if form.is_valid(): - # getting all the variables send through form - if form.cleaned_data['stuname']: - stuname = form.cleaned_data['stuname'] - try: - first_name = stuname.split(" ")[0] - last_name = stuname.split(" ")[1] - except: - first_name = stuname - last_name = '' - else: - stuname = '' - first_name = '' - last_name = '' - if form.cleaned_data['test_type']: - test_type = form.cleaned_data['test_type'] - else: - test_type = '' - if form.cleaned_data['uname']: - uname = form.cleaned_data['uname'] - else: - uname = '' - if form.cleaned_data['test_score']: - test_score = form.cleaned_data['test_score'] - else: - test_score = 0 - if form.cleaned_data['roll']: - rollno = form.cleaned_data['roll'] - else: - rollno = '' - if form.cleaned_data['year']: - year = form.cleaned_data['year'] - # result of the query when year is given - higherrecord = StudentRecord.objects.select_related('unique_id','record_id').filter(Q(record_id__in=PlacementRecord.objects.filter - (Q(placement_type="HIGHER STUDIES", - test_type__icontains=test_type, - name__icontains=uname, year=year, - test_score__gte=test_score)), - unique_id__in=Student.objects.filter - ((Q(id__in=ExtraInfo.objects.filter - (Q(user__in=User.objects.filter - (Q(first_name__icontains=first_name, - last_name__icontains=last_name)), - id__icontains=rollno)) - ))))) - - p2 = PlacementRecord.objects.filter( - Q(placement_type="HIGHER STUDIES", name__icontains=stuname, year__icontains=year)) - - """else: - # result of the query when year is not given - higherrecord = StudentRecord.objects.select_related('unique_id','record_id').filter( - Q(record_id__in=PlacementRecord.objects.filter - (Q(placement_type="HIGHER STUDIES", - test_type__icontains=test_type, - name__icontains=uname, - test_score__gte=test_score)), - unique_id__in=Student.objects.filter - ((Q(id__in=ExtraInfo.objects.filter - (Q(user__in=User.objects.filter - (Q(first_name__icontains=first_name, - last_name__icontains=last_name)), - id__icontains=rollno)) - ))))) - request.session['first_name'] = first_name - request.session['last_name'] = last_name - request.session['test_score'] = test_score - request.session['uname'] = uname - request.session['test_type'] = test_type - request.session['rollno'] = rollno - request.session['year'] = form.cleaned_data['year']""" - - total_query2 = p2.count() - - if total_query2 > 30: - pagination_higher = 1 - paginator = Paginator(p2, 30) - page = request.GET.get('page', 1) - p2 = paginator.page(page) - page = int(page) - total_page = int(page+3) - - if page < (paginator.num_pages-3): - if total_query2 > 30 and total_query2 <= 60: - page_range = range(1, 3) - else: - page_range = range(1, total_page+1) - - if page >= 5: - is_disabled = 1 - page_range = range(page-2, total_page) - else: - if page >= 5: - is_disabled = 1 - page_range = range(page-2, paginator.num_pages+1) - else: - page_range = range(page-2, paginator.num_pages+1) - else: - pagination_higher = 0 - else: - if request.GET.get('page') != None: - try: - if request.session['year']: - higherrecord = StudentRecord.objects.select_related('unique_id','record_id').filter( - Q(record_id__in=PlacementRecord.objects.filter( - Q(placement_type="HIGHER STUDIES", - test_type__icontains=request.session['test_type'], - name__icontains=request.session['uname'], - year=request.session['year'], - test_score__gte=request.session['test_score'])), - unique_id__in=Student.objects.filter( - (Q(id__in=ExtraInfo.objects.filter( - Q(user__in=User.objects.filter( - Q(first_name__icontains=request.session['first_name'], - last_name__icontains=request.session['last_name'])), - id__icontains=request.session['rollno'])) - ))))) - else: - higherrecord = StudentRecord.objects.select_related('unique_id','record_id').filter( - Q(record_id__in=PlacementRecord.objects.filter( - Q(placement_type="HIGHER STUDIES", - test_type__icontains=request.session['test_type'], - name__icontains=request.session['uname'], - test_score__gte=request.session['test_score'])), - unique_id__in=Student.objects.filter - ((Q(id__in=ExtraInfo.objects.filter( - Q(user__in=User.objects.filter( - Q(first_name__icontains=request.session['first_name'], - last_name__icontains=request.session['last_name'])), - id__icontains=request.session['rollno'])) - ))))) - except: - higherrecord = '' - - if higherrecord != '': - total_query = higherrecord.count() - else: - total_query = 0 - - if total_query > 30: - no_pagination = 1 - paginator = Paginator(higherrecord, 30) - page = request.GET.get('page', 1) - higherrecord = paginator.page(page) - page = int(page) - total_page = int(page + 3) - - if page<(paginator.num_pages-3): - if total_query > 30 and total_query <=60: - page_range = range(1, 3) - else: - page_range = range(1, total_page+1) - - if page >= 5: - is_disabled = 1 - page_range = range(page-2, total_page) - else: - if page >= 5: - is_disabled = 1 - page_range = range(page-2, paginator.num_pages+1) - else: - page_range = range(1, paginator.num_pages+1) - else: - no_pagination = 0 - else: - higherrecord = '' - if total_query2!=0: - higher_search_record=p2 - - context = { - 'form2' : form2, - 'form3' : form3, - 'form4' : form4, - 'current' : current, - 'current1' : current1, - 'current2' : current2, - - - 'all_records': all_records, #for flashing all placement Schedule - - 'placement_search_record': placement_search_record, - 'pbi_search_record': pbi_search_record, - 'higher_search_record': higher_search_record, - - - - 'statistics_tab' : statistics_tab, - 'pbirecord' : pbirecord, - 'placementrecord' : placementrecord, - 'higherrecord' : higherrecord, - 'years' : years, - 'records' : records, - 'delete_operation' : delete_operation, - 'page_range': page_range, - 'paginator': paginator, - 'pagination_placement': pagination_placement, - 'pagination_pbi': pagination_pbi, - 'pagination_higher': pagination_higher, - 'is_disabled': is_disabled, - 'officer_statistics_past_pbi_search': officer_statistics_past_pbi_search, - 'officer_statistics_past_higher_search': officer_statistics_past_higher_search - } - - return render(request, 'placementModule/placementstatistics.html', context) - - -@login_required -def delete_placement_statistics(request): - """ - The function is used to delete the placement statistic record. - @param: - request - trivial - @variables: - record_id = stores current StudentRecord Id. - """ - if 'deleterecord' in request.POST or 'deleterecordmanaged' in request.POST: - try: - if 'deleterecord' in request.POST: - record_id = int(request.POST['deleterecord']) - elif 'deleterecordmanaged' in request.POST: - record_id = int(request.POST['deleterecordmanaged']) - - student_record = StudentRecord.objects.get(pk=record_id) - PlacementRecord.objects.get(id=student_record.record_id.id).delete() - student_record.delete() - messages.success(request, 'Placement Statistics deleted Successfully!!') - - except Exception as e: - messages.error(request, 'Problem Occurred!! Please Try Again!!') - print(e) - - - if 'deleterecordmanaged' in request.POST: - return redirect('/placement/manage_records/') - - return redirect('/placement/statistics/') - - -def cv(request, username): - # Retrieve data or whatever you need - """ - The function is used to generate the cv in the pdf format. - Embeds the data into the predefined template. - @param: - request - trivial - username - name of user whose cv is to be generated - @variables: - user = stores current user - profile = stores extrainfo of user - current = Stores all working students from HoldsDesignation for the respective degignation - achievementcheck = variable for achievementcheck in form for cv generation - educationcheck = variable for educationcheck in form for cv generation - publicationcheck = variable for publicationcheck in form for cv generation - patentcheck = variable for patentcheck in form for cv generation - internshipcheck = variable for internshipcheck in form for cv generation - projectcheck = variable for projectcheck in form for cv generation - coursecheck = variable for coursecheck in form for cv generation - skillcheck = variable for skillcheck in form for cv generation - user = get_object_or_404(User, Q(username=username)) - profile = get_object_or_404(ExtraInfo, Q(user=user)) - import datetime - now = stores current timestamp - roll = roll of the user - student = variable storing the profile data - studentplacement = variable storing the placement data - skills = variable storing the skills data - education = variable storing the education data - course = variable storing the course data - experience = variable storing the experience data - project = variable storing the project data - achievement = variable storing the achievement data - publication = variable storing the publication data - patent = variable storing the patent data - """ - user = request.user - profile = get_object_or_404(ExtraInfo, Q(user=user)) - - current = HoldsDesignation.objects.filter(Q(working=user, designation__name="student")) - if current: - if request.method == 'POST': - achievementcheck = request.POST.get('achievementcheck') - educationcheck = request.POST.get('educationcheck') - publicationcheck = request.POST.get('publicationcheck') - patentcheck = request.POST.get('patentcheck') - internshipcheck = request.POST.get('internshipcheck') - projectcheck = request.POST.get('projectcheck') - coursecheck = request.POST.get('coursecheck') - skillcheck = request.POST.get('skillcheck') - reference_list = request.POST.getlist('reference_checkbox_list') - extracurricularcheck = request.POST.get('extracurricularcheck') - conferencecheck = request.POST.get('conferencecheck') - else: - conferencecheck = '1' - achievementcheck = '1' - educationcheck = '1' - publicationcheck = '1' - patentcheck = '1' - internshipcheck = '1' - projectcheck = '1' - coursecheck = '1' - skillcheck = '1' - extracurricularcheck = '1' - - - - user = get_object_or_404(User, Q(username=username)) - profile = get_object_or_404(ExtraInfo, Q(user=user)) - student_info=get_object_or_404(Student,Q(id=user.username)) - - batch=student_info.batch - - now = datetime.datetime.now() - print("year----->",now.year) - if now.year-batch<=4: - roll=now.year-batch - else: - roll=4 - - - student = get_object_or_404(Student, Q(id=profile.id)) - skills = Has.objects.select_related('skill_id','unique_id').filter(Q(unique_id=student)) - education = Education.objects.select_related('unique_id').filter(Q(unique_id=student)) - reference = Reference.objects.filter(id__in=reference_list) - course = Course.objects.select_related('unique_id').filter(Q(unique_id=student)) - experience = Experience.objects.select_related('unique_id').filter(Q(unique_id=student)) - project = Project.objects.select_related('unique_id').filter(Q(unique_id=student)) - achievement = Achievement.objects.select_related('unique_id').filter(Q(unique_id=student)) - extracurricular = Extracurricular.objects.select_related('unique_id').filter(Q(unique_id=student)) - conference = Conference.objects.select_related('unique_id').filter(Q(unique_id=student)) - publication = Publication.objects.select_related('unique_id').filter(Q(unique_id=student)) - patent = Patent.objects.select_related('unique_id').filter(Q(unique_id=student)) - today = datetime.date.today() - - if len(reference) == 0: - referencecheck = '0' - else: - referencecheck = '1' - - return render_to_pdf('placementModule/cv.html', {'pagesize': 'A4', 'user': user, 'references': reference, - 'profile': profile, 'projects': project, - 'skills': skills, 'educations': education, - 'courses': course, 'experiences': experience, - 'referencecheck': referencecheck, - 'achievements': achievement, - 'extracurriculars': extracurricular, - 'publications': publication, - 'patents': patent, 'roll': roll, - 'achievementcheck': achievementcheck, - 'extracurricularcheck': extracurricularcheck, - 'educationcheck': educationcheck, - 'publicationcheck': publicationcheck, - 'patentcheck': patentcheck, - 'conferencecheck': conferencecheck, - 'conferences': conference, - 'internshipcheck': internshipcheck, - 'projectcheck': projectcheck, - 'coursecheck': coursecheck, - 'skillcheck': skillcheck, - 'today':today}) - - -def render_to_pdf(template_src, context_dict): - """ - The function is used to generate the cv in the pdf format. - Embeds the data into the predefined template. - @param: - template_src - template of cv to be rendered - context_dict - data fetched from the dtatabase to be filled in the cv template - @variables: - template - stores the template - html - html rendered pdf - result - variable to store data in BytesIO - pdf - storing encoded html of pdf version - """ - template = get_template(template_src) - html = template.render(context_dict) - result = BytesIO() - pdf = pisa.pisaDocument(BytesIO(html.encode("UTF-8")), result) - if not pdf.err: - return HttpResponse(result.getvalue(), content_type='application/pdf') - return HttpResponse('We had some errors
    %s
    ' % escape(html)) - - -def export_to_xls_std_records(qs): - """ - The function is used to generate the file in the xls format. - Embeds the data into the file. - """ - response = HttpResponse(content_type='application/ms-excel') - response['Content-Disposition'] = 'attachment; filename="report.xls"' - - wb = xlwt.Workbook(encoding='utf-8') - ws = wb.add_sheet('Report') - - row_num = 0 - - font_style = xlwt.XFStyle() - font_style.font.bold = True - - columns = ['Roll No.', 'Name', 'CPI', 'Department', 'Discipline', 'Placed', 'Debarred' ] - - for col_num in range(len(columns)): - ws.write(row_num, col_num, columns[col_num], font_style) - - font_style = xlwt.XFStyle() - - for student in qs: - row_num += 1 - - row = [] - row.append(student.id.id) - row.append(student.id.user.first_name+' '+student.id.user.last_name) - row.append(student.cpi) - row.append(student.programme) - row.append(student.id.department.name) - if student.studentplacement.placed_type == "PLACED": - row.append('Yes') - else: - row.append('No') - if student.studentplacement.placed_type == "DEBAR": - row.append('Yes') - else: - row.append('No') - - for col_num in range(len(row)): - ws.write(row_num, col_num, row[col_num], font_style) - - wb.save(response) - return response -def resume(request, username): - # Retrieve data or whatever you need - """ - The function is used to generate the cv in the pdf format. - Embeds the data into the predefined template. - @param: - request - trivial - username - name of user whose cv is to be generated - @variables: - user = stores current user - profile = stores extrainfo of user - current = Stores all working students from HoldsDesignation for the respective degignation - achievementcheck = variable for achievementcheck in form for cv generation - educationcheck = variable for educationcheck in form for cv generation - publicationcheck = variable for publicationcheck in form for cv generation - patentcheck = variable for patentcheck in form for cv generation - internshipcheck = variable for internshipcheck in form for cv generation - projectcheck = variable for projectcheck in form for cv generation - coursecheck = variable for coursecheck in form for cv generation - skillcheck = variable for skillcheck in form for cv generation - user = get_object_or_404(User, Q(username=username)) - profile = get_object_or_404(ExtraInfo, Q(user=user)) - import datetime - now = stores current timestamp - roll = roll of the user - student = variable storing the profile data - studentplacement = variable storing the placement data - skills = variable storing the skills data - education = variable storing the education data - course = variable storing the course data - experience = variable storing the experience data - project = variable storing the project data - achievement = variable storing the achievement data - publication = variable storing the publication data - patent = variable storing the patent data - """ - user = request.user - profile = get_object_or_404(ExtraInfo, Q(user=user)) - - current = HoldsDesignation.objects.filter(Q(working=user, designation__name="student")) - if current: - if request.method == 'POST': - achievementcheck = request.POST.get('achievementcheck') - educationcheck = request.POST.get('educationcheck') - publicationcheck = request.POST.get('publicationcheck') - patentcheck = request.POST.get('patentcheck') - internshipcheck = request.POST.get('internshipcheck') - projectcheck = request.POST.get('projectcheck') - coursecheck = request.POST.get('coursecheck') - skillcheck = request.POST.get('skillcheck') - reference_list = request.POST.getlist('reference_checkbox_list') - extracurricularcheck = request.POST.get('extracurricularcheck') - conferencecheck = request.POST.get('conferencecheck') - else: - conferencecheck = '1' - achievementcheck = '1' - educationcheck = '1' - publicationcheck = '1' - patentcheck = '1' - internshipcheck = '1' - projectcheck = '1' - coursecheck = '1' - skillcheck = '1' - extracurricularcheck = '1' - - - # print(achievementcheck,' ',educationcheck,' ',publicationcheck,' ',patentcheck,' ',internshipcheck,' ',projectcheck,' \n\n\n') - user = get_object_or_404(User, Q(username=username)) - profile = get_object_or_404(ExtraInfo, Q(user=user)) - now = datetime.datetime.now() - if int(str(profile.id)[:2]) == 20: - if (now.month>4): - roll = 1+now.year-int(str(profile.id)[:4]) - else: - roll = now.year-int(str(profile.id)[:4]) - else: - if (now.month>4): - roll = 1+(now.year)-int("20"+str(profile.id)[0:2]) - else: - roll = (now.year)-int("20"+str(profile.id)[0:2]) - - student = get_object_or_404(Student, Q(id=profile.id)) - skills = Has.objects.select_related('skill_id','unique_id').filter(Q(unique_id=student)) - education = Education.objects.select_related('unique_id').filter(Q(unique_id=student)) - reference = Reference.objects.filter(id__in=reference_list) - course = Course.objects.select_related('unique_id').filter(Q(unique_id=student)) - experience = Experience.objects.select_related('unique_id').filter(Q(unique_id=student)) - project = Project.objects.select_related('unique_id').filter(Q(unique_id=student)) - achievement = Achievement.objects.select_related('unique_id').filter(Q(unique_id=student)) - extracurricular = Extracurricular.objects.select_related('unique_id').filter(Q(unique_id=student)) - conference = Conference.objects.select_related('unique_id').filter(Q(unique_id=student)) - publication = Publication.objects.select_related('unique_id').filter(Q(unique_id=student)) - patent = Patent.objects.select_related('unique_id').filter(Q(unique_id=student)) - today = datetime.date.today() - - if len(reference) == 0: - referencecheck = '0' - else: - referencecheck = '1' - - return render_to_pdf('placementModule/cv.html', {'pagesize': 'A4', 'user': user, 'references': reference, - 'profile': profile, 'projects': project, - 'skills': skills, 'educations': education, - 'courses': course, 'experiences': experience, - 'referencecheck': referencecheck, - 'achievements': achievement, - 'extracurriculars': extracurricular, - 'publications': publication, - 'patents': patent, 'roll': roll, - 'achievementcheck': achievementcheck, - 'extracurricularcheck': extracurricularcheck, - 'educationcheck': educationcheck, - 'publicationcheck': publicationcheck, - 'patentcheck': patentcheck, - 'conferencecheck': conferencecheck, - 'conferences': conference, - 'internshipcheck': internshipcheck, - 'projectcheck': projectcheck, - 'coursecheck': coursecheck, - 'skillcheck': skillcheck, - 'today':today}) - - - -def export_to_xls_invitation_status(qs): - response = HttpResponse(content_type='application/ms-excel') - response['Content-Disposition'] = 'attachment; filename="report.xls"' - - wb = xlwt.Workbook(encoding='utf-8') - ws = wb.add_sheet('Report') - - - row_num = 0 - - font_style = xlwt.XFStyle() - font_style.font.bold = True - - columns = ['Roll No.', 'Name', 'Company', 'CTC', 'Invitation Status'] - - for col_num in range(len(columns)): - ws.write(row_num, col_num, columns[col_num], font_style) - - - font_style = xlwt.XFStyle() - - for student in qs: - row_num += 1 - - row = [] - row.append(student.unique_id.id.id) - row.append(student.unique_id.id.user.first_name+' '+student.unique_id.id.user.last_name) - row.append(student.notify_id.company_name) - row.append(student.notify_id.ctc) - row.append(student.invitation) - - for col_num in range(len(row)): - ws.write(row_num, col_num, row[col_num], font_style) - - wb.save(response) - return response - - -def check_invitation_date(placementstatus): - """ - The function is used to run before render of student placement view for ensuring that - last date for RESPONSE is not passed - @param: - placementstatus - queryset containing placement status of particular student - @variables: - ps - individual PlacementStatus object - """ - try: - for ps in placementstatus: - if ps.invitation=='PENDING': - dt = ps.timestamp+datetime.timedelta(days=ps.no_of_days) - if dt name, version , batch , no of semester -class BatchSerializer(serializers.ModelSerializer): - curriculum_display = serializers.SerializerMethodField() - - class Meta: - model = Batch - fields = '__all__' - - def get_curriculum_display(self, obj): - """ - Get appropriate curriculum display for both single and multi-curriculum batches - """ - from applications.programme_curriculum.api.views_student_management import get_batch_curriculum_display - return get_batch_curriculum_display(obj) - - - -class CurriculumBatchSerializer(serializers.ModelSerializer): - class Meta: - model = Curriculum - fields = ['name', 'version', 'no_of_semester'] - -class CurriculumSerializer(serializers.ModelSerializer): - class Meta: - model = Curriculum - fields = ['id','name', 'version', 'batches', 'no_of_semester'] - - def to_representation(self, instance): - representation = super().to_representation(instance) - batches = instance.batches.all() - representations = [] - for batch in batches: - batch_representation = { - 'id':representation['id'], - 'name': representation['name'], - 'version': representation['version'], - 'no_of_semester': representation['no_of_semester'], - 'batch': f"{representation['name']} {batch.year}" - } - representations.append(batch_representation) - return representations - - - -class ProgrammeInfoSerializer(serializers.ModelSerializer): - - class Meta: - model = Programme - fields = ['category', 'name', 'programme_begin_year'] - - - - -# this is for Semester model ... -# no frontend form for this model ... -class SemesterSerializer(serializers.ModelSerializer): - class Meta: - model = Semester - fields = "__all__" - - - -# this is for course model ... -# fields in frontend form --> coursecode, coursename, credit -class CourseSerializer(serializers.ModelSerializer): - class Meta: - model = Course - fields = "__all__" - - - -# this is for Batch model ... -# field in frontend form --> name, discipline, year, curriculum . -# class BatchSerializer(serializers.ModelSerializer): -# class Meta: -# model = Batch -# fields = "__all__" - - -# for this 2 there is no frontend form ... - - -# CourseSlot model serializers ... -class CourseSlotSerializer(serializers.ModelSerializer): - class Meta: - model = CourseSlot - fields = "__all__" - - - -# CourseInstructor model serializers ... -class CourseInstructorSerializer(serializers.ModelSerializer): - class Meta: - model = CourseInstructor - fields = "__all__" - - -class ProgrammePostSerializer(serializers.ModelSerializer): - class Meta: - model = Programme - fields = ['id', 'category', 'name', 'programme_begin_year'] - -class StudentBatchUploadSerializer(serializers.ModelSerializer): - class Meta: - model = StudentBatchUpload - fields = '__all__' diff --git a/FusionIIIT/applications/programme_curriculum/api/urls.py b/FusionIIIT/applications/programme_curriculum/api/urls.py deleted file mode 100644 index 7f3587a1e..000000000 --- a/FusionIIIT/applications/programme_curriculum/api/urls.py +++ /dev/null @@ -1,157 +0,0 @@ -from django.conf.urls import url -from django.urls import path, include -from django.contrib import admin -from . import views -from . import views_student_management -from .. import views_password_email - - -urlpatterns = [ - path('programmes/', views.view_all_programmes, name='view_all_programmes'), - path('working_curriculums/', views.view_all_working_curriculums, name='view_all_working_curriculums'), - path('curriculums//', views.view_curriculums_of_a_programme, name='view_curriculums_of_a_programme'), - path('curriculum_semesters//', views.view_semesters_of_a_curriculum, name='view_semesters_of_a_curriculum'), - path('semester//', views.view_a_semester_of_a_curriculum, name='view_a_semester_of_a_curriculum'), - path('courseslot//', views.view_a_courseslot, name='view_a_courseslot'), - path('courses/', views.view_all_courses, name='view_all_courses'), - path('course//', views.view_a_course, name='view_a_course'), - path('disciplines/', views.view_all_discplines, name='view_all_discplines'), - path('batches/', views.view_all_batches, name='view_all_batches'), - - # Admin views - path('admin_programmes/', views.admin_view_all_programmes, name='admin_view_all_programmes'), - path('admin_working_curriculums/', views.Admin_view_all_working_curriculums, name='admin_view_all_working_curriculums'), - path('admin_curriculums//', views.admin_view_curriculums_of_a_programme, name='admin_view_curriculums_of_a_programme'), - path('admin_curriculum_semesters//', views.admin_view_semesters_of_a_curriculum, name='admin_view_semesters_of_a_curriculum'), - path('admin_semester//', views.admin_view_a_semester_of_a_curriculum, name='admin_view_a_semester_of_a_curriculum'), - path('admin_courseslot//', views.admin_view_a_courseslot, name='admin_view_a_courseslot'), - path('admin_courses/', views.admin_view_all_courses, name='admin_view_all_courses'), - path('admin_course//', views.admin_view_a_course, name='admin_view_a_course'), - path('admin_disciplines/', views.admin_view_all_discplines, name='admin_view_all_discplines'), - - # Admin forms - path('admin_add_programme/', views.add_programme_form, name='add_programme_form'), - path('admin_add_discipline/', views.add_discipline_form, name='add_discipline_form'), - path('admin_add_curriculum/', views.add_curriculum_form, name='add_curriculum_form'), - path('admin_add_course/', views.add_course_form, name='add_course_form'), - path('admin_add_courseslot/', views.add_courseslot_form, name='add_courseslot_form'), - path('admin_add_course/', views.add_course_form, name='add_course_form'), - path('admin_add_batch/', views_student_management.create_batch, name='admin_add_batch'), - - path('admin_update_course//', views.update_course_form, name='update_course_form'), - path('admin_course_audit_logs//', views.course_audit_logs, name='course_audit_logs'), - path('admin_edit_curriculum//', views.edit_curriculum_form, name='edit_curriculum_form'), - path('admin_edit_programme//', views.edit_programme_form, name='edit_programme_form'), - path('admin_edit_courseslot//', views.edit_courseslot_form, name='edit_courseslot_form'), - path('admin_delete_courseslot//', views.delete_courseslot, name='delete_courseslot'), - path('admin_edit_batch//', views.edit_batch_form, name='edit_batch_form'), - path('admin_edit_discipline//', views.edit_discipline_form, name='edit_discipline_form'), - path('admin_instigate_semester//', views.instigate_semester, name='instigate_semester'), - path('admin_replicate_curriculum//', views.replicate_curriculum, name='replicate_curriculum'), - - # Course proposals - path('view_course_proposal_forms/',views.view_course_proposal_forms,name='view_course_proposal_forms'), - path('faculty_view_all_courses/', views.faculty_view_all_courses, name='faculty_view_all_courses'), - path('faculty_view_a_course//',views.faculty_view_a_course,name="faculty_view_a_course"), - path('reject_form//', views.reject_form, name='reject_form'), - path('new_course_proposal_file/',views.new_course_proposal_file,name='new_course_proposal_file'), - path('update_course_proposal_file//',views.update_course_proposal_file,name='update_course_proposal_file'), - - path('view_a_course_proposal_form//',views.view_a_course_proposal_form,name='view_a_course_proposal_form'), - path('filetracking//',views.filetracking,name='filetracking'), - path('inward_files/',views.inward_files,name='inward_files'), - path('forward_course_forms//',views.forward_course_forms,name='forward_course_forms'), - path('forward_course_forms_II/',views.forward_course_forms_II,name='forward_course_forms'), - path('view_inward_files//',views.view_inward_files,name='view_inward_files'), - path('outward_files/',views.outward_files,name='outward_files'), - path('tracking_archive//',views.tracking_archive,name='tracking_archive'), - path('tracking_unarchive//',views.tracking_unarchive,name='tracking_unarchive'), - path('file_archive//',views.file_archive,name='file_archive'), - path('file_unarchive//',views.file_unarchive,name='file_unarchive'), - path('get_superior_data/', views.get_superior_data, name='get_superior_data'), - # Admin utilities - path('admin_get_course_slot_type/',views.course_slot_type_choices,name='course_slot_type_choices'), - path('admin_get_semesterDetails/',views.semester_details,name='semester_details'), - path('admin_get_program//',views.get_programme,name='get_program'), - - path('admin_get_batch_name/', views.get_batch_names, name='get_batch_names'), - path('admin_get_disciplines/', views.get_all_disciplines, name='get_all_disciplines'), - path('admin_get_unlinked_curriculam/', views.get_unused_curriculam, name='get_unused_curricula'), - - # Course instructors - path('admin_course_instructors/',views.admin_view_all_course_instructor,name='admin_view_all_course_instructor'), - path('admin_instructor/',views.admin_view_all_course_instructor,name='admin_view_all_course_instructor_legacy'), - path('admin_faculties/', views.admin_view_all_faculties, name='admin_view_all_faculties'), - path('admin_add_course_instructor/', views.add_course_instructor, name='add_course_instructor'), - path('admin_update_course_instructor//', views.update_course_instructor_form, name='update_course_instructor_form'), - path('admin_delete_course_instructor//', views.admin_delete_course_instructor, name='admin_delete_course_instructor'), - - # Delete APIs - path('admin_delete_course//', views.admin_delete_course, name='admin_delete_course'), - path('admin_delete_programme//', views.admin_delete_programme, name='admin_delete_programme'), - path('admin_delete_curriculum//', views.admin_delete_curriculum, name='admin_delete_curriculum'), - path('admin_delete_discipline//', views.admin_delete_discipline, name='admin_delete_discipline'), - - # Student management - path('admin_batches/', views_student_management.admin_batches_unified, name='admin_batches_unified'), - path('admin_batch_students//', views_student_management.get_batch_students, name='get_batch_students'), - path('batches//students/', views_student_management.get_batch_students, name='get_batch_students_legacy'), - path('process_excel_upload/', views_student_management.process_excel_upload, name='process_excel_upload'), - path('admin_process_excel_upload/', views_student_management.process_excel_upload, name='admin_process_excel_upload'), - path('save_students_batch/', views_student_management.save_students_batch, name='save_students_batch'), - path('admin_save_students_batch/', views_student_management.save_students_batch, name='admin_save_students_batch'), - path('admin_add_single_student/', views_student_management.add_single_student, name='admin_add_single_student'), - path('set_total_seats/', views_student_management.set_total_seats, name='set_total_seats'), - path('admin_set_total_seats/', views_student_management.set_total_seats, name='admin_set_total_seats'), - path('update_student_status/', views_student_management.update_student_status, name='update_student_status'), - path('admin_update_student_status/', views_student_management.update_student_status, name='admin_update_student_status'), - path('bulk_update_student_status/', views_student_management.bulk_update_student_status, name='bulk_update_student_status'), - path('admin_bulk_update_student_status/', views_student_management.bulk_update_student_status, name='admin_bulk_update_student_status'), - path('fix_stuck_reported_students/', views_student_management.fix_stuck_reported_students, name='fix_stuck_reported_students'), - path('download_students//', views_student_management.export_students, name='export_students'), - path('admin_export_students//', views_student_management.export_students, name='admin_export_students'), - path('upload_history/', views_student_management.upload_history, name='upload_history'), - path('admin_upload_history/', views_student_management.upload_history, name='admin_upload_history'), - path('list_students/', views_student_management.list_students, name='list_students'), - path('admin_list_students/', views_student_management.list_students, name='admin_list_students'), - - # Individual student CRUD - path('student//', views_student_management.get_student, name='get_student'), - path('student//update/', views_student_management.update_student, name='update_student'), - path('student//delete/', views_student_management.delete_student, name='delete_student'), - path('admin_student//', views_student_management.get_student, name='admin_get_student'), - path('admin_student//update/', views_student_management.update_student, name='admin_update_student'), - path('admin_student//delete/', views_student_management.delete_student, name='admin_delete_student'), - - # Batch management CRUD - path('batches/create/', views_student_management.create_batch, name='create_batch'), - path('batches//update/', views_student_management.update_batch, name='update_batch'), - path('batches//delete/', views.delete_batch, name='delete_batch'), - path('batches//delete/', views.delete_batch_invalid, name='delete_batch_invalid'), - path('admin_delete_batch//', views.delete_batch, name='admin_delete_batch'), - path('batches/list/', views_student_management.list_batches_with_status, name='list_batches_with_status'), - path('batches/sync/', views_student_management.sync_batch_data, name='sync_batch_data'), - path('batches/sync_to_configuration/', views_student_management.sync_batches_to_configuration, name='sync_batches_to_configuration'), - path('batches/validate_prerequisites/', views_student_management.validate_batch_prerequisites, name='validate_batch_prerequisites'), - path('batches/curriculum_status/', views_student_management.check_batches_curriculum_status, name='check_batches_curriculum_status'), - path('validate_student_upload_prerequisites/', views_student_management.validate_student_upload_prerequisites, name='validate_student_upload_prerequisites'), - path('find_duplicate_curriculums/', views_student_management.find_duplicate_curriculums, name='find_duplicate_curriculums'), - path('consolidate_duplicate_curriculums/', views_student_management.consolidate_duplicate_curriculums, name='consolidate_duplicate_curriculums'), - path('find_duplicate_batches/', views_student_management.find_duplicate_batches, name='find_duplicate_batches'), - path('consolidate_duplicate_batches/', views_student_management.consolidate_duplicate_batches, name='consolidate_duplicate_batches'), - - # Student status management - path('students//update_status/', views_student_management.update_student_status_crud, name='update_student_status_crud'), - - # Password management - path('batches/auto_generate_passwords/', views_student_management.auto_generate_passwords_for_batch, name='auto_generate_passwords_for_batch'), - - # Password email management - path('send_student_password/', views_password_email.send_student_password, name='send_student_password'), - path('admin_send_student_password/', views_password_email.send_student_password, name='admin_send_student_password'), - path('bulk_send_passwords/', views_password_email.bulk_send_passwords, name='bulk_send_passwords'), - path('admin_bulk_send_passwords/', views_password_email.bulk_send_passwords, name='admin_bulk_send_passwords'), - path('password_email_status//', views_password_email.password_email_status, name='password_email_status'), - path('bulk_operation_status//', views_password_email.bulk_operation_status, name='bulk_operation_status'), - path('manage_email_templates/', views_password_email.manage_email_templates, name='manage_email_templates'), -] diff --git a/FusionIIIT/applications/programme_curriculum/api/views.py b/FusionIIIT/applications/programme_curriculum/api/views.py deleted file mode 100644 index dec3ea7ce..000000000 --- a/FusionIIIT/applications/programme_curriculum/api/views.py +++ /dev/null @@ -1,4321 +0,0 @@ -from django.db.models.query_utils import Q -from django.http import request -from django.shortcuts import get_object_or_404, render, HttpResponse,redirect -from django.http import HttpResponse, HttpResponseRedirect,JsonResponse -from django.views.decorators.http import require_http_methods -import datetime -# import itertools -from django.contrib import messages -from django.contrib.auth.decorators import login_required -from django.contrib.auth.models import User -from ..models import Programme, Discipline, Curriculum, Semester, Course, Batch, CourseSlot,NewProposalFile,Proposal_Tracking,CourseInstructor,CourseAuditLog -from ..forms import ProgrammeForm, DisciplineForm, CurriculumForm, SemesterForm, CourseForm, BatchForm, CourseSlotForm, ReplicateCurriculumForm,NewCourseProposalFile,CourseProposalTrackingFile, CourseInstructor, CourseInstructorForm -from ..filters import CourseFilter, BatchFilter, CurriculumFilter - -from .serializers import CourseSerializer,CurriculumSerializer,BatchSerializer -from .views_student_management import get_batch_curriculum_display, get_available_curriculums_for_batch -from django.core.serializers import serialize -from django.db import IntegrityError, transaction -from django.utils import timezone -from django.forms.models import model_to_dict -import json, xlrd -from django.db.models import F -from rest_framework.response import Response -from rest_framework.decorators import api_view, permission_classes, authentication_classes -from rest_framework.permissions import IsAuthenticated -from rest_framework.authentication import TokenAuthentication -from django.core.exceptions import PermissionDenied -from django.utils.decorators import method_decorator -from django.views.decorators.csrf import csrf_exempt -from rest_framework import status -import re - -from notification.views import prog_and_curr_notif -# from applications.academic_information.models import Student -from applications.globals.models import (DepartmentInfo, Designation,ExtraInfo, Faculty, HoldsDesignation) -# ------------module-functions---------------# - -@login_required(login_url='/accounts/login') -def programme_curriculum(request): - """ - This function is used to Differenciate acadadmin and all other user. - - @param: - request - contains metadata about the requested page - - @variables: - user_details - Gets the information about the logged in user. - des - Gets the designation about the looged in user. - """ - user=request.user - - user_details = ExtraInfo.objects.get(user = request.user) - des = HoldsDesignation.objects.all().filter(user = request.user).first() - if request.session['currentDesignationSelected']== "student" or request.session['currentDesignationSelected']== "Associate Professor" or request.session['currentDesignationSelected']== "Professor" or request.session['currentDesignationSelected']== "Assistant Professor" or request.session['currentDesignationSelected']== "Dean Academic": - return HttpResponseRedirect('/programme_curriculum/programmes/') - elif 'hod' in request.session['currentDesignationSelected'].lower() : - return HttpResponseRedirect('/programme_curriculum/programmes/') - elif request.session['currentDesignationSelected'] == "acadadmin" : - return HttpResponseRedirect('/programme_curriculum/admin_programmes') - - return HttpResponseRedirect('/programme_curriculum/programmes/') - - - -# ------------all-user-functions---------------# - - - -def view_all_programmes(request): - """ - This function is used to display all the programmes offered by the institute. - @variables: - ug - UG programmes - pg - PG programmes - phd - PHD programmes - """ - url='programme_curriculum/' - user_details = ExtraInfo.objects.get(user = request.user) - des = HoldsDesignation.objects.all().filter(user = request.user).first() - if request.session['currentDesignationSelected']== "acadadmin": - return render(request, 'programme_curriculum/admin_programmes/') - - elif request.session['currentDesignationSelected']== "Associate Professor" or request.session['currentDesignationSelected']== "Professor" or request.session['currentDesignationSelected']== "Assistant Professor" or request.session['currentDesignationSelected']== "Dean Academic" : - url+='faculty/' - - elif 'hod' in request.session['currentDesignationSelected'].lower(): - url+='faculty/' - notifs = request.user.notifications.all() - - ug = Programme.objects.filter(category='UG') - pg = Programme.objects.filter(category='PG') - phd = Programme.objects.filter(category='PHD') - url+='view_all_programmes.html' - - return render(request, url, {'ug': ug, 'pg': pg, 'phd': phd,'notifications': notifs,}) - - -def view_curriculums_of_a_programme(request, programme_id): - """ - This function is used to Display Curriculum of a specific Programmes in JSON format. - - @param: - programme_id - Id of a specific programme - - @variables: - curriculums - Curriculums of a specific programmes - working_curriculum - Curriculums that are active - past_curriculum - Curriculums that are obsolete - """ - # user_details = ExtraInfo.objects.get(user=request.user) - # des = HoldsDesignation.objects.filter(user=request.user).first() - - # Commented out role-based URL adjustments - # if request.session['currentDesignationSelected'] == "acadadmin": - # return render(request, 'programme_curriculum/admin_programmes/') - - # elif request.session['currentDesignationSelected'] in ["Associate Professor", "Professor", "Assistant Professor", "Dean Academic"]: - # url += 'faculty/' - - # elif 'hod' in request.session['currentDesignationSelected'].lower(): - # url += 'faculty/' - - # Fetch program and related curriculums - program = get_object_or_404(Programme, id=programme_id) - curriculums = program.curriculums.all() # Adjust if it's a related name - - - # Apply filters - curriculumfilter = CurriculumFilter(request.GET, queryset=curriculums) - curriculums = curriculumfilter.qs - - # Separate working and past curriculums - batches = Batch.objects.all() - working_curriculums = curriculums.filter(working_curriculum=1) - past_curriculums = curriculums.filter(working_curriculum=0) - - # Prepare JSON data - data = { - 'program': model_to_dict(program), - 'name':program.name, - # 'working_curriculums': [model_to_dict(c) for c in working_curriculums], - 'working_curriculums': [ - { - **model_to_dict(c), - 'batches': [ - { - 'id': b.id, - 'name': b.name, - 'discipline': b.discipline.acronym, - 'year': b.year, - 'running_batch': b.running_batch, - # 'total_seats': b.total_seats, - } - for b in c.batches.all() - ] - # Add batches for each curriculum - } - for c in working_curriculums - ], - 'past_curriculums': [ - { - **model_to_dict(c), - 'batches': [ - { - 'id': b.id, - 'name': b.name, - 'discipline': b.discipline.acronym, # Use acronym instead of ID - 'year': b.year, - 'running_batch': b.running_batch, - # 'total_seats': b.total_seats, - } - for b in c.batches.all() - ] - } - for c in past_curriculums - ], - # 'notifications': [model_to_dict(n) for n in request.user.notifications.all()] - } - - return JsonResponse(data) - - -def view_all_working_curriculums(request): - - """ views all the working curriculums offered by the institute """ - url='programme_curriculum/' - user_details = ExtraInfo.objects.get(user = request.user) - des = HoldsDesignation.objects.all().filter(user = request.user).first() - if request.session['currentDesignationSelected']== "acadadmin": - return render(request, 'programme_curriculum/admin_programmes/') - - elif request.session['currentDesignationSelected']== "Associate Professor" or request.session['currentDesignationSelected']== "Professor" or request.session['currentDesignationSelected']== "Assistant Professor" or request.session['currentDesignationSelected']== "Dean Academic" : - - url+='faculty/' - - elif 'hod' in request.session['currentDesignationSelected'].lower(): - url+='faculty/' - curriculums = Curriculum.objects.filter(working_curriculum=1) - notifs = request.user.notifications.all() - curriculumfilter = CurriculumFilter(request.GET, queryset=curriculums) - - curriculums = curriculumfilter.qs - return render(request,url+'view_all_working_curriculums.html',{'curriculums':curriculums, 'curriculumfilter': curriculumfilter,'notifications': notifs,}) - -def view_semesters_of_a_curriculum(request, curriculum_id): - """ API to get all the semesters of a specific curriculum """ - - # user_details = ExtraInfo.objects.get(user=request.user) - # des = HoldsDesignation.objects.all().filter(user=request.user).first() - - # # Redirect logic based on user designation - # if request.session['currentDesignationSelected'] in ["student", "Associate Professor", "Professor", "Assistant Professor"]: - # return HttpResponseRedirect('/programme_curriculum/programmes/') - # elif str(request.user) == "acadadmin": - # pass - # elif 'hod' in request.session['currentDesignationSelected'].lower(): - # return HttpResponseRedirect('/programme_curriculum/programmes/') - - curriculum = get_object_or_404(Curriculum, Q(id=curriculum_id)) - semesters = curriculum.semesters.all() - - semester_slots = [] - for sem in semesters: - a = list(sem.courseslots.all()) - semester_slots.append(a) - - # Pad the course slots to ensure even length - max_length = max(len(course_slots) for course_slots in semester_slots) - for course_slots in semester_slots: - course_slots += [""] * (max_length - len(course_slots)) - - # Calculate total credits for each semester - semester_credits = [] - for semester in semesters: - credits_sum = 0 - for course_slot in semester.courseslots.all(): - max_credit = max(course.credit for course in course_slot.courses.all()) - credits_sum += max_credit - semester_credits.append(credits_sum) - - # Transpose the semester slots for easy tabular representation - transpose_semester_slots = list(zip(*semester_slots)) - - # Get all batches excluding the current curriculum - all_batches = Batch.objects.filter(running_batch=True, curriculum=curriculum_id).order_by('year') - - # Prepare the response data structure - data = { - 'name': curriculum.name, - 'no_of_semester': len(semesters), - 'semesters': [{ - 'id': sem.id, - 'semester_no': sem.semester_no, - 'start_semester': sem.start_semester, # Add any other fields needed by the frontend - 'end_semester': sem.end_semester, - 'instigate_semester': sem.instigate_semester, - } for sem in semesters], - 'semester_slots': [ - [{ - 'id': course_slot.id, - 'name': course_slot.name, - # 'code':course_slot.code, - 'courses': [{'name': course.name, 'code':course.code,'lecture_hours': course.lecture_hours, 'tutorial_hours': course.tutorial_hours, 'credit': course.credit} for course in course_slot.courses.all()] - } if course_slot else None for course_slot in course_slots] - for course_slots in transpose_semester_slots - ], - 'semester_credits': semester_credits, - 'batches': list(all_batches.values_list('name', 'year')) - } - - # Return the data as a JSON response for the frontend - return JsonResponse(data) - - -def view_a_semester_of_a_curriculum(request, semester_id): - """ - Retrieve and return details of a specific semester of a specific curriculum in JSON format. - - Args: - - semester_id: The ID of the semester to fetch. - - Returns: - - JSON response containing semester details and associated course slots. - """ - # Fetch the specific semester or return a 404 if not found - semester = get_object_or_404(Semester, id=semester_id) - - # Fetch the associated course slots for the semester - course_slots = semester.courseslots # Using the property defined in the Semester model - - # Prepare data for the semester and its course slots - semester_data = { - 'id': semester.id, - 'curriculum': semester.curriculum.name, # Get the curriculum name - 'semester_no': semester.semester_no, # Use semester_no from the Semester model - 'start_semester': semester.start_semester.isoformat() if semester.start_semester else None, - 'end_semester': semester.end_semester.isoformat() if semester.end_semester else None, - 'instigate_semester': semester.instigate_semester, - 'semester_info': semester.semester_info, - 'course_slots': [ - { - 'id': slot.id, - 'type': slot.type, # Assuming the CourseSlot model has a 'type' field - 'course_slot_info': slot.course_slot_info, # Assuming this field exists in CourseSlot - 'duration': slot.duration, # Assuming this field exists - 'name': slot.name, - 'courses': [ - { - 'id': course.id, - 'code': course.code, - 'name': course.name, - 'credits': course.credit, - } for course in slot.courses.all() # Fetch related courses for each slot - ] - } for slot in course_slots - ] - } - - # Return the semester and course slot details as JSON - return JsonResponse({'semester': semester_data}) - - -def view_a_courseslot(request, courseslot_id): - """ view a course slot """ - # user_details = ExtraInfo.objects.get(user=request.user) - # des = HoldsDesignation.objects.all().filter(user=request.user).first() - - # if request.session['currentDesignationSelected'] == "acadadmin": - # # Return relevant response for 'acadadmin' - # return JsonResponse({'message': 'Unauthorized for this operation'}, status=403) - - # elif request.session['currentDesignationSelected'] in ["Associate Professor", "Professor", "Assistant Professor", "Dean Academic"]: - # # Handling for faculty members - # user_type = "faculty" - - # elif 'hod' in request.session['currentDesignationSelected'].lower(): - # # Handling for hod - # user_type = "faculty" - - # else: - # # Default case if no designation matched - # return JsonResponse({'message': 'Invalid designation'}, status=403) - - # Fetch the course slot details - course_slot = get_object_or_404(CourseSlot, id=courseslot_id) - - # Prepare the course slot data to return in JSON format - data = { - 'id': course_slot.id, - 'name': course_slot.name, - 'semester': course_slot.semester.__str__(), # You can use str or a specific field from the semester - 'type': course_slot.type, - 'course_slot_info': course_slot.course_slot_info, - 'min_registration_limit': course_slot.min_registration_limit, - 'max_registration_limit': course_slot.max_registration_limit, - 'duration': course_slot.duration, - 'courses': [ - { - 'id': course.id, - 'code': course.code, - 'name': course.name, - 'credit': course.credit, - } for course in course_slot.courses.all() - ] - } - - # Return the course slot data as a JSON response - return JsonResponse(data) - - -def view_all_courses(request): - """ - Retrieve and return details of all courses in JSON format. - - Returns: - - JSON response containing a list of courses. - """ - # Fetch all courses - courses = Course.objects.all() - - # Apply filtering based on request parameters (if any) - coursefilter = CourseFilter(request.GET, queryset=courses) - filtered_courses = coursefilter.qs - - # Prepare the data to return in JSON format - courses_data = [ - { - 'id': course.id, - 'code': course.code, - 'name': course.name, - 'credit': course.credit, - 'department': course.department.name, # Assuming course has a department relation - 'semester': course.semester.__str__(), # You can change this based on the relation structure - } for course in filtered_courses - ] - - # Return the filtered courses as a JSON response - return JsonResponse({'courses': courses_data}) - - -def view_a_course(request, course_id): - """ views the details of a Course """ - url='programme_curriculum/' - user_details = ExtraInfo.objects.get(user = request.user) - des = HoldsDesignation.objects.all().filter(user = request.user).first() - if request.session['currentDesignationSelected']== "acadadmin": - return render(request, 'programme_curriculum/admin_programmes/') - elif request.session['currentDesignationSelected']== "Associate Professor" or request.session['currentDesignationSelected']== "Professor" or request.session['currentDesignationSelected']== "Assistant Professor" or request.session['currentDesignationSelected']== "Dean Academic" : - - url+='faculty/' - elif 'hod' in request.session['currentDesignationSelected'].lower(): - url+='faculty/' - course = get_object_or_404(Course, Q(id=course_id)) - notifs = request.user.notifications.all() - return render(request, url+'view_a_course.html', {'course': course,'notifications': notifs,}) - - -def view_all_discplines(request): - """ views the details of a Course """ - url='programme_curriculum/' - user_details = ExtraInfo.objects.get(user = request.user) - des = HoldsDesignation.objects.all().filter(user = request.user).first() - if request.session['currentDesignationSelected']== "acadadmin": - return render(request, 'programme_curriculum/admin_programmes/') - elif request.session['currentDesignationSelected']== "Associate Professor" or request.session['currentDesignationSelected']== "Professor" or request.session['currentDesignationSelected']== "Assistant Professor" or request.session['currentDesignationSelected']== "Dean Academic" : - url+='faculty/' - - elif 'hod' in request.session['currentDesignationSelected'].lower(): - url+='faculty/' - - disciplines = Discipline.objects.all() - notifs = request.user.notifications.all() - return render(request, url+'view_all_disciplines.html', {'disciplines': disciplines,'notifications': notifs,}) - - -def view_all_batches(request): - """ views the details of a Course """ - url='programme_curriculum/' - user_details = ExtraInfo.objects.get(user = request.user) - des = HoldsDesignation.objects.all().filter(user = request.user).first() - if request.session['currentDesignationSelected']== "acadadmin": - return render(request, 'programme_curriculum/admin_programmes/') - elif request.session['currentDesignationSelected']== "Associate Professor" or request.session['currentDesignationSelected']== "Professor" or request.session['currentDesignationSelected']== "Assistant Professor" or request.session['currentDesignationSelected']== "Dean Academic" : - - url+='faculty/' - elif 'hod' in request.session['currentDesignationSelected'].lower(): - url+='faculty/' - - batches = Batch.objects.all().order_by('year') - - batchfilter = BatchFilter(request.GET, queryset=batches) - - batches = batchfilter.qs - - finished_batches = batches.filter(running_batch=False) - - batches = batches.filter(running_batch=True) - notifs = request.user.notifications.all() - - return render(request, url+'view_all_batches.html', {'batches': batches, 'finished_batches': finished_batches, 'batchfilter': batchfilter,'notifications': notifs,}) - - - - -# ------------Acad-Admin-functions---------------# - - -# @api_view(['GET']) -# @login_required(login_url='/accounts/login') -def admin_view_all_programmes(request): - """ - API to return all programmes (UG, PG, PhD) for an admin user. - """ - # Fetch programmes based on their category - ug = Programme.objects.filter(category='UG').prefetch_related('discipline_set').values( - 'id', 'name', 'category', 'programme_begin_year', 'discipline__name' - ) - pg = Programme.objects.filter(category='PG').prefetch_related('discipline_set').values( - 'id', 'name', 'category', 'programme_begin_year', 'discipline__name' - ) - phd = Programme.objects.filter(category='PHD').prefetch_related('discipline_set').values( - 'id', 'name', 'category', 'programme_begin_year', 'discipline__name' - ) - - # Prepare the JSON response data - response_data = { - 'ug_programmes': list(ug), - 'pg_programmes': list(pg), - 'phd_programmes': list(phd) - } - - # Return a JsonResponse - return JsonResponse(response_data, status=200, safe=False) - -@api_view(['GET']) -def admin_view_curriculums_of_a_programme(request, programme_id): - program = get_object_or_404(Programme, id=programme_id) - curriculums = program.curriculums.all() - - # Filtering working and past curriculums - working_curriculums = curriculums.filter(working_curriculum=1) - past_curriculums = curriculums.filter(working_curriculum=0) - - data = { - 'program': { - 'id': program.id, - 'name': program.name, - 'category': program.category, - 'beginyear': program.programme_begin_year, - }, - 'working_curriculums': CurriculumSerializer(working_curriculums, many=True).data, - 'past_curriculums': CurriculumSerializer(past_curriculums, many=True).data, - } - return JsonResponse(data) - -@api_view(['GET']) -def Admin_view_all_working_curriculums(request): - """API view to return all working curriculums offered by the institute as JSON""" - - user_details = ExtraInfo.objects.get(user=request.user) - des = HoldsDesignation.objects.filter(user=request.user).first() - - # Access control: redirect non-admin users - # if request.session['currentDesignationSelected'] in ["student", "Associate Professor", "Professor", "Assistant Professor"]: - # return JsonResponse({'error': 'Access denied'}, status=403) - # elif str(request.user) != "acadadmin" and 'hod' not in request.session['currentDesignationSelected'].lower(): - # return JsonResponse({'error': 'Access denied'}, status=403) - - # Fetch all working curriculums - curriculums = Curriculum.objects.filter(working_curriculum=1) - - # Filter based on GET parameters if any (curriculum filter) - curriculumfilter = CurriculumFilter(request.GET, queryset=curriculums) - curriculums = curriculumfilter.qs - - # Manually serialize the curriculums into a list of dictionaries - curriculum_data = [] - for curriculum in curriculums: - primary_batches = curriculum.batches.all() - - from django.db.models import Q - multi_curriculum_batches = Batch.objects.filter( - curriculum_options__isnull=False - ).exclude( - curriculum_options=[] - ) - - batches_with_this_curriculum = [] - for batch in primary_batches: - batches_with_this_curriculum.append(batch) - - for batch in multi_curriculum_batches: - if batch.curriculum_options: - for curr_option in batch.curriculum_options: - if curr_option.get('id') == curriculum.id: - batches_with_this_curriculum.append(batch) - break - - unique_batches = list(set(batches_with_this_curriculum)) - - curriculum_data.append({ - 'id':curriculum.id, - 'name': curriculum.name, - 'version': str(curriculum.version), # Convert Decimal to string for JSON compatibility - 'batch': [str(batch) for batch in unique_batches], # Include both primary and multi-curriculum batches - 'semesters': curriculum.no_of_semester, - }) - - # Return the data as JSON response - return JsonResponse({'curriculums': curriculum_data}, safe=False) - -def admin_view_semesters_of_a_curriculum(request, curriculum_id): - """API endpoint to get all semesters of a specific curriculum for React frontend.""" - - # Check user permissions - # if request.session['currentDesignationSelected'] in ["student", "Associate Professor", "Professor", "Assistant Professor"] or ('hod' in request.session['currentDesignationSelected'].lower() and str(request.user) != "acadadmin"): - # return JsonResponse({'error': 'Unauthorized access'}, status=403) - - curriculum = get_object_or_404(Curriculum, Q(id=curriculum_id)) - semesters = curriculum.semesters.all() - - # Prepare semesters data - semester_slots = [] - semester_credits = [] - - semester_data = [] - for semester in semesters: - # For each slot in the semester, retrieve associated courses - slots = [] - for slot in semester.courseslots.all(): - courses = list(slot.courses.values('id', 'name', 'code', 'credit','tutorial_hours','lecture_hours')) # Adjust fields as needed - slots.append({ - 'id': slot.id, - 'type': slot.type, - 'name': slot.name, - 'courses': courses - }) - - # Calculate total credits for the semester based on maximum credit of each course slot - credits_sum = sum(max(course['credit'] for course in slot['courses']) if slot['courses'] else 0 for slot in slots) - - semester_data.append({ - 'id':semester.id, - 'semester_no': semester.semester_no, - 'start_semester': semester.start_semester, - 'end_semester': semester.end_semester, - 'slots': slots, - 'credits': credits_sum - }) - all_batches = Batch.objects.filter(running_batch=True, curriculum=curriculum_id).order_by('year') - batch_data = [ - { - 'id': batch.id, - 'name': batch.name, - 'discipline': batch.discipline.acronym, # Use acronym or other attributes if available in Discipline - 'year': batch.year, - 'running_batch': batch.running_batch - } for batch in all_batches - ] - batches_without_curriculum = Batch.objects.filter(curriculum__isnull=True) - unlinked_batch_data=[ - { - 'id': batch.id, - 'name': batch.name, - 'discipline': batch.discipline.acronym, # Use acronym or other attributes if available in Discipline - 'year': batch.year, - 'running_batch': batch.running_batch - } for batch in batches_without_curriculum - ] - curriculum_data = { - 'curriculum_id': curriculum.id, - 'curriculum_name': curriculum.name, - 'version': curriculum.version, - 'programme_id': curriculum.programme.id, - 'batches':batch_data, - 'semesters': semester_data, - 'unlikedbatches':unlinked_batch_data, - 'working_curriculum':curriculum.working_curriculum - } - - return JsonResponse(curriculum_data) - -def admin_view_a_semester_of_a_curriculum(request, semester_id): - # user_details = ExtraInfo.objects.get(user=request.user) - # des = HoldsDesignation.objects.filter(user=request.user).first() - - # # Access Control - # if request.session['currentDesignationSelected'] in ["student", "Associate Professor", "Professor", "Assistant Professor"]: - # return JsonResponse({'error': 'Access denied'}, status=403) - # elif str(request.user) == "acadadmin" or 'hod' in request.session['currentDesignationSelected'].lower(): - # pass - # else: - # return JsonResponse({'error': 'Access denied'}, status=403) - - # Get semester and course slots - semester = get_object_or_404(Semester, id=semester_id) - course_slots = semester.courseslot_set.all() - - # Prepare JSON response - semester_data = { - 'id': semester.id, - 'semester_no':semester.semester_no, - 'curriculum':semester.curriculum.name, - 'curriculum_version':semester.curriculum.version, - 'instigate_semester':semester.instigate_semester, - 'start_semester':semester.start_semester, - 'end_semester':semester.end_semester, - 'semester_info':semester.semester_info, - 'course_slots': [ - { - 'id': slot.id, - 'name': slot.name, - 'type': slot.type, - 'course_slot_info': slot.course_slot_info, - 'duration': slot.duration, - 'min_registration_limit': slot.min_registration_limit, - 'max_registration_limit': slot.max_registration_limit, - 'courses': [ - { - 'id': course.id, - 'code': course.code, - 'name': course.name, - 'credit': course.credit - } for course in slot.courses.all() - ] - } - for slot in course_slots - ] - } - - return JsonResponse(semester_data, safe=False) - -def admin_view_a_courseslot(request, courseslot_id): - """API to view a course slot""" - - # # Check user designation and role - # user_details = ExtraInfo.objects.get(user=request.user) - # des = HoldsDesignation.objects.filter(user=request.user).first() - # current_designation = request.session.get('currentDesignationSelected', '').lower() - - # if current_designation in ["student", "associate professor", "professor", "assistant professor"]: - # return JsonResponse({'redirect': '/programme_curriculum/programmes/'}, status=302) - # elif str(request.user) == "acadadmin": - # pass - # elif 'hod' in current_designation: - # return JsonResponse({'redirect': '/programme_curriculum/programmes/'}, status=302) - - # Get course slot and check for edit mode - course_slot = get_object_or_404(CourseSlot, Q(id=courseslot_id)) - edit = request.POST.get('edit', -1) - - if edit == str(course_slot.id): - # Return edit information - return JsonResponse({ - 'course_slot': { - 'id': course_slot.id, - 'name': course_slot.name, - 'semester':course_slot.semester, - 'type':course_slot.type, - 'course_slot_info':course_slot.course_slot_info, - 'duration':course_slot.duration, - 'min_registration_limit':course_slot.min_registration_limit, - 'max_registration_limit':course_slot.max_registration_limit, - - # Add other fields as necessary - }, - 'edit': True - }) - - # Default response if not in edit mode - return JsonResponse({ - 'course_slot': { - 'id': course_slot.id, - 'name': course_slot.name, - 'type':course_slot.type, - 'course_slot_info':course_slot.course_slot_info, - 'duration':course_slot.duration, - 'min_registration_limit':course_slot.min_registration_limit, - 'max_registration_limit':course_slot.max_registration_limit, - "courses": [ - { - "id": course.id, - "code":course.code, - "name": course.name, - "credit": course.credit, - - } for course in course_slot.courses.all() - ], - "curriculum": { - "id": course_slot.semester.curriculum.id, - "name": course_slot.semester.curriculum.name, - "version": course_slot.semester.curriculum.version, - "semester_no":course_slot.semester.semester_no, - } - - }, - 'edit': False - }) - -@api_view(['GET']) -def admin_view_all_courses(request): - """Returns all courses with required fields as JSON data.""" - - # Ensure that only authorized users can access this data (based on your logic) - # if request.session['currentDesignationSelected'] in ["student", "Associate Professor", "Professor", "Assistant Professor"]: - # return JsonResponse({'error': 'Unauthorized access'}, status=403) - # elif str(request.user) == "acadadmin": - # pass - # elif 'hod' in request.session['currentDesignationSelected'].lower(): - # return JsonResponse({'error': 'Unauthorized access'}, status=403) - - # Get all courses - courses = Course.objects.all() - - # Apply filters if necessary - coursefilter = CourseFilter(request.GET, queryset=courses) - courses = coursefilter.qs - - # Prepare data for JSON response (only include relevant fields) - courses_data = [ - { - "id": course.id, - "code": course.code, - "name": course.name, - "version": str(course.version), - "credits": course.credit - } - for course in courses - ] - - return JsonResponse({'courses': courses_data}) - -@api_view(['GET']) -def admin_view_a_course(request, course_id): - """View to handle the details of a Course as an API""" - - # Get user details - user_details = ExtraInfo.objects.get(user=request.user) - des = HoldsDesignation.objects.filter(user=request.user).first() - - # Check if the user is authorized to view the course details - # if request.session['currentDesignationSelected'] in [ - # "student", - # "Associate Professor", - # "Professor", - # "Assistant Professor" - # ]: - # return JsonResponse({'error': 'Unauthorized access'}, status=403) - - # if str(request.user) == "acadadmin": - # pass # Acadadmin can view the course - # elif 'hod' in request.session['currentDesignationSelected'].lower(): - # return JsonResponse({'error': 'Unauthorized access'}, status=403) - - # Fetch the course based on the course_id - course = get_object_or_404(Course, Q(id=course_id)) - course_serializer = CourseSerializer(course) - - - - # Send the course data as JSON - return JsonResponse(course_serializer.data, safe=False) -# def admin_view_all_discplines(request): -# """ views the details of a Course """ - -# user_details = ExtraInfo.objects.get(user = request.user) -# des = HoldsDesignation.objects.all().filter(user = request.user).first() -# if request.session['currentDesignationSelected']== "student" or request.session['currentDesignationSelected']== "Associate Professor" or request.session['currentDesignationSelected']== "Professor" or request.session['currentDesignationSelected']== "Assistant Professor" : -# return HttpResponseRedirect('/programme_curriculum/programmes/') -# elif str(request.user) == "acadadmin" : -# pass -# elif 'hod' in request.session['currentDesignationSelected'].lower(): -# return HttpResponseRedirect('/programme_curriculum/programmes/') - -# disciplines = Discipline.objects.all() -# return render(request, 'programme_curriculum/acad_admin/admin_view_all_disciplines.html', {'disciplines': disciplines}) -@api_view(['GET']) -def admin_view_all_discplines(request): - """API to view all disciplines with related programmes""" - - user_details = ExtraInfo.objects.get(user=request.user) - des = HoldsDesignation.objects.filter(user=request.user).first() - - # Ensure only authorized users can access - # if request.session['currentDesignationSelected'] in ["student", "Associate Professor", "Professor", "Assistant Professor"]: - # return HttpResponseRedirect('/programme_curriculum/programmes/') - # elif str(request.user) == "acadadmin": - # pass - # elif 'hod' in request.session['currentDesignationSelected'].lower(): - # return HttpResponseRedirect('/programme_curriculum/programmes/') - - # Get all disciplines and related programmes - disciplines = Discipline.objects.all().prefetch_related('programmes') - - # Prepare the data to return in JSON format - data = [] - for discipline in disciplines: - programmes = discipline.programmes.all() # Get programmes related to the discipline - programme_list = [ - {'name': programme.name,'id':programme.id} for programme in programmes - ] # Assuming Programme has name and acronym attributes - - data.append({ - 'name': discipline.name, - 'acronym':discipline.acronym, - 'id': discipline.id, - 'programmes': programme_list - }) - - return JsonResponse({'disciplines': data}, safe=False) - - - -@api_view(['GET']) -def admin_view_all_batches(request): - """ views the details of a Course """ - - user_details = ExtraInfo.objects.get(user=request.user) - des = HoldsDesignation.objects.all().filter(user=request.user).first() - - # Check user role - # if request.session['currentDesignationSelected'] in ["student", "Associate Professor", "Professor", "Assistant Professor"]: - # return JsonResponse({'error': 'Access Denied'}, status=403) - # elif str(request.user) == "acadadmin": - # pass - # elif 'hod' in request.session['currentDesignationSelected'].lower(): - # return JsonResponse({'error': 'Access Denied'}, status=403) - - # Fetch batches - batches = Batch.objects.all().order_by('year') - batchfilter = BatchFilter(request.GET, queryset=batches) - batches = batchfilter.qs - - finished_batches = batches.filter(running_batch=False) - running_batches = batches.filter(running_batch=True) - - # Serialize the batch data with filled seats calculation - batch_data = [] - for batch in running_batches: - # Import the centralized function - from .views_student_management import calculate_batch_filled_seats - - # Use centralized filled seats calculation function - filled_seats = calculate_batch_filled_seats(batch) - available_seats = max(0, batch.total_seats - filled_seats) - - from .views_student_management import get_batch_curriculum_display - curriculum_display = get_batch_curriculum_display(batch) - - batch_data.append({ - 'batch_id': batch.id, - 'name': batch.name, - 'discipline': str(batch.discipline.acronym), - 'year': batch.year, - 'curriculum': curriculum_display if curriculum_display != "No curriculum assigned" else (batch.curriculum.name if batch.curriculum else None), - 'curriculum_display': curriculum_display, - 'id': batch.curriculum.id if batch.curriculum else None, - 'curriculumVersion': batch.curriculum.version if batch.curriculum else None, - 'running_batch': batch.running_batch, - 'totalSeats': batch.total_seats, - 'total_seats': batch.total_seats, - 'filledSeats': filled_seats, - 'filled_seats': filled_seats, - 'availableSeats': available_seats, - 'available_seats': available_seats - }) - - finished_batch_data = [] - for batch in finished_batches: - # Import the centralized function - from .views_student_management import calculate_batch_filled_seats - - # Use centralized filled seats calculation function - filled_seats = calculate_batch_filled_seats(batch) - available_seats = max(0, batch.total_seats - filled_seats) - - # Get curriculum display for multi-curriculum support - from .views_student_management import get_batch_curriculum_display - curriculum_display = get_batch_curriculum_display(batch) - - finished_batch_data.append({ - 'batch_id': batch.id, - 'name': batch.name, - 'discipline': str(batch.discipline.acronym), - 'year': batch.year, - 'curriculum': curriculum_display if curriculum_display != "No curriculum assigned" else (batch.curriculum.name if batch.curriculum else None), - 'curriculum_display': curriculum_display, - 'id': batch.curriculum.id if batch.curriculum else None, - 'curriculumVersion': batch.curriculum.version if batch.curriculum else None, - 'running_batch': batch.running_batch, - 'totalSeats': batch.total_seats, - 'total_seats': batch.total_seats, - 'filledSeats': filled_seats, - 'filled_seats': filled_seats, - 'availableSeats': available_seats, - 'available_seats': available_seats - }) - - return JsonResponse({ - 'batches': batch_data, - 'finished_batches': finished_batch_data, - 'filter': batchfilter.data - }) - - -# def add_discipline_form(request): - -# user_details = ExtraInfo.objects.get(user = request.user) -# des = HoldsDesignation.objects.all().filter(user = request.user).first() -# if request.session['currentDesignationSelected']== "student" or request.session['currentDesignationSelected']== "Associate Professor" or request.session['currentDesignationSelected']== "Professor" or request.session['currentDesignationSelected']== "Assistant Professor" : -# return HttpResponseRedirect('/programme_curriculum/programmes/') -# elif str(request.user) == "acadadmin" : -# pass -# elif 'hod' in request.session['currentDesignationSelected'].lower(): -# return HttpResponseRedirect('/programme_curriculum/programmes/') - -# form = DisciplineForm() -# submitbutton= request.POST.get('Submit') -# if submitbutton: -# if request.method == 'POST': -# form = DisciplineForm(request.POST) -# if form.is_valid(): -# form.save() -# messages.success(request, "Added Discipline successful") -# return HttpResponseRedirect('/programme_curriculum/admin_disciplines/') -# return render(request, 'programme_curriculum/acad_admin/add_discipline_form.html',{'form':form}) - -@csrf_exempt -@api_view(['POST']) -@permission_classes([IsAuthenticated]) -def add_discipline_form(request): - if request.method == 'POST': - try: - data = json.loads(request.body) # Parse JSON from the request - form = DisciplineForm(data) # Populate the form with the data - - if form.is_valid(): - form.save() - return JsonResponse({"message": "Discipline added successfully!"}, status=201) - else: - return JsonResponse({"errors": form.errors}, status=400) - - except Exception as e: - return JsonResponse({"error": str(e)}, status=500) - else: - return JsonResponse({"error": "Invalid HTTP method. Use POST."}, status=405) - -@csrf_exempt -@permission_classes([IsAuthenticated]) -def edit_discipline_form(request, discipline_id): - - # user_details = ExtraInfo.objects.get(user = request.user) - # des = HoldsDesignation.objects.all().filter(user = request.user).first() - # if request.session['currentDesignationSelected']== "student" or request.session['currentDesignationSelected']== "Associate Professor" or request.session['currentDesignationSelected']== "Professor" or request.session['currentDesignationSelected']== "Assistant Professor" : - # return HttpResponseRedirect('/programme_curriculum/programmes/') - # elif str(request.user) == "acadadmin" : - # pass - # elif 'hod' in request.session['currentDesignationSelected'].lower(): - # return HttpResponseRedirect('/programme_curriculum/programmes/') - - discipline = get_object_or_404(Discipline, Q(id=discipline_id)) - if request.method == 'GET': - # Return initial form data - form_data = { - 'id': discipline.id, - 'name': discipline.name, - 'acronym': discipline.acronym, - 'programmes': [programme.id for programme in discipline.programmes.all()], - } - return JsonResponse(form_data) - - elif request.method == 'POST': - # Handle form submission - try: - data = json.loads(request.body) - form = DisciplineForm(data, instance=discipline) - if form.is_valid(): - form.save() - return JsonResponse({ - "message": f"Updated {discipline.name} successfully." - }, status=201) - else: - # Return form errors - return JsonResponse({ - 'status': 'error', - 'message': form.errors, - }, status=400) - - except json.JSONDecodeError: - return JsonResponse({ - 'status': 'error', - 'message': 'Invalid JSON data.', - }, status=400) - - return JsonResponse({ - 'status': 'error', - 'message': 'Invalid request method.', - }, status=405) - - -# def add_programme_form(request): - -# user_details = ExtraInfo.objects.get(user = request.user) -# des = HoldsDesignation.objects.all().filter(user = request.user).first() -# if request.session['currentDesignationSelected']== "student" or request.session['currentDesignationSelected']== "Associate Professor" or request.session['currentDesignationSelected']== "Professor" or request.session['currentDesignationSelected']== "Assistant Professor" : -# return HttpResponseRedirect('/programme_curriculum/programmes/') -# elif str(request.user) == "acadadmin" : -# pass -# elif 'hod' in request.session['currentDesignationSelected'].lower(): -# return HttpResponseRedirect('/programme_curriculum/programmes/') - -# form = ProgrammeForm() -# submitbutton= request.POST.get('Submit') -# if submitbutton: -# if request.method == 'POST': -# form = ProgrammeForm(request.POST) - -# if form.is_valid(): -# form.save() -# programme = Programme.objects.last() -# messages.success(request, "Added successful") -# return HttpResponseRedirect('/programme_curriculum/admin_curriculums/' + str(programme.id) + '/') -# return render(request,'programme_curriculum/acad_admin/add_programme_form.html',{'form':form, 'submitbutton': submitbutton}) - -# @permission_classes([IsAuthenticated]) -# @api_view(['POST']) -@csrf_exempt -def add_programme_form(request): - if request.method == 'POST': - data = json.loads(request.body) - form_data={ - 'name': data.get('name'), - 'category': data.get('category'), - 'programme_begin_year': data.get('programme_begin_year'), - # 'discipline': data.get('discipline') - } - form = ProgrammeForm(form_data) - - if form.is_valid(): - form.save() - programme = Programme.objects.last() - return JsonResponse({ - "message": "Programme added successfully", - "programme_id": programme.id, - }) - else: - return JsonResponse({ - "error": "Invalid form data", - "details": form.errors, - }, status=400) - - return JsonResponse({"error": "Invalid request method"}, status=405) - - -@csrf_exempt -@permission_classes([IsAuthenticated]) -def edit_programme_form(request, programme_id): - - # user_details = ExtraInfo.objects.get(user = request.user) - # des = HoldsDesignation.objects.all().filter(user = request.user).first() - # if request.session['currentDesignationSelected']== "student" or request.session['currentDesignationSelected']== "Associate Professor" or request.session['currentDesignationSelected']== "Professor" or request.session['currentDesignationSelected']== "Assistant Professor" : - # return HttpResponseRedirect('/programme_curriculum/programmes/') - # elif str(request.user) == "acadadmin" : - # pass - # elif 'hod' in request.session['currentDesignationSelected'].lower(): - # return HttpResponseRedirect('/programme_curriculum/programmes/') - - programme = get_object_or_404(Programme, Q(id=programme_id)) - if request.method == 'GET': - # Return initial form data - form_data = { - 'id': programme.id, - 'name': programme.name, - 'category': programme.category, - 'programme_begin_year': programme.begin_year, - } - return JsonResponse(form_data) - - elif request.method == 'POST': - # Handle form submission - try: - data = json.loads(request.body) - form = ProgrammeForm(data, instance=programme) - - if form.is_valid(): - form.save() - return JsonResponse({ - "message": "Updated successfully." - }, status=201) - else: - # Return form errors - return JsonResponse({ - 'status': 'error', - 'message': form.errors, - }, status=401) - - except json.JSONDecodeError: - return JsonResponse({ - 'status': 'error', - 'message': 'Invalid JSON data.', - }, status=400) - - return JsonResponse({ - 'status': 'error', - 'message': 'Invalid request method.', - }, status=405) - - -@permission_classes([IsAuthenticated]) -@api_view(['POST']) -def add_curriculum_form(request): - """ - This function is used to add Curriculum and Semester into Curriculum and Semester table. - - @variables: - no_of_semester - Get number of Semesters from form. - NewSemester - For initializing a new semester. - """ - if request.method == 'POST': - try: - # Parse the incoming JSON data - data = json.loads(request.body) - - curriculum_name = data.get('curriculum_name') - programme_id = data.get('programme') - working_curriculum = data.get('working_curriculum', False) - version_no = data.get('version_no', 1.0) - - # Handle multiple possible field names for number of semesters - num_semesters = (data.get('num_semesters') or - data.get('no_of_semester') or - data.get('numberOfSemesters') or - data.get('semesters') or 1) - - # Handle multiple possible field names for credits - num_credits = (data.get('num_credits') or - data.get('min_credit') or - data.get('minCredits') or - data.get('credits') or 0) - - # Convert to int to ensure they are valid numbers - num_semesters = int(num_semesters) - num_credits = int(num_credits) - - # Validate that the programme exists - try: - programme = Programme.objects.get(id=programme_id) - except Programme.DoesNotExist: - return JsonResponse({'error': 'Invalid programme ID'}, status=400) - - # Mark the previous versions as non-latest for this curriculum name - Curriculum.objects.filter(name=curriculum_name, programme=programme).update(latest_version=False) - - # Create the new Curriculum instance - curriculum = Curriculum( - name=curriculum_name, - programme=programme, - working_curriculum=working_curriculum, - version=version_no, - no_of_semester=num_semesters, - min_credit=num_credits, - latest_version=True - ) - curriculum.save() - - # Add Semesters for the Curriculum - semesters = [ - Semester(curriculum=curriculum, semester_no=semester_no) - for semester_no in range(1, num_semesters + 1) - ] - Semester.objects.bulk_create(semesters) - - return JsonResponse({'message': 'Curriculum added successfully!'}, status=201) - except Exception as e: - return JsonResponse({'error': str(e)}, status=400) - return JsonResponse({'error': 'Invalid request method.'}, status=405) - - -@permission_classes([IsAuthenticated]) -@api_view(['GET', 'PUT']) -def edit_curriculum_form(request, curriculum_id): - """ - Handle getting and updating Curriculum and Semesters through an API endpoint. - """ - if request.method == 'GET': - # Return current curriculum data for editing - curriculum = get_object_or_404(Curriculum, id=curriculum_id) - - curriculum_data = { - 'id': curriculum.id, - 'curriculum_name': curriculum.name, - 'programme': curriculum.programme.id, - 'programme_name': curriculum.programme.name, - 'working_curriculum': curriculum.working_curriculum, - 'version_no': curriculum.version, - 'num_semesters': curriculum.no_of_semester, # Frontend expects num_semesters - 'num_credits': curriculum.min_credit, - } - - return JsonResponse({'status': 'success', 'curriculum': curriculum_data}) - - elif request.method == 'PUT': - try: - # Parse the incoming JSON data - data = json.loads(request.body) - - curriculum_name = data.get('curriculum_name') - programme_id = data.get('programme') - working_curriculum = data.get('working_curriculum', False) - version_no = data.get('version_no', 1.0) - - # Handle multiple possible field names for number of semesters - num_semesters = (data.get('num_semesters') or - data.get('no_of_semester') or - data.get('numberOfSemesters') or - data.get('semesters')) - - if num_semesters is None: - num_semesters = 1 - - # Convert to int to ensure it's a valid number - num_semesters = int(num_semesters) - - # Handle multiple possible field names for credits - num_credits = (data.get('num_credits') or - data.get('min_credit') or - data.get('minCredits') or - data.get('credits') or 0) - - # Fetch the existing curriculum - curriculum = get_object_or_404(Curriculum, id=curriculum_id) - - # Validate that the programme exists - try: - programme = Programme.objects.get(id=programme_id) - except Programme.DoesNotExist: - return JsonResponse({'error': 'Invalid programme ID'}, status=status.HTTP_400_BAD_REQUEST) - - # Update the curriculum fields - curriculum.name = curriculum_name - curriculum.programme = programme - curriculum.working_curriculum = working_curriculum - curriculum.version = version_no - curriculum.no_of_semester = num_semesters - curriculum.min_credit = num_credits - curriculum.save() - - # Handle semester updates - old_no_of_semesters = Semester.objects.filter(curriculum=curriculum).count() - new_no_of_semesters = num_semesters - - if old_no_of_semesters != new_no_of_semesters: - if old_no_of_semesters > new_no_of_semesters: - # Remove extra semesters - for semester_no in range(new_no_of_semesters + 1, old_no_of_semesters + 1): - try: - Semester.objects.filter(curriculum=curriculum, semester_no=semester_no).delete() - except Exception as e: - return JsonResponse({'error': f'Failed to remove old semester: {str(e)}'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) - elif old_no_of_semesters < new_no_of_semesters: - # Add new semesters - semesters = [ - Semester(curriculum=curriculum, semester_no=semester_no) - for semester_no in range(old_no_of_semesters + 1, new_no_of_semesters + 1) - ] - Semester.objects.bulk_create(semesters) - - return JsonResponse({'message': 'Curriculum updated successfully!'}, status=status.HTTP_200_OK) - except Exception as e: - return JsonResponse({'error': str(e)}, status=status.HTTP_400_BAD_REQUEST) - return JsonResponse({'error': 'Invalid request method.'}, status=status.HTTP_405_METHOD_NOT_ALLOWED) -@csrf_exempt -def add_course_form(request): - - # user_details = ExtraInfo.objects.get(user = request.user) - # des = HoldsDesignation.objects.all().filter(user = request.user).first() - # if request.session['currentDesignationSelected']== "student" or request.session['currentDesignationSelected']== "Associate Professor" or request.session['currentDesignationSelected']== "Professor" or request.session['currentDesignationSelected']== "Assistant Professor" : - # return HttpResponseRedirect('/programme_curriculum/programmes/') - # elif str(request.user) == "acadadmin" : - # pass - # elif 'hod' in request.session['currentDesignationSelected'].lower(): - # return HttpResponseRedirect('/programme_curriculum/programmes/') - - try: - if request.method == 'POST': - data = json.loads(request.body.decode('utf-8')) - - form = CourseForm(data) - - if form.is_valid(): - try: - new_course = form.save(commit=False) - new_course.save() - - # Handle many-to-many relationships if any - if 'disciplines' in data: - new_course.disciplines.set(data['disciplines']) - - if 'pre_requisit_courses' in data and data['pre_requisit_courses']: - new_course.pre_requisit_courses.set(data['pre_requisit_courses']) - - # Create initial audit log for course creation (only for authenticated users) - if request.user.is_authenticated and not request.user.is_anonymous: - initial_data = { - 'name': new_course.name, - 'code': new_course.code, - 'credit': new_course.credit, - 'version': new_course.version, - 'lecture_hours': new_course.lecture_hours, - 'tutorial_hours': new_course.tutorial_hours, - 'pratical_hours': new_course.pratical_hours, - 'discussion_hours': new_course.discussion_hours, - 'project_hours': new_course.project_hours, - 'pre_requisits': new_course.pre_requisits, - 'syllabus': new_course.syllabus, - 'ref_books': new_course.ref_books, - 'percent_quiz_1': new_course.percent_quiz_1, - 'percent_midsem': new_course.percent_midsem, - 'percent_quiz_2': new_course.percent_quiz_2, - 'percent_endsem': new_course.percent_endsem, - 'percent_project': new_course.percent_project, - 'percent_lab_evaluation': new_course.percent_lab_evaluation, - 'percent_course_attendance': new_course.percent_course_attendance, - 'max_seats': new_course.max_seats, - 'working_course': new_course.working_course, - } - - create_course_audit_log( - course=new_course, - user=request.user, - action='CREATE', - old_data=None, - new_data=initial_data, - version_bump_type='MAJOR', # New course creation is always major - old_version=None, - new_version=new_course.version, - admin_override=False, - reason="New course created" - ) - - return JsonResponse({'success': True, 'message': 'Course added successfully', 'course_id': new_course.id}, status=201) - - except Exception as save_error: - return JsonResponse({'success': False, 'message': f'Error saving course: {str(save_error)}'}, status=500) - else: - # Return detailed form validation errors - error_details = {} - for field, errors in form.errors.items(): - error_details[field] = list(errors) - - return JsonResponse({ - 'success': False, - 'message': 'Form validation failed', - 'errors': error_details - }, status=400) - - return JsonResponse({'error': 'Invalid request method. Use POST.'}, status=405) - - except Exception as e: - return JsonResponse({'error': str(e)}, status=500) - - -@csrf_exempt -@api_view(['GET', 'PUT']) -@permission_classes([IsAuthenticated]) -def update_course_form(request, course_id): - """ - Handle getting and updating Course through an API endpoint. - """ - try: - course = get_object_or_404(Course, id=course_id) - - if request.method == 'GET': - # Return course data for editing - data = { - 'name': course.name, - 'code': course.code, - 'credit': course.credit, - 'version': course.version, - 'lecture_hours': course.lecture_hours, - 'tutorial_hours': course.tutorial_hours, - 'pratical_hours': course.pratical_hours, - 'discussion_hours': course.discussion_hours, - 'project_hours': course.project_hours, - 'disciplines': [d.id for d in course.disciplines.all()], - 'pre_requisits': course.pre_requisits, - 'pre_requisit_courses': [c.id for c in course.pre_requisit_courses.all()], - 'syllabus': course.syllabus, - 'ref_books': course.ref_books, - 'percent_quiz_1': course.percent_quiz_1, - 'percent_midsem': course.percent_midsem, - 'percent_quiz_2': course.percent_quiz_2, - 'percent_endsem': course.percent_endsem, - 'percent_project': course.percent_project, - 'percent_lab_evaluation': course.percent_lab_evaluation, - 'percent_course_attendance': course.percent_course_attendance, - 'max_seats': course.max_seats, - 'working_course': course.working_course, - # Version history and audit info - 'version_history': list(Course.objects.filter(code=course.code).order_by('-version').values('version', 'id')), - 'recent_changes': list(CourseAuditLog.objects.filter(course=course).order_by('-timestamp')[:5].values( - 'timestamp', 'user__username', 'action', 'version_bump_type', 'old_version', 'new_version', 'reason' - )) - } - return Response(data, status=status.HTTP_200_OK) - - elif request.method == 'PUT': - # Handle course update with intelligent versioning - data = json.loads(request.body) - - # Check if admin wants to override version bump decision - admin_override = data.get('admin_override_version', False) - manual_version = data.get('version') if admin_override else None - - # Capture old course data for audit and comparison - old_course_data = { - 'name': course.name, - 'code': course.code, - 'credit': course.credit, - 'lecture_hours': course.lecture_hours, - 'tutorial_hours': course.tutorial_hours, - 'pratical_hours': course.pratical_hours, - 'discussion_hours': course.discussion_hours, - 'project_hours': course.project_hours, - 'pre_requisits': course.pre_requisits, - 'pre_requisit_courses': list(course.pre_requisit_courses.values_list('id', flat=True)), - 'syllabus': course.syllabus, - 'ref_books': course.ref_books, - 'percent_quiz_1': course.percent_quiz_1, - 'percent_midsem': course.percent_midsem, - 'percent_quiz_2': course.percent_quiz_2, - 'percent_endsem': course.percent_endsem, - 'percent_project': course.percent_project, - 'percent_lab_evaluation': course.percent_lab_evaluation, - 'percent_course_attendance': course.percent_course_attendance, - 'disciplines': list(course.disciplines.values_list('id', flat=True)), - 'max_seats': course.max_seats, - 'working_course': course.working_course, - } - old_version = course.version - - # Prepare new course data - new_course_data = {} - update_fields = [ - 'code', 'name', 'credit', 'lecture_hours', 'tutorial_hours', - 'pratical_hours', 'discussion_hours', 'project_hours', - 'pre_requisits', 'syllabus', 'percent_quiz_1', 'percent_midsem', - 'percent_quiz_2', 'percent_endsem', 'percent_project', - 'percent_lab_evaluation', 'percent_course_attendance', 'ref_books', - 'max_seats', 'working_course' - ] - - # Collect new values for comparison - for field in update_fields: - if field in data: - new_course_data[field] = data[field] - else: - new_course_data[field] = getattr(course, field) - - # Handle many-to-many fields - if 'disciplines' in data: - new_course_data['disciplines'] = data['disciplines'] - else: - new_course_data['disciplines'] = list(course.disciplines.values_list('id', flat=True)) - - if 'pre_requisit_courses' in data: - new_course_data['pre_requisit_courses'] = data['pre_requisit_courses'] - else: - new_course_data['pre_requisit_courses'] = list(course.pre_requisit_courses.values_list('id', flat=True)) - - # Determine if version bump is needed - bump_type, changed_academic_fields, reason = determine_version_bump_type(old_course_data, new_course_data) - - # Calculate new version - new_version = old_version - if admin_override and manual_version: - # Admin manually specified version - new_version = manual_version - bump_type = 'MAJOR' # Assume major if manually overridden - reason = f"Admin override: {reason}" - elif bump_type != 'NONE': - # Auto-calculate new version based on bump type - new_version = calculate_new_version(old_version, bump_type) - - # Validate new version if it changed - if float(new_version) != float(old_version): - previous_latest = Course.objects.filter(code=course.code).order_by('-version').first() - if float(new_version) <= float(previous_latest.version): - return Response( - {'error': f'Version must be greater than current version ({previous_latest.version})'}, - status=status.HTTP_400_BAD_REQUEST - ) - - # Update course fields - for field in update_fields: - if field in data: - setattr(course, field, data[field]) - - # Update version if needed - if float(new_version) != float(old_version): - # Mark previous version as not latest - Course.objects.filter(code=course.code, latest_version=True).update(latest_version=False) - course.version = new_version - course.latest_version = True - - course.save() - - # Update many-to-many relationships - if 'disciplines' in data: - discipline_ids = data['disciplines'] - disciplines = Discipline.objects.filter(id__in=discipline_ids) - course.disciplines.set(disciplines) - - if 'pre_requisit_courses' in data: - prereq_course_ids = data['pre_requisit_courses'] - prereq_courses = Course.objects.filter(id__in=prereq_course_ids) - course.pre_requisit_courses.set(prereq_courses) - - # Create audit log - create_course_audit_log( - course=course, - user=request.user, - action='UPDATE', - old_data=old_course_data, - new_data=new_course_data, - version_bump_type=bump_type, - old_version=old_version, - new_version=new_version, - admin_override=admin_override, - reason=reason - ) - - # Prepare response - response_data = { - 'message': 'Course updated successfully!', - 'course_id': course.id, - 'code': course.code, - 'name': course.name, - 'old_version': str(old_version), - 'new_version': str(new_version), - 'version_bump_type': bump_type, - 'changed_academic_fields': changed_academic_fields, - 'reason': reason - } - - return Response(response_data, status=status.HTTP_200_OK) - - except Exception as e: - return Response( - {'error': str(e)}, - status=status.HTTP_400_BAD_REQUEST - ) - - -@csrf_exempt -@api_view(['GET']) -@permission_classes([IsAuthenticated]) -def course_audit_logs(request, course_id): - """ - Get audit logs for a specific course - """ - try: - course = get_object_or_404(Course, id=course_id) - - # Get audit logs with pagination - page = int(request.GET.get('page', 1)) - page_size = int(request.GET.get('page_size', 20)) - offset = (page - 1) * page_size - - audit_logs = CourseAuditLog.objects.filter(course=course).order_by('-timestamp')[offset:offset + page_size] - total_count = CourseAuditLog.objects.filter(course=course).count() - - logs_data = [] - for log in audit_logs: - logs_data.append({ - 'id': log.id, - 'user': log.user.username, - 'timestamp': log.timestamp, - 'action': log.action, - 'version_bump_type': log.version_bump_type, - 'old_version': str(log.old_version) if log.old_version else None, - 'new_version': str(log.new_version) if log.new_version else None, - 'changed_fields': log.changed_fields, - 'admin_override': log.admin_override, - 'reason': log.reason, - 'old_values': log.old_values, - 'new_values': log.new_values - }) - - return Response({ - 'logs': logs_data, - 'total_count': total_count, - 'page': page, - 'page_size': page_size, - 'has_next': offset + page_size < total_count - }, status=status.HTTP_200_OK) - - except Exception as e: - return Response( - {'error': str(e)}, - status=status.HTTP_400_BAD_REQUEST - ) - - -@permission_classes([IsAuthenticated]) -@api_view(['POST']) -def add_courseslot_form(request): - try: - # Check permissions - # user_details = ExtraInfo.objects.get(user=request.user) - # des = HoldsDesignation.objects.filter(user=request.user).first() - - # if request.session['currentDesignationSelected'] not in ["acadadmin"]: - # return JsonResponse({ - # 'status': 'error', - # 'message': 'Permission denied' - # }, status=403) - - # Parse the JSON data - data = json.loads(request.body) - - # Create the course slot - course_slot = CourseSlot.objects.create( - semester_id=data['semester'], - name=data['name'], - type=data['type'], - course_slot_info=data.get('course_slot_info', ''), - duration=data.get('duration', 1), - min_registration_limit=data.get('min_registration_limit', 0), - max_registration_limit=data.get('max_registration_limit', 1000) - ) - - # Add courses if provided - if 'courses' in data and data['courses']: - course_slot.courses.set(data['courses']) - - return JsonResponse({ - 'status': 'success', - 'message': 'Course slot created successfully', - 'id': course_slot.id - }) - - except Exception as e: - return JsonResponse({ - 'status': 'error', - 'message': str(e) - }, status=400) - -@csrf_exempt # Use this decorator if you're not using CSRF tokens in your API calls -@permission_classes([IsAuthenticated]) -def edit_courseslot_form(request, courseslot_id): - - courseslot = get_object_or_404(CourseSlot, Q(id=courseslot_id)) - curriculum_id = courseslot.semester.curriculum.id - - if request.method == 'GET': - # Prepare the course slot data for the frontend - courseslot_data = { - 'id': courseslot.id, - 'semester': courseslot.semester.id, - 'name': courseslot.name, - 'type': courseslot.type, - 'course_slot_info': courseslot.course_slot_info, - 'courses': [course.id for course in courseslot.courses.all()], - 'duration': courseslot.duration, - 'min_registration_limit': courseslot.min_registration_limit, - 'max_registration_limit': courseslot.max_registration_limit, - 'curriculum_id': curriculum_id, - } - return JsonResponse({'status': 'success', 'courseslot': courseslot_data}) - - elif request.method == 'PUT': - # Handle updating the course slot - try: - data = json.loads(request.body) # Parse JSON data from the request body - form = CourseSlotForm(data, instance=courseslot) - if form.is_valid(): - form.save() - return JsonResponse({'status': 'success', 'message': 'Course slot updated successfully', 'redirect_url': f'/programme_curriculum/admin_curriculum_semesters/{curriculum_id}/'}) - else: - return JsonResponse({'status': 'error', 'errors': form.errors}, status=400) - except Exception as e: - return JsonResponse({'status': 'error', 'message': str(e)}, status=500) - - else: - return JsonResponse({'status': 'error', 'message': 'Invalid request method'}, status=405) - - - -@csrf_exempt -@api_view(['DELETE']) -@permission_classes([IsAuthenticated]) -def delete_courseslot(request, courseslot_id): - try: - # Check if the user has the required session key - # if 'currentDesignationSelected' not in request.session: - # return JsonResponse({'error': 'User role not defined in session'}, status=403) - - # Check user permissions - user_details = ExtraInfo.objects.get(user=request.user) - des = HoldsDesignation.objects.all().filter(user=request.user).first() - - # Restrict access based on user role - # current_designation = request.session['currentDesignationSelected'] - # if current_designation == "student" or \ - # current_designation == "Associate Professor" or \ - # current_designation == "Professor" or \ - # current_designation == "Assistant Professor": - # return JsonResponse({'error': 'Permission denied'}, status=403) - # elif str(request.user) == "acadadmin": - # pass # Allow access for academic admin - # elif 'hod' in current_designation.lower(): - # return JsonResponse({'error': 'Permission denied'}, status=403) - - # Fetch the course slot - courseslot = get_object_or_404(CourseSlot, id=courseslot_id) - - # Delete the course slot - courseslotname = courseslot.name - curriculum_id = courseslot.semester.curriculum.id - courseslot.delete() - - return JsonResponse({'message': f"Deleted {courseslotname} successfully"}, status=200) - except Exception as e: - # Return a generic error message without logging - return JsonResponse({'error': 'Internal server error'}, status=500) - - -@permission_classes([IsAuthenticated]) -@api_view(['POST']) -@csrf_exempt # Use this decorator if CSRF is not handled elsewhere -def add_batch_form(request): - """ - Handle adding a new Batch through an API endpoint. - """ - if request.method == 'POST': - data = request.data - - # Map frontend fields to the correct model fields - batch_data = { - "name": data.get("batch_name"), - "discipline": data.get("discipline"), - "year": data.get("batchYear"), - "curriculum": data.get("disciplineBatch"), # Assuming curriculum is disciplineBatch - "running_batch": data.get("runningBatch"), # Assuming running_batch is a field in the model - "total_seats": data.get("total_seats") or data.get("totalSeats") or data.get("Total_Seats") or 60, # Try multiple field name variations - } - - # data = json.loads(request.body) - serializer = BatchSerializer(data=batch_data) - if serializer.is_valid(): - serializer.save() - return Response({"message": "Added Batch successfully"}, status=status.HTTP_201_CREATED) - - return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) - return Response({"error": "Invalid request method"}, status=status.HTTP_405_METHOD_NOT_ALLOWED) - - -@csrf_exempt # Use this decorator if you're not using CSRF tokens in your API calls -@permission_classes([IsAuthenticated]) -def edit_batch_form(request, batch_id): - - # user_details = ExtraInfo.objects.get(user = request.user) - # des = HoldsDesignation.objects.all().filter(user = request.user).first() - # if request.session['currentDesignationSelected']== "student" or request.session['currentDesignationSelected']== "Associate Professor" or request.session['currentDesignationSelected']== "Professor" or request.session['currentDesignationSelected']== "Assistant Professor" : - # return HttpResponseRedirect('/programme_curriculum/programmes/') - # elif str(request.user) == "acadadmin" : - # pass - # elif 'hod' in request.session['currentDesignationSelected'].lower(): - # return HttpResponseRedirect('/programme_curriculum/programmes/') - - # Fetch the course slot - batch = get_object_or_404(Batch, Q(id=batch_id)) - if request.method == 'GET': - # Prepare the course slot data for the frontend - batch_data = { - 'id': batch.id, - 'discipline': batch.discipline_id, - 'name': batch.name, - 'year': batch.year, - 'curriculum_id': batch.curriculum_id, - 'curriculum_display': get_batch_curriculum_display(batch), - 'running_batch': batch.running_batch, - 'total_seats': batch.total_seats, # Add total_seats to GET response - 'available_curriculums': get_available_curriculums_for_batch(batch), - } - - curricula_data = None - - if batch.curriculum_id is not None: - # Fetch the curriculum object - curriculum = get_object_or_404(Curriculum, Q(id=batch.curriculum_id)) - - # Serialize the curriculum data - curricula_data = [{ - 'id': curriculum.id, - 'name': curriculum.name, - 'version': str(curriculum.version), - # Uncomment these if you want to include discipline and programme details - # 'discipline': curriculum.discipline.name, - # 'programme': curriculum.programme.name, - }] - - return JsonResponse({'status': 'success', 'batch': batch_data,'curriculum':curricula_data if curricula_data is not None else None}) - - elif request.method == 'PUT': - try: - # Fetch the existing batch instance - try: - batch = Batch.objects.get(id=batch_id) - except Batch.DoesNotExist: - return JsonResponse({'error': 'Batch not found'}, status=404) - - # Parse the incoming JSON data - data = json.loads(request.body) - - # Update batch fields - batch.name = data.get('batch_name', batch.name) # Use existing value if not provided - batch.year = data.get('batchYear', batch.year) # Use existing value if not provided - batch.running_batch = data.get('runningBatch', batch.running_batch) # Use existing value if not provided - - # Handle total_seats update - try multiple possible field names - new_total_seats = data.get('total_seats') or data.get('totalSeats') or data.get('Total_Seats') or batch.total_seats - batch.total_seats = new_total_seats - - # Update discipline (if provided) - discipline_id = data.get('discipline') - if discipline_id: - try: - discipline = Discipline.objects.get(id=discipline_id) - batch.discipline = discipline - except Discipline.DoesNotExist: - return JsonResponse({'error': 'Invalid discipline ID'}, status=400) - - curriculum_data = data.get('disciplineBatch') or data.get('curriculum') or data.get('curriculum_data') - if curriculum_data is not None: - # Normalize to list of ids if possible - curriculum_ids = [] - if isinstance(curriculum_data, list): - curriculum_ids = curriculum_data - elif isinstance(curriculum_data, str): - if ',' in curriculum_data: - curriculum_ids = [c.strip() for c in curriculum_data.split(',') if c.strip()] - elif curriculum_data.lower() in ['null', 'none', '']: - curriculum_ids = [] - else: - curriculum_ids = [curriculum_data] - else: - curriculum_ids = [curriculum_data] - - if len(curriculum_ids) > 1: - option_list = [] - for cid in curriculum_ids: - try: - c_obj = Curriculum.objects.get(id=int(cid)) - option_list.append({'id': c_obj.id, 'name': c_obj.name, 'version': str(c_obj.version)}) - except (Curriculum.DoesNotExist, ValueError): - return JsonResponse({'error': f'Invalid curriculum ID: {cid}'}, status=400) - batch.curriculum = None - try: - batch.curriculum_options = option_list - except Exception: - pass - elif len(curriculum_ids) == 1: - # Single curriculum selected - try: - c_obj = Curriculum.objects.get(id=int(curriculum_ids[0])) - batch.curriculum = c_obj - try: - batch.curriculum_options = None - except Exception: - pass - except (Curriculum.DoesNotExist, ValueError): - return JsonResponse({'error': 'Invalid curriculum ID'}, status=400) - else: - batch.curriculum = None - try: - batch.curriculum_options = None - except Exception: - pass - - # Save the updated batch - batch.save() - - return JsonResponse({'status': 'success', 'message': 'Batch updated successfully'}) - except Exception as e: - return JsonResponse({'status': 'error', 'message': str(e)}, status=500) - return JsonResponse({'error': 'Invalid request method'}, status=405) - -def instigate_semester(request, semester_id): - """ - This function is used to add the semester information. - - @variables: - no_of_semester - Get number of Semesters from form. - OldSemester - For Removing dropped Semester. - NewSemester - For initializing a new semester. - """ - user_details = ExtraInfo.objects.get(user = request.user) - des = HoldsDesignation.objects.all().filter(user = request.user).first() - if request.session['currentDesignationSelected']== "student" or request.session['currentDesignationSelected']== "Associate Professor" or request.session['currentDesignationSelected']== "Professor" or request.session['currentDesignationSelected']== "Assistant Professor" : - return HttpResponseRedirect('/programme_curriculum/programmes/') - elif str(request.user) == "acadadmin" : - pass - elif 'hod' in request.session['currentDesignationSelected'].lower(): - return HttpResponseRedirect('/programme_curriculum/programmes/') - - - semester = get_object_or_404(Semester, Q(id=semester_id)) - sdate = semester.start_semester - edate = semester.end_semester - isem = semester.instigate_semester - info = semester.semester_info - form = SemesterForm(initial={'start_semester': sdate ,'end_semester': edate ,'instigate_semester': isem , 'semester_info': info, }) - curriculum_id = semester.curriculum.id - submitbutton = request.POST.get('Submit') - if submitbutton: - if request.method == 'POST': - form = SemesterForm(request.POST or None) - if form.is_valid(): - semester.start_semester = form.cleaned_data['start_semester'] - semester.end_semester = form.cleaned_data['end_semester'] - semester.instigate_semester = form.cleaned_data['instigate_semester'] - semester.semester_info = form.cleaned_data['semester_info'] - semester.save() - messages.success(request, "Instigated "+ str(semester) +" successful") - return HttpResponseRedirect('/programme_curriculum/admin_curriculum_semesters/' + str(semester.curriculum.id) + '/') - - return render(request,'programme_curriculum/acad_admin/instigate_semester_form.html',{'semester':semester, 'form':form, 'submitbutton':submitbutton, 'curriculum_id':curriculum_id}) - -@csrf_exempt # Use this decorator if you're not using CSRF tokens in your API calls -@permission_classes([IsAuthenticated]) -def replicate_curriculum(request, curriculum_id): - """ - This function is used to replicate the previous curriculum into a new curriculum. - It accepts data from a React frontend via a POST request. - """ - if request.method != 'POST': - return JsonResponse({'status': 'error', 'message': 'Only POST requests are allowed'}, status=405) - - try: - # Parse JSON data from the request body - data = json.loads(request.body) - - # Extract data from the request - # curriculum_id = data.get('curriculum_id') - curriculum_name = data.get('curriculum_name') - programme_id = data.get('programme') - working_curriculum = data.get('working_curriculum') - version_no = data.get('version_no') - num_semesters = data.get('num_semesters') - num_credits = data.get('num_credits') - - # Fetch the old curriculum - old_curriculum = get_object_or_404(Curriculum, Q(id=curriculum_id)) - - # Create a new curriculum - # curriculum = Curriculum( - # name=curriculum_name, - # programme=programme, - # working_curriculum=working_curriculum, - # version=version_no, - # no_of_semester=num_semesters, - # min_credit=num_credits, - # latest_version=True - # ) - try: - programme = Programme.objects.get(id=programme_id) - except Programme.DoesNotExist: - return JsonResponse({'error': 'Invalid programme ID'}, status=400) - - - new_curriculum = Curriculum( - programme=programme, - name=curriculum_name, - version=version_no, - working_curriculum=working_curriculum, - no_of_semester=num_semesters, - min_credit=num_credits, - latest_version=True - ) - new_curriculum.save() - - # Replicate semesters and course slots - old_semesters = old_curriculum.semesters.all() - for semester_no in range(1, num_semesters + 1): - new_semester = Semester(curriculum=new_curriculum, semester_no=semester_no) - new_semester.save() - - for old_sem in old_semesters: - if old_sem.semester_no == semester_no: - for slot in old_sem.courseslots.all(): - courses = slot.courses.all() - slot.pk = None - slot.semester = new_semester - slot.save(force_insert=True) - slot.courses.set(courses) - - return JsonResponse({'status': 'success', 'message': 'Curriculum replicated successfully', 'curriculum_id': new_curriculum.id}) - - except Exception as e: - return JsonResponse({'status': 'error', 'message': str(e)}, status=400) -#new - -@permission_classes([IsAuthenticated]) -@csrf_exempt # Use this decorator if you're not using CSRF tokens in your API calls -def view_course_proposal_forms(request): - data = {} - username = request.GET.get('username') # Get username from query parameters - des = request.GET.get('des') # Get designation from query parameters - - if not username or not des: - return JsonResponse({'error': 'Username and designation are required'}, status=400) - - # Check the user's designation - if des not in ["Associate Professor", "Professor", "Assistant Professor"]: - if des == "student": - return JsonResponse({'redirect': '/programme_curriculum/programmes/'}, status=302) - elif des == "acadadmin": - return JsonResponse({'redirect': '/programme_curriculum/admin_programmes/'}, status=302) - else: - data['error'] = 'Files Cannot be sent with current Designation. Switch to "Professor or Assistant Professor or Associate Professor"' - return JsonResponse(data, status=403) - - # Fetch course proposals - courseProposal = NewProposalFile.objects.filter(uploader=username, designation=des, is_update=False) - updatecourseProposal = NewProposalFile.objects.filter(uploader=username, designation=des, is_update=True) - - # Serialize the querysets to JSON - courseProposal_data = json.loads(serialize('json', courseProposal)) - updatecourseProposal_data = json.loads(serialize('json', updatecourseProposal)) - - # Prepare the response data - response_data = { - 'courseProposals': courseProposal_data, - 'updateProposals': updatecourseProposal_data, - 'message': 'Data fetched successfully', - } - - return JsonResponse(response_data, status=200) - -@login_required(login_url='/accounts/login') -def faculty_view_all_courses(request): - """ views all the course slots of a specfic semester """ - - user_details = ExtraInfo.objects.get(user = request.user) - des = HoldsDesignation.objects.all().filter(user = request.user).first() - if request.session['currentDesignationSelected'] == "student" : - return HttpResponseRedirect('/programme_curriculum/programmes/') - elif request.session['currentDesignationSelected'] == "acadadmin" : - return HttpResponseRedirect('/programme_curriculum/admin_programmes') - elif request.session['currentDesignationSelected'] == "Associate Professor" or request.session['currentDesignationSelected'] == "Professor" or request.session['currentDesignationSelected'] == "Assistant Professor": - pass - elif 'hod' in request.session['currentDesignationSelected'].lower(): - pass - - courses = Course.objects.all() - notifs = request.user.notifications.all() - coursefilter = CourseFilter(request.GET, queryset=courses) - - courses = coursefilter.qs - - return render(request, 'programme_curriculum/faculty/faculty_view_all_courses.html', {'courses': courses, 'coursefilter': coursefilter,'notifications': notifs,}) - - -def faculty_view_a_course(request, course_id): - """ views the details of a Course """ - - user_details = ExtraInfo.objects.get(user = request.user) - des = HoldsDesignation.objects.all().filter(user = request.user).first() - if request.session['currentDesignationSelected'] == "student" : - return HttpResponseRedirect('/programme_curriculum/programmes/') - elif request.session['currentDesignationSelected'] == "acadadmin" : - return HttpResponseRedirect('/programme_curriculum/admin_programmes') - elif request.session['currentDesignationSelected'] == "Associate Professor" or request.session['currentDesignationSelected'] == "Professor" or request.session['currentDesignationSelected'] == "Assistant Professor" : - pass - elif 'hod' in request.session['currentDesignationSelected'].lower(): - pass - course = get_object_or_404(Course, Q(id=course_id)) - notifs = request.user.notifications.all() - return render(request, 'programme_curriculum/faculty/faculty_view_a_course.html', {'course': course,'notifications': notifs,}) - -@csrf_exempt -@permission_classes([IsAuthenticated]) -def view_a_course_proposal_form(request,CourseProposal_id): - user_details = ExtraInfo.objects.get(user = request.user) - des = HoldsDesignation.objects.all().filter(user = request.user).first() - - if request.session['currentDesignationSelected'] == "Associate Professor" or request.session['currentDesignationSelected'] == "Professor" or request.session['currentDesignationSelected'] == "Assistant Professor": - pass - elif 'hod' in request.session['currentDesignationSelected'].lower(): - pass - elif request.session['currentDesignationSelected'] == "acadadmin" : - return HttpResponseRedirect('/programme_curriculum/admin_programmes') - else: - return HttpResponseRedirect('/programme_curriculum/programmes') - - user_details = ExtraInfo.objects.get(user = request.user) - des = HoldsDesignation.objects.all().filter(user = request.user).first() - proposalform = get_object_or_404(NewProposalFile, Q(id=CourseProposal_id)) - notifs = request.user.notifications.all() - return render(request, 'programme_curriculum/faculty/view_a_course_proposal.html', {'proposal': proposalform,'notifications': notifs,}) - - -@permission_classes([IsAuthenticated]) -@csrf_exempt # Only for development, remove in production with proper CSRF handling -def new_course_proposal_file(request): - # des = HoldsDesignation.objects.all().filter(user = request.user).first() - # if request.session['currentDesignationSelected'] == "Associate Professor" or request.session['currentDesignationSelected'] == "Professor" or request.session['currentDesignationSelected'] == "Assistant Professor": - # pass - # elif request.session['currentDesignationSelected'] == "acadadmin" : - # return HttpResponseRedirect('/programme_curriculum/admin_programmes') - # else: - # return HttpResponseRedirect('/programme_curriculum/programmes') - - if request.method == 'POST': - try: - # Parse JSON data from request body - data = json.loads(request.body) - - # Create form data dictionary mapping frontend fields to model fields - form_data = { - 'name': data.get('name'), - 'code': data.get('code'), - 'credit': data.get('credit'), - 'version': data.get('version'), - 'lecture_hours': data.get('lecture_hours'), - 'tutorial_hours': data.get('tutorial_hours'), - 'pratical_hours': data.get('pratical_hours'), - 'project_hours': data.get('project_hours'), - 'discussion_hours': data.get('discussion_hours'), - 'syllabus': data.get('syllabus'), - 'percent_quiz_1': data.get('percent_quiz_1'), - 'percent_midsem': data.get('percent_midsem'), - 'percent_quiz_2': data.get('percent_quiz_2'), - 'percent_endsem': data.get('percent_endsem'), - 'percent_project': data.get('percent_project'), - 'percent_lab_evaluation': data.get('percent_lab_evaluation'), - 'percent_course_attendance': data.get('percent_course_attendance'), - 'ref_books': data.get('ref_books'), - 'disciplines': data.get('disciplines'), - # 'pre_requisit_courses': data.get('pre_requisit_courses'), - 'pre_requisits': data.get('pre_requisits'), - 'max_seats': data.get('maxSeats'), - 'subject': data.get('Title'), - 'description': data.get('Description'), - 'uploader': data.get('uploader'), - 'designation': data.get('Designation'), - 'is_update': data.get('isUpdate', False), - # 'designation': request.session.get('currentDesignationSelected', ''), - } - prerequisite_course_ids = data.get('pre_requisit_courses', []) - valid_courses = Course.objects.filter(id__in=prerequisite_course_ids) - if len(valid_courses) != len(prerequisite_course_ids): - invalid_ids = set(prerequisite_course_ids) - set(c.id for c in valid_courses) - return JsonResponse({ - 'status': 'error', - 'message': f'Invalid prerequisite course IDs: {invalid_ids}' - }, status=400) - # Create and validate form - form = NewCourseProposalFile(form_data) - - if form.is_valid(): - new_course = form.save(commit=False) - new_course.is_read = False - new_course.save() - - if prerequisite_course_ids: - new_course.pre_requisit_courses.add(*valid_courses) - - return JsonResponse({ - 'status': 'success', - 'message': 'Course added successfully', - 'course_id': new_course.id, - 'prerequisite_courses_added': len(prerequisite_course_ids) - }, status=201) - else: - return JsonResponse({ - 'status': 'error', - 'message': 'Invalid form data', - 'errors': form.errors - }, status=400) - - except json.JSONDecodeError: - return JsonResponse({ - 'status': 'error', - 'message': 'Invalid JSON data' - }, status=400) - except Exception as e: - return JsonResponse({ - 'status': 'error', - 'message': str(e) - }, status=500) - - return JsonResponse({ - 'status': 'error', - 'message': 'Only POST requests are allowed' - }, status=405) - -@permission_classes([IsAuthenticated]) -@csrf_exempt # Remove in production and use proper CSRF handling -def filetracking(request, proposal_id): - if request.method == 'POST': - try: - # Parse JSON data from request body - data = json.loads(request.body) - - # Get the file being tracked - file = get_object_or_404(NewProposalFile, id=proposal_id) - - # Get user objects from IDs - try: - receiver_id=data.get('receiverId') - receiver_des=data.get('receiverDesignation') - receiver_user = User.objects.get(username=data.get('receiverId')) - uploader_user=User.objects.get(username=data.get('uploader')) - - receiver_designation = Designation.objects.get(name=data.get('receiverDesignation')) - - discipline = Discipline.objects.get(id=data.get('discipline')) # Assuming single discipline - except (User.DoesNotExist, Designation.DoesNotExist, Discipline.DoesNotExist) as e: - return JsonResponse({ - 'status': 'error', - 'message': f'Invalid reference: {str(e)}' - }, status=400) - - # Create tracking record - tracking_data = { - 'file_id': proposal_id, - 'current_id': data.get('uploader'), - 'current_design': data.get('designation'), - 'receive_id': receiver_user.id, - 'receive_design': receiver_designation.id, - 'disciplines': discipline.id, - 'remarks': data.get('remarks', ''), - # 'is_submitted': True - } - form = CourseProposalTrackingFile(tracking_data) - - if form.is_valid(): - try: - tracking = form.save(commit=False) - tracking.save() - - # Prepare notification data - file_data = f"{file.name} {file.code}" - notification_data = ( - f"Received as {receiver_id} - {receiver_des} " - f"Course Proposal Form '{file_data}' " - f"By {data.get('uploader')} - {data.get('designation')}" - ) - prog_and_curr_notif(uploader_user,receiver_user,notification_data) - - return JsonResponse({ - 'status': 'success', - 'message': 'File tracking submitted successfully', - 'notification': notification_data - }, status=201) - - except IntegrityError: - return JsonResponse({ - 'status': 'error', - 'message': 'This file tracking record already exists' - }, status=400) - else: - return JsonResponse({ - 'status': 'error', - 'message': 'Invalid form data', - 'errors': form.errors - }, status=400) - - except json.JSONDecodeError: - return JsonResponse({ - 'status': 'error', - 'message': 'Invalid JSON data' - }, status=400) - except Exception as e: - return JsonResponse({ - 'status': 'error', - 'message': str(e) - }, status=500) - - return JsonResponse({ - 'status': 'error', - 'message': 'Only POST requests are allowed' - }, status=405) - - -@permission_classes([IsAuthenticated]) -@csrf_exempt -def inward_files(request): - - try: - - username = request.GET.get('username') - designation_name = request.GET.get('des') - if not username or not designation_name: - return JsonResponse({ - 'status': 'error', - 'message': 'Username and designation are required' - }, status=400) - - # Get user and designation objects - user = User.objects.get(username=username) - designation = Designation.objects.get(name=designation_name) - # Get inward files - course_proposals = Proposal_Tracking.objects.filter( - receive_design=designation.id, - receive_id=user.id - ).values( - 'id', - 'file_id', - 'current_id', - 'current_design', - 'receive_id__username', - 'receive_design__name', - 'remarks', - 'receive_date', - 'forward_date', - 'sender_archive', - 'receiver_archive', - 'disciplines', - 'is_rejected' - ) - - return JsonResponse({ - 'status': 'success', - 'courseProposals': list(course_proposals), - 'designation': designation_name - }) - - except User.DoesNotExist: - return JsonResponse({ - 'status': 'error', - 'message': 'User not found' - }, status=404) - except Designation.DoesNotExist: - return JsonResponse({ - 'status': 'error', - 'message': 'Designation not found' - }, status=404) - except Exception as e: - return JsonResponse({ - 'status': 'error', - 'message': str(e) - }, status=500) - -@permission_classes([IsAuthenticated]) -@csrf_exempt -def outward_files(request): - try: - # Get user details - # user_details = ExtraInfo.objects.get(user=request.user) - - # Get current designation - current_designation = request.GET.get('des', '') - current_username = request.GET.get('username', '') - - if not current_designation: - return JsonResponse({ - 'status': 'error', - 'message': 'Designation is required' - }, status=400) - - # Check permissions based on designation - if current_designation.lower() == "dean academic": - return JsonResponse({ - 'status': 'success', - 'message': f'As a "{current_designation}" you cannot have any outgoing files', - 'courseProposals': [] - }) - - try: - designation = Designation.objects.get(name=current_designation) - except Designation.DoesNotExist: - return JsonResponse({ - 'status': 'error', - 'message': 'Invalid designation' - }, status=400) - - # Get outward files - course_proposals = Proposal_Tracking.objects.filter( - current_design=current_designation, - current_id=current_username # Changed from des.user to username - ).values( - 'id', - 'file_id', - 'current_id', - 'current_design', - 'receive_id__username', - 'receive_design__name', - 'remarks', - 'receive_date', - 'forward_date', - 'sender_archive', - 'is_rejected' - ) - - return JsonResponse({ - 'status': 'success', - 'courseProposals': list(course_proposals), - 'designation': current_designation - }) - - except ExtraInfo.DoesNotExist: - return JsonResponse({ - 'status': 'error', - 'message': 'User details not found' - }, status=404) - except Exception as e: - return JsonResponse({ - 'status': 'error', - 'message': str(e) - }, status=500) - -@csrf_exempt -def update_course_proposal_file(request, course_id): - # Fetch user designation (will break if request.user is Anonymous) - # try: - # des = HoldsDesignation.objects.filter(user=request.user).first() - # except Exception: - # return JsonResponse({'error': 'Could not determine designation. Are you logged in?'}, status=400) - - # current_designation = request.session.get('currentDesignationSelected', None) - # if current_designation in ["Associate Professor", "Professor", "Assistant Professor"]: - # pass - # elif current_designation == "acadadmin": - # return HttpResponseRedirect('/programme_curriculum/admin_programmes') - # else: - # return HttpResponseRedirect('/programme_curriculum/programmes/') - - uploader = getattr(request.user, 'extrainfo', None) - # design = current_designation - course = get_object_or_404(Course, Q(id=course_id)) - file_data = course.code + ' - ' + course.name - - if request.method == "PUT": - try: - data = json.loads(request.body) - except json.JSONDecodeError: - return JsonResponse({"error": "Invalid JSON"}, status=400) - - form = NewCourseProposalFile(data, instance=course) - if form.is_valid(): - previous = Course.objects.filter(code=course.code).order_by('version').last() - course.version = previous.version - - new_course = form.save(commit=False) - new_course.is_read = False - new_course.is_update = True - - add = True - ver = 0 - - old_course = Course.objects.filter( - code=new_course.code, - name=new_course.name, - credit=new_course.credit, - lecture_hours=new_course.lecture_hours, - tutorial_hours=new_course.tutorial_hours, - pratical_hours=new_course.pratical_hours, - discussion_hours=new_course.discussion_hours, - project_hours=new_course.project_hours, - pre_requisits=new_course.pre_requisits, - syllabus=new_course.syllabus, - percent_quiz_1=new_course.percent_quiz_1, - percent_midsem=new_course.percent_midsem, - percent_quiz_2=new_course.percent_quiz_2, - percent_endsem=new_course.percent_endsem, - percent_project=new_course.percent_project, - percent_lab_evaluation=new_course.percent_lab_evaluation, - percent_course_attendance=new_course.percent_course_attendance, - ref_books=new_course.ref_books - ) - - if old_course.exists(): - for i in old_course: - if set(form.cleaned_data['pre_requisit_courses']) != set(i.pre_requisit_courses.all()): - add = True - else: - add = False - ver = i.version - break - - if add: - new_course.save() - new_course.pre_requisit_courses.set(form.cleaned_data['pre_requisit_courses']) - return JsonResponse({"message": "Course updated successfully", "course_id": new_course.id}, status=200) - else: - return JsonResponse({"error": f"A Course with the same values already exists at Version Number {ver}"}, status=400) - else: - return JsonResponse({"error": form.errors}, status=400) - - # For GET (rendering form in browser), fallback - form = NewCourseProposalFile(initial={'uploader': des.user, 'designation': design}, instance=course) - return render(request, 'programme_curriculum/faculty/course_proposal_form.html', { - 'form': form, - 'submitbutton': None, - 'file_info': file_data, - }) - - - -# def forward_course_forms(request,ProposalId): -# de= ProposalId -# des = HoldsDesignation.objects.all().filter(user = request.user).first() - -# courseform = Proposal_Tracking.objects.all().filter(id=ProposalId) - -# uploader = request.user.extrainfo -# design=request.session['currentDesignationSelected'] -# file = get_object_or_404(Proposal_Tracking, Q(id=ProposalId)) -# file_id = int(file.file_id) -# file2 = get_object_or_404(NewProposalFile, Q(id=file_id)) -# file_data=file2.code + ' ' + file2.name -# Proposal_D = file.id - -# if request.session['currentDesignationSelected'] == "Dean Academic" : -# file = get_object_or_404(Proposal_Tracking, Q(id=ProposalId)) -# file_id = int(file.file_id) -# file2 = get_object_or_404(NewProposalFile, Q(id=file_id)) -# course =Course.objects.all().filter(code=file2.code).last() -# version_error='' -# if(course): -# previous = Course.objects.all().filter(code=course.code).order_by('version').last() -# course.version=previous.version -# form = CourseForm(instance=file2,initial={'disciplines':file.disciplines}) -# submitbutton= request.POST.get('Submit') -# if submitbutton: -# if request.method == 'POST': -# form = CourseForm(request.POST) -# if form.is_valid() : - -# new_course = form.save(commit=False) -# if(new_course.version>previous.version): -# previous.latest_version=False -# previous.save() -# file.is_added=True -# file.is_submitted=True -# file.save() -# form.latest_version=True -# form.save() -# course = Course.objects.last() - -# receiver=file2.uploader -# receiver_id = User.objects.get(username=receiver) -# data=f'The Course " {file2.code} -{file2.name}" Updated Successfully' -# # data=file.subject -# prog_and_curr_notif(request.user,receiver_id,data) -# messages.success(request, "Updated "+ file2.code+'-'+file2.name +" successful") -# return HttpResponseRedirect("/programme_curriculum/course/" + str(course.id) + "/") -# else: -# version_error+=f'The version should be greater than {previous.version}' - -# return render(request,'programme_curriculum/faculty/dean_view_a_course_proposal.html',{'course':course, 'form':form, 'submitbutton': submitbutton,'version_error':version_error,'id':Proposal_D}) -# else: -# form = CourseForm(instance=file2,initial={'disciplines':file.disciplines}) -# # course1 =Course.objects.filter(code=file2.code,name=file2.name,credit=file2.credit,lecture_hours=file2.lecture_hours,tutorial_hours=file2.tutorial_hours,pratical_hours=file2.pratical_hours,discussion_hours=file2.discussion_hours,project_hours=file2.project_hours,pre_requisits=file2.pre_requisits,syllabus=file2.syllabus,percent_quiz_1=file2.percent_quiz_1,percent_midsem=file2.percent_midsem,percent_quiz_2=file2.percent_quiz_2,percent_project=file2.percent_project,percent_endsem=file2.percent_endsem,percent_lab_evaluation=file2.percent_lab_evaluation,percent_course_attendance=file2.percent_course_attendance,ref_books=file2.ref_books) -# submitbutton= request.POST.get('Submit') - -# if submitbutton: -# if request.method == 'POST': -# form = CourseForm(request.POST) - -# if form.is_valid(): -# file.is_added=True -# file.is_submitted=True -# file.save() -# form.save() -# receiver=file2.uploader -# receiver_id = User.objects.get(username=receiver) -# data='The Course "'+ file2.code+ ' - '+ file2.name + '" Added Successfully' -# # data=file.subject -# prog_and_curr_notif(request.user,receiver_id,data) -# course =Course.objects.all().filter(code=file2.code).last() -# messages.success(request, "Added "+ file2.name +" successful") -# return HttpResponseRedirect("/programme_curriculum/course/" + str(course.id) + "/") - - - -# return render(request, 'programme_curriculum/faculty/dean_view_a_course_proposal.html', {'course': file2 ,'form':form,'submitbutton': submitbutton,'id':Proposal_D}) - -# elif 'hod' in request.session['currentDesignationSelected'].lower(): - -# form=CourseProposalTrackingFile(initial={'current_id':des.user,'current_design':request.session['currentDesignationSelected'],'file_id':file.file_id,'disciplines':file.disciplines}) - -# # The above code is trying to retrieve the value of the 'Submit' key from the POST request -# # data. It is using the `get` method on the `request.POST` dictionary to access the value -# # associated with the 'Submit' key. -# submitbutton= request.POST.get('Submit') - -# if submitbutton: -# if request.method == 'POST': -# form = CourseProposalTrackingFile(request.POST) -# if form.is_valid(): -# try: -# file.is_submitted=True -# file.save() -# form.is_read=False -# form.save() - -# receiver=request.POST.get('receive_id') - -# uploader=request.POST.get('current_id') -# uploader_design=request.POST.get('current_design') - - -# receiver_design=request.POST.get('receive_design') -# receiver_des= Designation.objects.get(id=receiver_design) -# receiver_id = User.objects.get(id=receiver) -# messages.success(request, "Added successful") -# data='Received as '+ str(receiver_id) +'-'+str(receiver_des) +' Course Proposal of Course '+file_data +' By '+str(uploader)+' - '+str(uploader_design) - -# prog_and_curr_notif(request.user,receiver_id,data) -# return HttpResponseRedirect('/programme_curriculum/outward_files/') -# except IntegrityError as e: -# form.add_error(None, 'Proposal_ tracking with this File id, Current id, Current design and Disciplines already exists.') -# elif request.session['currentDesignationSelected'] == "acadadmin" : -# return HttpResponseRedirect('/programme_curriculum/admin_programmes') -# else: -# return HttpResponseRedirect('/programme_curriculum/programmes') - - - -# return render(request,'programme_curriculum/faculty/forward.html',{'form':form,'receive_date':file.receive_date,'proposal':file2,'submitbutton': submitbutton,'id':Proposal_D}) - -@csrf_exempt -@permission_classes([IsAuthenticated]) -def forward_course_forms(request, ProposalId): - try: - # Parse JSON data from request body - data = json.loads(request.body) - username = request.GET.get('username') - designation = request.GET.get('des') - if not username or not designation: - return JsonResponse({ - 'status': 'error', - 'message': 'Username and designation are required' - }, status=400) - - # Get the tracking record - file = get_object_or_404(Proposal_Tracking, id=ProposalId) - file_id = int(file.file_id) - file2 = get_object_or_404(NewProposalFile, id=file_id) - file_data = f"{file2.name} {file2.code}" - - - # Handle different designation cases - if designation == "Dean Academic": - course = Course.objects.filter(code=file2.code).last() - previous = None - version_error = None - - if course: - previous = Course.objects.filter(code=course.code).order_by('version').last() - if data.get('version') <= previous.version: - version_error = f'The version should be greater than {previous.version}' - return JsonResponse({ - 'status': 'error', - 'message': version_error - }, status=400) - - # Update course and tracking - course_data = { - 'code': data.get('code'), - 'name': data.get('name'), - 'version': data.get('version'), - 'credit': data.get('credit'), - 'lecture_hours': data.get('lecture_hours'), - 'tutorial_hours': data.get('tutorial_hours'), - 'pratical_hours': data.get('pratical_hours'), - 'discussion_hours': data.get('discussion_hours'), - 'project_hours': data.get('project_hours'), - 'pre_requisits': data.get('pre_requisits'), - 'pre_requisit_courses': data.get('pre_requisit_courses'), - 'syllabus': data.get('syllabus'), - 'percent_quiz_1': data.get('percent_quiz_1'), - 'percent_midsem': data.get('percent_midsem'), - 'percent_quiz_2': data.get('percent_quiz_2'), - 'percent_endsem': data.get('percent_endsem'), - 'percent_project': data.get('percent_project'), - 'percent_lab_evaluation': data.get('percent_lab_evaluation'), - 'percent_course_attendance': data.get('percent_course_attendance'), - 'ref_books': data.get('ref_books'), - 'working_course': data.get('working_course'), - # 'disciplines': data.get('disciplines'), - 'latest_version': True, - 'max_seats': data.get('maxSeats') - } - prerequisite_course_ids = data.get('pre_requisit_courses', []) - discipline_ids = data.get('disciplines',[]) - # if discipline_ids: - # valid_disciplines = Discipline.objects.filter(id__in=discipline_ids) - # if len(valid_disciplines) != len(discipline_ids): - # invalid_ids = set(discipline_ids) - set(d.id for d in valid_disciplines) - # return JsonResponse({ - # 'status': 'error', - # 'message': f'Invalid discipline IDs: {invalid_ids}' - # }, status=400) - - valid_courses = Course.objects.filter(id__in=prerequisite_course_ids) - if len(valid_courses) != len(prerequisite_course_ids): - invalid_ids = set(prerequisite_course_ids) - set(c.id for c in valid_courses) - return JsonResponse({ - 'status': 'error', - 'message': f'Invalid prerequisite course IDs: {invalid_ids}' - }, status=400) - - form = CourseForm(course_data) - - if form.is_valid(): - - new_course = form.save(commit=False) - # new_course.is_read = False - new_course.save() - file.is_added = True - file.is_submitted = True - file.save() - - receiver=file2.uploader - receiver_id = User.objects.get(username=receiver) - uploader_id= User.objects.get(username=username) - flag_updated = file2.is_update - notification_data = ( - f'The updated version of course "{file_data}" was added successfully' - if flag_updated - else f'The course "{file_data}" was added successfully' - ) - prog_and_curr_notif(uploader_id, receiver_id, notification_data) - - if prerequisite_course_ids: - new_course.pre_requisit_courses.add(*valid_courses) - if discipline_ids: - new_course.disciplines.add(*discipline_ids) - return JsonResponse({ - 'status': 'success', - 'message': 'Course added successfully', - 'course_id': new_course.id, - 'prerequisite_courses_added': len(prerequisite_course_ids) - }, status=201) - else: - return JsonResponse({ - 'status': 'error', - 'message': 'Invalid form data', - 'errors': form.errors - }, status=400) - - elif 'hod' in designation.lower(): - try: - receiver_user = User.objects.get(username=data.get('receiverId')) - uploader_user = User.objects.get(username=data.get('uploader')) - receiver_designation = Designation.objects.get(name=data.get('receiverDesignation')) - discipline = Discipline.objects.get(id=data.get('discipline')) # Assuming single discipline - except (User.DoesNotExist, Designation.DoesNotExist, Discipline.DoesNotExist) as e: - return JsonResponse({ - 'status': 'error', - 'message': f'Invalid reference: {str(e)}' - }, status=400) - - # Create tracking record - tracking_data = { - 'file_id': data.get('fileId'), - 'current_id': data.get('uploader'), - 'current_design': data.get('designation'), - 'receive_id': receiver_user.id, - 'receive_design': receiver_designation.id, - 'disciplines': discipline.id, - 'remarks': data.get('remarks', ''), - # 'is_submitted': True - } - # Validate form data - required_fields = ['receiverId', 'receiverDesignation'] - if not all(field in data for field in required_fields): - return JsonResponse({ - 'status': 'error', - 'message': 'Missing required fields' - }, status=400) - - # Update tracking - - form = CourseProposalTrackingFile(tracking_data) - - if form.is_valid(): - try: - tracking = form.save(commit=False) - tracking.save() - file.is_submitted = True - file.save() - - # Prepare notification data - # file_data = f"{file.name} {file.code}" - notification_data = ( - f"Received as {receiver_user} - {receiver_designation} " - f"Course Proposal Form '{file_data}' " - f"By {data.get('uploader')} - {data.get('designation')}" - ) - prog_and_curr_notif(uploader_user,receiver_user,notification_data) - - except IntegrityError: - return JsonResponse({ - 'status': 'error', - 'message': 'This file tracking record already exists' - }, status=400) - else: - return JsonResponse({ - 'status': 'error', - 'message': 'Invalid form data', - 'errors': form.errors - }, status=400) - - # Create notification - # notification_data = { - # 'sender': username, - # 'receiver': data['receiverId'], - # 'message': f'Received as {data["receiverId"]}-{data["receiverDesignation"]} Course Proposal of Course {file2.code} {file2.name} By {username} - {designation}', - # 'course_id': file2.id - # } - - return JsonResponse({ - 'status': 'success', - 'message': 'File tracking submitted successfully', - # 'notification': notification_data - }, status=201) - - - else: - return JsonResponse({ - 'status': 'error', - 'message': 'Unauthorized designation' - }, status=403) - - except json.JSONDecodeError: - return JsonResponse({ - 'status': 'error', - 'message': 'Invalid JSON data' - }, status=400) - except Exception as e: - return JsonResponse({ - 'status': 'error', - 'message': str(e) - }, status=500) - - -@csrf_exempt -@permission_classes([IsAuthenticated]) -def forward_course_forms_II(request): - try: - id=request.GET.get('id') - if not id: - return JsonResponse({ - 'status': 'error', - 'message': 'ProposalId is required' - }, status=400) - # Get the tracking record - file = get_object_or_404(Proposal_Tracking, id=id) - file_id = int(file.file_id) - file2 = get_object_or_404(NewProposalFile, id=file_id) - - response_data = { - 'proposal': { - 'id': file2.id, - 'name': file2.name, - 'code': file2.code, - 'credit': file2.credit, - 'uploader': file2.uploader, - 'designation': file2.designation, - 'lecture_hours': file2.lecture_hours, - 'tutorial_hours': file2.tutorial_hours, - 'pratical_hours': file2.pratical_hours, - 'project_hours': file2.project_hours, - 'discussion_hours': file2.discussion_hours, - 'syllabus': file2.syllabus, - 'percent_quiz_1': file2.percent_quiz_1, - 'percent_midsem': file2.percent_midsem, - 'percent_quiz_2': file2.percent_quiz_2, - 'percent_endsem': file2.percent_endsem, - 'percent_project': file2.percent_project, - 'percent_lab_evaluation': file2.percent_lab_evaluation, - 'percent_course_attendance': file2.percent_course_attendance, - 'ref_books': file2.ref_books, - 'pre_requisits': file2.pre_requisits, - 'max_seats': file2.max_seats, - 'pre_requisits_courses': list(file2.pre_requisit_courses.values('id', 'name','code','version')), - 'discipline':[file.disciplines.id], - }, - # Add other fields as needed - } - return JsonResponse({ - 'status': 'success', - 'data': response_data - }) - except Exception as e: - return JsonResponse({ - 'status': 'error', - 'message': str(e) - }, status=500) - - -@csrf_exempt -@permission_classes([IsAuthenticated]) -def view_inward_files(request,ProposalId): - try: - # Get parameters from frontend - # proposal_id = request.GET.get('proposalId') - username = request.GET.get('username') - designation = request.GET.get('des') - - if not all([ProposalId, username, designation]): - return JsonResponse({ - 'status': 'error', - 'message': 'proposalId, username and designation are required' - }, status=400) - - # Get the tracking record - file = get_object_or_404(Proposal_Tracking, id=ProposalId) - file_id = int(file.file_id) - proposal_file = get_object_or_404(NewProposalFile, id=file_id) - - # Get user details - # user = request.user - # user_details = ExtraInfo.objects.get(user=username) - # user_designation = HoldsDesignation.objects.filter(user=username).first() - - # Prepare response data - response_data = { - 'proposal': { - 'id': proposal_file.id, - 'name': proposal_file.name, - 'code': proposal_file.code, - 'credit': proposal_file.credit, - 'uploader': proposal_file.uploader, - 'designation': proposal_file.designation, - 'lecture_hours': proposal_file.lecture_hours, - 'tutorial_hours': proposal_file.tutorial_hours, - 'pratical_hours': proposal_file.pratical_hours, - 'project_hours': proposal_file.project_hours, - 'discussion_hours': proposal_file.discussion_hours, - 'syllabus': proposal_file.syllabus, - 'percent_quiz_1': proposal_file.percent_quiz_1, - 'percent_midsem': proposal_file.percent_midsem, - 'percent_quiz_2': proposal_file.percent_quiz_2, - 'percent_endsem': proposal_file.percent_endsem, - 'percent_project': proposal_file.percent_project, - 'percent_lab_evaluation': proposal_file.percent_lab_evaluation, - 'percent_course_attendance': proposal_file.percent_course_attendance, - 'ref_books': proposal_file.ref_books, - 'pre_requisits': proposal_file.pre_requisits, - 'max_seats': proposal_file.max_seats, - 'pre_requisit_courses': list(proposal_file.pre_requisit_courses.values('id', 'name','code','version')), - 'subject': proposal_file.subject, - 'description': proposal_file.description, - - }, - 'tracking': { - 'id': file.id, - 'file_id': file.file_id, - 'receive_date': file.receive_date, - 'forward_date': file.forward_date, - 'is_rejected': file.is_rejected, - 'is_added': file.is_added, - 'receive_id': file.receive_id.username, - 'receive_design': file.receive_design.name, - 'current_id': file.current_id, - 'current_design': file.current_design, - 'remarks': file.remarks, - 'disciplines_name': file.disciplines.name, - 'disciplines_acronym': file.disciplines.acronym, - }, - 'trackings': list(Proposal_Tracking.objects.filter( - file_id=file.file_id, - disciplines=file.disciplines - ).values( - 'id', - 'current_id', - 'current_design', - 'receive_date', - 'forward_date', - 'remarks', - 'is_rejected', - 'is_added' - )), - 'status_info': { - 'rejected_message': f'"{proposal_file.name}" Course Rejected by {file.receive_id} - {file.receive_design}' - if file.is_rejected else None, - 'added_message': f'"{proposal_file.code} - {proposal_file.name}" Course Added Successfully' - if (file.is_added and not file.is_rejected) else None - } - } - - return JsonResponse({ - 'status': 'success', - 'data': response_data - }) - - except Exception as e: - return JsonResponse({ - 'status': 'error', - 'message': str(e) - }, status=500) - -@csrf_exempt -@api_view(['POST']) -@permission_classes([IsAuthenticated]) -def reject_form(request, ProposalId): - try: - # Get query parameters - username = request.GET.get('username', '') - designation = request.GET.get('des', '') - - if not username or not designation: - return JsonResponse({ - 'status': 'error', - 'message': 'Missing username or designation parameters' - }, status=400) - - # Get the tracking and file objects - try: - track = Proposal_Tracking.objects.get(id=ProposalId) - file2 = NewProposalFile.objects.get(id=track.file_id) - except Proposal_Tracking.DoesNotExist: - return JsonResponse({ - 'status': 'error', - 'message': 'Proposal not found' - }, status=404) - except NewProposalFile.DoesNotExist: - return JsonResponse({ - 'status': 'error', - 'message': 'Proposal file not found' - }, status=404) - - if track.is_added: - return JsonResponse({ - 'status': 'error', - 'message': 'Course already forwarded or added, cannot be rejected' - }, status=400) - - # Update tracking - track.is_rejected = True - track.is_submitted = True - track.save() - - # Send notification - try: - receiver_user = User.objects.get(username=file2.uploader) - notification_message = ( - f'The Course "{file2.code} - {file2.name}" was Rejected by ' - f'{username} ({designation})' - ) - prog_and_curr_notif(request.user, receiver_user, notification_message) - except User.DoesNotExist: - # Continue even if notification fails - pass - - return JsonResponse({ - 'status': 'success', - 'message': 'Course Proposal Form Rejected', - 'data': { - 'proposal_id': ProposalId, - 'rejected_by': username, - 'designation': designation - } - }, status=200) - - except Exception as e: - return JsonResponse({ - 'status': 'error', - 'message': 'Server error: ' + str(e) - }, status=500) - - - -@csrf_exempt -@permission_classes([IsAuthenticated]) -def tracking_unarchive(request,ProposalId): - try: - # Parse JSON data from request body - # data = json.loads(request.body) - # proposal_id = request.GET.get('proposalId') - username = request.GET.get('username',' ') - designation = request.GET.get('des',' ') - - if not all([username, designation]): - return JsonResponse({ - 'status': 'error', - 'message': 'Missing required fields' - }, status=400) - - # Get the tracking record - track = get_object_or_404(Proposal_Tracking, id=ProposalId) - - # Check if current user is the sender or receiver - if (str(track.current_design) == designation and - str(track.current_id) == username): - # Current user is the sender - track.sender_archive = False - action = 'sender_archive' - else: - # Current user is the receiver - track.receiver_archive = False - action = 'receiver_archive' - - track.save() - - return JsonResponse({ - 'status': 'success', - 'message': 'File Unarchived successfully', - 'action': action, - 'proposal_id': ProposalId - }) - - except json.JSONDecodeError: - return JsonResponse({ - 'status': 'error', - 'message': 'Invalid JSON data' - }, status=400) - except Exception as e: - return JsonResponse({ - 'status': 'error', - 'message': str(e) - }, status=500) - -@csrf_exempt -@permission_classes([IsAuthenticated]) -def tracking_archive(request,ProposalId): - try: - # Parse JSON data from request body - # data = json.loads(request.body) - # proposal_id = request.GET.get('proposalId') - username = request.GET.get('username',' ') - designation = request.GET.get('des',' ') - - if not all([username, designation]): - return JsonResponse({ - 'status': 'error', - 'message': 'Missing required fields' - }, status=400) - - # Get the tracking record - track = get_object_or_404(Proposal_Tracking, id=ProposalId) - - # Check if current user is the sender or receiver - if (str(track.current_design) == designation and - str(track.current_id) == username): - # Current user is the sender - track.sender_archive = True - action = 'sender_archive' - else: - # Current user is the receiver - track.receiver_archive = True - action = 'receiver_archive' - - track.save() - - return JsonResponse({ - 'status': 'success', - 'message': 'File archived successfully', - 'action': action, - 'proposal_id': ProposalId - }) - - except json.JSONDecodeError: - return JsonResponse({ - 'status': 'error', - 'message': 'Invalid JSON data' - }, status=400) - except Exception as e: - return JsonResponse({ - 'status': 'error', - 'message': str(e) - }, status=500) - -@csrf_exempt # Use this decorator if you're not using CSRF tokens in your API calls -@permission_classes([IsAuthenticated]) -def file_archive(request,FileId): - try: - file = get_object_or_404(NewProposalFile, Q(id=FileId)) - file.is_archive = True - file.save() - return JsonResponse({ - 'status': 'success', - 'message': 'File archived successfully', - 'file_id': FileId - }) - except Exception as e: - return JsonResponse({ - 'status': 'error', - 'message': str(e) - }, status=400) - -@csrf_exempt # Use this decorator if you're not using CSRF tokens in your API calls -@permission_classes([IsAuthenticated]) -def file_unarchive(request,FileId): - - try: - file = get_object_or_404(NewProposalFile, Q(id=FileId)) - file.is_archive = False - file.save() - return JsonResponse({ - 'status': 'success', - 'message': 'File archived successfully', - 'file_id': FileId - }) - except Exception as e: - return JsonResponse({ - 'status': 'error', - 'message': str(e) - }, status=400) - -@csrf_exempt -@permission_classes([IsAuthenticated]) -def course_slot_type_choices(request): - """ - API endpoint to return the list of course slot type choices from the CourseSlot model. - """ - choices = [{'value': key, 'label': label} for key, label in CourseSlot._meta.get_field('type').choices] - return JsonResponse({'choices': choices}) - -@csrf_exempt -@permission_classes([IsAuthenticated]) -def semester_details(request): - curriculum_id = request.GET.get('curriculum_id') - - if not curriculum_id: - return JsonResponse({'error': 'Missing curriculum_id parameter'}, status=400) - - curriculum = get_object_or_404(Curriculum, id=curriculum_id) - - # Retrieve curriculum details - curriculum_name = curriculum.name - curriculum_version = curriculum.version # Ensure this field exists in the model - - # Get all semesters related to the given curriculum ID - semesters = Semester.objects.filter(curriculum_id=curriculum_id).order_by('semester_no') - semester_list = [ - { - "semester_id": semester.id, - "semester_number": semester.semester_no - } - for semester in semesters - ] - - data = { - "curriculum_name": curriculum_name, - "curriculum_version": curriculum_version, - "semesters": semester_list, - } - - return JsonResponse(data) - -@api_view(['GET']) -@csrf_exempt -@permission_classes([IsAuthenticated]) -def get_programme(request, programme_id): - program = get_object_or_404(Programme, id=programme_id) - # curriculums = program.curriculums.all() - - # Filtering working and past curriculums - # working_curriculums = curriculums.filter(working_curriculum=1) - # past_curriculums = curriculums.filter(working_curriculum=0) - - data = { - 'program': { - # 'id': program.id, - 'category': program.category, - 'name': program.name, - # 'beginyear': program.programme_begin_year, - }, - # 'working_curriculums': CurriculumSerializer(working_curriculums, many=True).data, - # 'past_curriculums': CurriculumSerializer(past_curriculums, many=True).data, - } - return JsonResponse(data) - -@csrf_exempt -@permission_classes([IsAuthenticated]) -def get_batch_names(request): - choices = [{'value': key, 'label': label} for key, label in Batch._meta.get_field('name').choices] - return JsonResponse({'choices': choices}) - -@csrf_exempt -@permission_classes([IsAuthenticated]) -def get_all_disciplines(request): - # Fetch all disciplines from the database - disciplines = Discipline.objects.all() - - # Serialize the disciplines into a list of dictionaries - disciplines_data = [ - { - 'id': discipline.id, - 'name': discipline.name, - 'acronym': discipline.acronym, - 'programmes': [programme.name for programme in discipline.programmes.all()] - } - for discipline in disciplines - ] - - # Return the serialized data as a JSON response - return JsonResponse(disciplines_data, safe=False) -@csrf_exempt -@permission_classes([IsAuthenticated]) -def get_unused_curriculam(request): - used_curriculum_ids = Batch.objects.exclude(curriculum__isnull=True).values_list('curriculum_id', flat=True) - unused_curricula = Curriculum.objects.exclude(id__in=used_curriculum_ids) - # ...existing code... - unused_curricula_data = [ - { - 'id': curriculum.id, - 'name': curriculum.name, - 'version': curriculum.version, - # 'discipline': curriculum.discipline.name, - # 'programme': curriculum.programme.name, - } - for curriculum in unused_curricula - ] - - # Return the serialized data as a JSON response - return JsonResponse(unused_curricula_data, safe=False) -@csrf_exempt -@permission_classes([IsAuthenticated]) -def admin_view_all_course_instructor(request): - # Fetch all records from the CourseInstructor table - course_instructors = CourseInstructor.objects.select_related( - 'course_id', 'instructor_id__id__user' # Traversing Faculty → ExtraInfo → User - ).annotate( - course_name=F('course_id__name'), - course_code = F('course_id__code'), - course_version=F('course_id__version'), - faculty_first_name=F('instructor_id__id__user__first_name'), - faculty_last_name=F('instructor_id__id__user__last_name') - ).values( - 'course_id', 'course_name', 'course_code', 'course_version', - 'instructor_id','semester_type', 'faculty_first_name', 'faculty_last_name', - 'year', 'id' - ) - for instructor in course_instructors: - obj = CourseInstructor( - year=instructor['year'], - semester_type=instructor['semester_type'] - ) - instructor['academic_year'] = obj.academic_year - - # Convert queryset to a list - course_instructors_data = list(course_instructors) - - # Convert queryset to a list - # course_instructors_data = list(course_instructors) - return JsonResponse({'course_instructors': course_instructors_data}) -@csrf_exempt -@permission_classes([IsAuthenticated]) -def admin_view_all_faculties(request): - # Fetch all faculties with their user details - faculties = Faculty.objects.select_related('id__user').annotate( - faculty_first_name=F('id__user__first_name'), - faculty_last_name=F('id__user__last_name') - ).values( - 'id', # Faculty ID from globals_faculty - 'faculty_first_name', # First name from auth.user - 'faculty_last_name' # Last name from auth.user - ) - - # Convert queryset to a list - faculties_data = list(faculties) - - return JsonResponse({'faculties': faculties_data}) - -def parse_academic_year(academic_year, semester_type): - """ - Parse academic_year string (e.g., "2024-25") and determine the working_year based on semester type. - For Odd Semester, working_year = first part (e.g., 2024). - For Even Semester, working_year = second part prefixed by '20' (e.g., 2025 if academic_year is "2024-25"). - The session is set to the academic_year string. - """ - parts = academic_year.split("-") - if len(parts) != 2: - raise ValueError("Invalid academic year format. Expected format like '2024-25'.") - first_year = parts[0].strip() - second_year = parts[1].strip() - if semester_type == "Odd Semester": - working_year = int(first_year) - elif semester_type == "Even Semester": - working_year = int("20" + second_year) - else: - # For any other semester type (e.g., Summer Semester) use the first year by default. - working_year = int("20"+second_year) - return working_year - -@csrf_exempt -@permission_classes([IsAuthenticated]) -def add_course_instructor(request): - if request.method != "POST": - return JsonResponse({"error": "Invalid request method"}, status=405) - - # 1) JSON branch (manual form) - if request.body and request.content_type == "application/json": - try: - data = json.loads(request.body) - # convert academic_year + semester_type → year - if "semester_type" not in data: - return JsonResponse( - {"error": "Must include semester_type"}, - status=400, - ) - - data["year"] = parse_academic_year( - data["academic_year"], data["semester_type"] - ) - data.pop("academic_year", None) - form = CourseInstructorForm(data) - if form.is_valid(): - form.save() - return JsonResponse( - {"success": "Instructor added successfully"}, status=201 - ) - return JsonResponse( - {"error": "Invalid form", "details": form.errors}, status=400 - ) - except ValueError as ve: - return JsonResponse({"error": str(ve)}, status=400) - except Exception as e: - return JsonResponse({"error": f"Unexpected error: {e}"}, status=500) - - # 2) Excel branch - if request.FILES.get("manual_instructor_xsl") and request.POST.get( - "excel_submit" - ): - f = request.FILES["manual_instructor_xsl"] - try: - book = xlrd.open_workbook(file_contents=f.read()) - sheet = book.sheet_by_index(0) - except Exception as e: - return JsonResponse({"error": f"Excel read error: {e}"}, status=400) - - errors = [] - rows = [] - with transaction.atomic(): - for i in range(1, sheet.nrows): - try: - code = str(sheet.cell(i, 0).value).strip() - version = float(sheet.cell(i, 1).value) - instr_id = str(sheet.cell(i, 2).value).strip() - acad_year = str(sheet.cell(i, 3).value).strip() - sem_type = str(sheet.cell(i, 4).value).strip() - - # convert year - year = parse_academic_year(acad_year, sem_type) - - # validate semester type choice - if sem_type not in dict(CourseInstructor.SEMESTER_TYPE_CHOICES): - raise ValueError(f"Bad semester_type '{sem_type}'") - - course = Course.objects.filter(code__iexact=code, version=version).first() - if not course: - raise ValueError(f"Course {code} v{version} not found") - - fac = Faculty.objects.filter(id=instr_id).first() - if not fac: - raise ValueError(f"Instructor {instr_id} not found") - - if CourseInstructor.objects.filter( - course_id=course, instructor_id=fac, year=year - ).exists(): - raise ValueError("Duplicate entry") - - rows.append((course, fac, year, sem_type)) - except Exception as e: - errors.append({"row": i + 1, "error": str(e)}) - - if errors: - return JsonResponse({"error": "Validation failed", "details": errors}, status=400) - - # bulk insert - for course, fac, year, sem_type in rows: - CourseInstructor.objects.create( - course_id=course, - instructor_id=fac, - year=year, - semester_type=sem_type - ) - - return JsonResponse({"success": f"{len(rows)} instructors added"}, status=201) - - return JsonResponse({"error": "Unrecognized request"}, status=400) - - - -@csrf_exempt -@permission_classes([IsAuthenticated]) -def update_course_instructor_form(request, instructor_id): - # Retrieve the CourseInstructor object or return 404 if not found - course_instructor = get_object_or_404(CourseInstructor, id=instructor_id) - - if request.method == 'POST': - # Parse the JSON data from the request body - try: - payload = json.loads(request.body) - form = CourseInstructorForm(payload, instance=course_instructor) - if form.is_valid(): - form.save() # Save the updated data to the database - return JsonResponse({'status': 'success', 'message': 'Course Instructor updated successfully!'}) - else: - # Return validation errors if the form is invalid - return JsonResponse({'status': 'error', 'errors': form.errors}, status=400) - except json.JSONDecodeError: - return JsonResponse({'status': 'error', 'message': 'Invalid JSON data'}, status=400) - - # Handle unsupported HTTP methods - return JsonResponse({'status': 'error', 'message': 'Method not allowed'}, status=405) - -@csrf_exempt # Use this decorator if you're not using CSRF tokens in your API calls -@permission_classes([IsAuthenticated]) -def get_superior_data(request): - try: - # Get parameters from request - username = request.GET.get('uploaderId') - designation = request.GET.get('uploaderDes', '').lower() - - if not username: - return JsonResponse({'error': 'Username is required'}, status=400) - - # Get the user object - user = User.objects.get(username=username) - - # Get user's extra info and department - extra_info = ExtraInfo.objects.get(user=user) - user_department = extra_info.department - - # Initialize response data - response_data = { - 'user': username, - 'department': user_department.name if user_department else None, - 'superior_data': None - } - - # Check if user is Professor/Associate Professor/Assistant Professor - professor_designations = ['professor', 'associate professor', 'assistant professor'] - is_professor = designation in professor_designations - - # Check if user is HOD (using regex to match HOD (DEPT) pattern) - is_hod = bool(re.match(r'^hod\s*\(.*\)$', designation, re.IGNORECASE)) - - if is_professor and user_department: - # Get HOD of the same department - hod_designation_name = f"HOD ({user_department.name})" - try: - hod_designation = Designation.objects.get(name__iexact=hod_designation_name) - hod = HoldsDesignation.objects.filter( - designation=hod_designation - ).select_related('working', 'working__extrainfo').first() - - if hod: - hod_user = hod.working - hod_extra_info = hod_user.extrainfo - response_data['superior_data'] = { - 'username': hod_user.username, - 'name': f"{hod_user.first_name} {hod_user.last_name}", - 'designation': hod_designation_name, - 'department': user_department.name, - 'department_id': user_department.id, - 'email': hod_user.email, - 'phone': hod_extra_info.phone_no - } - except Designation.DoesNotExist: - pass # HOD designation for this department doesn't exist - - elif is_hod: - # Get Dean Academic - try: - dean_designation = Designation.objects.get(name__iexact='Dean Academic') - dean = HoldsDesignation.objects.filter( - designation=dean_designation - ).select_related('working', 'working__extrainfo').first() - - if dean: - dean_user = dean.working - dean_extra_info = dean_user.extrainfo - response_data['superior_data'] = { - 'username': dean_user.username, - 'name': f"{dean_user.first_name} {dean_user.last_name}", - 'designation': 'Dean Academic', - 'department': dean_extra_info.department.name if dean_extra_info.department else None, - 'department_id': dean_extra_info.department.id if dean_extra_info.department else None, - 'email': dean_user.email, - 'phone': dean_extra_info.phone_no - } - except Designation.DoesNotExist: - pass # Dean Academic designation doesn't exist - - return JsonResponse(response_data) - - except User.DoesNotExist: - return JsonResponse({'error': 'User not found'}, status=404) - except ExtraInfo.DoesNotExist: - return JsonResponse({'error': 'User extra info not found'}, status=404) - except Exception as e: - return JsonResponse({'error': str(e)}, status=500) - - -# ============================================================================= -# COURSE INSTRUCTOR DELETE ENDPOINT -# ============================================================================= - -@csrf_exempt -@api_view(['DELETE']) -@authentication_classes([TokenAuthentication]) -@permission_classes([IsAuthenticated]) -def admin_delete_course_instructor(request, instructor_id): - """ - Delete a course instructor assignment - Only accessible by authenticated users (frontend handles role check) - """ - try: - # For API endpoints, we rely on token authentication - # The frontend handles role-based access control - - # Find the course instructor record - try: - course_instructor = CourseInstructor.objects.select_related( - 'course_id', 'instructor_id__id__user' - ).get(id=instructor_id) - except CourseInstructor.DoesNotExist: - return JsonResponse({ - 'success': False, - 'message': 'Course instructor not found.' - }, status=404) - - # Store details for response - try: - course_name = course_instructor.course_id.name - course_code = course_instructor.course_id.code - instructor_name = f"{course_instructor.instructor_id.id.user.first_name} {course_instructor.instructor_id.id.user.last_name}" - academic_year = course_instructor.academic_year - semester_type = course_instructor.semester_type - except Exception as detail_error: - return JsonResponse({ - 'success': False, - 'message': f'Error accessing course instructor details: {str(detail_error)}' - }, status=500) - - # Delete the course instructor assignment - try: - course_instructor.delete() - except Exception as delete_error: - return JsonResponse({ - 'success': False, - 'message': f'Error deleting course instructor: {str(delete_error)}' - }, status=500) - - return JsonResponse({ - 'success': True, - 'message': f'Course instructor assignment deleted successfully.', - 'details': { - 'course_name': course_name, - 'course_code': course_code, - 'instructor_name': instructor_name, - 'academic_year': academic_year, - 'semester_type': semester_type, - 'deleted_id': instructor_id - } - }, status=200) - - except Exception as e: - import traceback - traceback.print_exc() - return JsonResponse({ - 'success': False, - 'message': f'An error occurred while deleting course instructor: {str(e)}' - }, status=500) - - -# ============================================================================= -# ADDITIONAL DELETE ENDPOINTS -# ============================================================================= - -@csrf_exempt -@api_view(['DELETE']) -@authentication_classes([TokenAuthentication]) -@permission_classes([IsAuthenticated]) -def admin_delete_course(request, course_id): - """ - Delete a course - Only accessible by authenticated users (frontend handles role check) - """ - try: - # Find the course record - try: - course = Course.objects.get(id=course_id) - except Course.DoesNotExist: - return JsonResponse({ - 'success': False, - 'message': 'Course not found.' - }, status=404) - - # Store details for response - course_name = course.name - course_code = course.code - - # Check for dependencies before deletion - try: - # Check if course has active instructors - instructor_count = CourseInstructor.objects.filter(course_id=course).count() - if instructor_count > 0: - return JsonResponse({ - 'success': False, - 'message': f'Cannot delete course. It has {instructor_count} active instructor assignment(s). Please remove instructor assignments first.' - }, status=400) - - # Check if course is part of any curriculum (through course slots) - # Note: Courses are linked to curriculum through CourseSlot -> Semester -> Curriculum - slot_count = CourseSlot.objects.filter(courses=course).count() - if slot_count > 0: - return JsonResponse({ - 'success': False, - 'message': f'Cannot delete course. It is assigned to {slot_count} course slot(s) in curriculum(s). Please remove from course slots first.' - }, status=400) - - except Exception as dependency_error: - return JsonResponse({ - 'success': False, - 'message': f'Error checking course dependencies: {str(dependency_error)}' - }, status=500) - - # Delete the course - try: - course.delete() - except Exception as delete_error: - return JsonResponse({ - 'success': False, - 'message': f'Error deleting course: {str(delete_error)}' - }, status=500) - - return JsonResponse({ - 'success': True, - 'message': f'Course "{course_name} ({course_code})" deleted successfully.', - 'details': { - 'course_name': course_name, - 'course_code': course_code, - 'deleted_id': course_id - } - }, status=200) - - except Exception as e: - import traceback - traceback.print_exc() - return JsonResponse({ - 'success': False, - 'message': f'An error occurred while deleting course: {str(e)}' - }, status=500) - - -@csrf_exempt -@api_view(['DELETE']) -@authentication_classes([TokenAuthentication]) -@permission_classes([IsAuthenticated]) -def admin_delete_programme(request, programme_id): - """ - Delete a programme - Only accessible by authenticated users (frontend handles role check) - """ - try: - # Find the programme record - try: - programme = Programme.objects.get(id=programme_id) - except Programme.DoesNotExist: - return JsonResponse({ - 'success': False, - 'message': 'Programme not found.' - }, status=404) - - # Store details for response - programme_name = programme.name - programme_category = programme.category - programme_begin_year = programme.programme_begin_year - - # Check for dependencies before deletion - try: - # Primary check: if programme has curriculums (direct relationship) - curriculum_count = Curriculum.objects.filter(programme=programme).count() - if curriculum_count > 0: - return JsonResponse({ - 'success': False, - 'message': f'Cannot delete programme. It has {curriculum_count} curriculum(s). Please remove curriculums first.' - }, status=400) - - except Exception as dependency_error: - return JsonResponse({ - 'success': False, - 'message': f'Error checking programme dependencies: {str(dependency_error)}' - }, status=500) - - # Delete the programme - try: - programme.delete() - except Exception as delete_error: - return JsonResponse({ - 'success': False, - 'message': f'Error deleting programme: {str(delete_error)}' - }, status=500) - - return JsonResponse({ - 'success': True, - 'message': f'Programme "{programme_name} ({programme_category})" deleted successfully.', - 'details': { - 'programme_name': programme_name, - 'programme_category': programme_category, - 'programme_begin_year': programme_begin_year, - 'deleted_id': programme_id - } - }, status=200) - - except Exception as e: - import traceback - traceback.print_exc() - return JsonResponse({ - 'success': False, - 'message': f'An error occurred while deleting programme: {str(e)}' - }, status=500) - - -@csrf_exempt -@api_view(['DELETE']) -@authentication_classes([TokenAuthentication]) -@permission_classes([IsAuthenticated]) -def admin_delete_curriculum(request, curriculum_id): - """ - Delete a curriculum - Only accessible by authenticated users (frontend handles role check) - """ - try: - # Find the curriculum record - try: - curriculum = Curriculum.objects.select_related('programme').get(id=curriculum_id) - except Curriculum.DoesNotExist: - return JsonResponse({ - 'success': False, - 'message': 'Curriculum not found.' - }, status=404) - - # Store details for response - curriculum_name = curriculum.name - curriculum_version = curriculum.version - programme_name = curriculum.programme.name if curriculum.programme else None - - # Check for dependencies before deletion - try: - # SIMPLE RULE: Only check if curriculum has active batches - # If no active batches are using this curriculum, it can be deleted regardless of course content - from applications.programme_curriculum.models import Batch - batch_count = Batch.objects.filter(curriculum=curriculum, running_batch=True).count() - - if batch_count > 0: - return JsonResponse({ - 'success': False, - 'message': f'Cannot delete curriculum. It is assigned to {batch_count} active batch(es). Please unassign from batches first.' - }, status=400) - - except Exception as dependency_error: - import traceback - traceback.print_exc() - return JsonResponse({ - 'success': False, - 'message': f'Error checking curriculum dependencies: {str(dependency_error)}' - }, status=500) - - # Delete the curriculum - try: - curriculum.delete() - except Exception as delete_error: - import traceback - traceback.print_exc() - return JsonResponse({ - 'success': False, - 'message': f'Error deleting curriculum: {str(delete_error)}' - }, status=500) - - return JsonResponse({ - 'success': True, - 'message': f'Curriculum "{curriculum_name} (v{curriculum_version})" deleted successfully.', - 'details': { - 'curriculum_name': curriculum_name, - 'curriculum_version': curriculum_version, - 'programme_name': programme_name, - 'deleted_id': curriculum_id - } - }, status=200) - - except Exception as e: - import traceback - traceback.print_exc() - return JsonResponse({ - 'success': False, - 'message': f'An error occurred while deleting curriculum: {str(e)}' - }, status=500) - - -@csrf_exempt -@api_view(['DELETE']) -@authentication_classes([TokenAuthentication]) -@permission_classes([IsAuthenticated]) -def admin_delete_discipline(request, discipline_id): - """ - Delete a discipline - Only accessible by authenticated users (frontend handles role check) - Simple rule: Only check if discipline has any programmes/batches using it - """ - try: - # Find the discipline record - try: - discipline = Discipline.objects.get(id=discipline_id) - except Discipline.DoesNotExist: - return JsonResponse({ - 'success': False, - 'message': 'Discipline not found.' - }, status=404) - - # Store details for response - discipline_name = discipline.name - discipline_acronym = discipline.acronym - - # Check for dependencies before deletion - try: - # Check if discipline has any batches using it - from applications.programme_curriculum.models import Batch - batch_count = Batch.objects.filter(discipline=discipline, running_batch=True).count() - - if batch_count > 0: - return JsonResponse({ - 'success': False, - 'message': f'Cannot delete discipline. It is used by {batch_count} active batch(es). Please remove batches first.' - }, status=400) - - except Exception as dependency_error: - import traceback - traceback.print_exc() - return JsonResponse({ - 'success': False, - 'message': f'Error checking discipline dependencies: {str(dependency_error)}' - }, status=500) - - # Delete the discipline - try: - discipline.delete() - except Exception as delete_error: - import traceback - traceback.print_exc() - return JsonResponse({ - 'success': False, - 'message': f'Error deleting discipline: {str(delete_error)}' - }, status=500) - - return JsonResponse({ - 'success': True, - 'message': f'Discipline "{discipline_name} ({discipline_acronym})" deleted successfully.', - 'details': { - 'discipline_name': discipline_name, - 'discipline_acronym': discipline_acronym, - 'deleted_id': discipline_id - } - }, status=200) - - except Exception as e: - import traceback - traceback.print_exc() - return JsonResponse({ - 'success': False, - 'message': f'An error occurred while deleting discipline: {str(e)}' - }, status=500) - - -@csrf_exempt -@require_http_methods(["DELETE", "POST"]) -def delete_batch(request, batch_id): - """ - Delete batch with safety checks - URL: /programme_curriculum/api/batches//delete/ - Methods: DELETE or POST (for frontend compatibility) - """ - try: - from applications.programme_curriculum.models import Batch - from .views_student_management import StudentBatchUpload - - # Get the batch to delete - try: - batch = Batch.objects.get(id=batch_id) - except Batch.DoesNotExist: - return JsonResponse({ - 'success': False, - 'message': f'Batch with ID {batch_id} not found' - }, status=404) - - # 🔒 STUDENT VALIDATION: Check if batch has any students - try: - # Step 1: Check StudentBatchUpload table - FIRST filter by year, THEN by discipline - uploaded_students_this_year = StudentBatchUpload.objects.filter( - year=batch.year # FIRST: Only students from this academic year (e.g., 2025) - ).filter( - branch__icontains=batch.discipline.name # THEN: Only this discipline within that year - ).count() - - # Step 2: Check academic_information.Student table - # Students directly assigned to THIS specific batch record - try: - from applications.academic_information.models import Student - academic_students_this_batch = Student.objects.filter( - batch_id=batch # Direct foreign key to THIS specific batch - ).count() - except ImportError: - academic_students_this_batch = 0 - - # Total = students in this year's uploads + students assigned to this specific batch - total_students = uploaded_students_this_year + academic_students_this_batch - - if total_students > 0: - return JsonResponse({ - 'success': False, - 'message': f'Cannot delete batch "{batch.name} {batch.discipline.acronym} {batch.year}". It contains {total_students} students. Please transfer or remove students first.', - 'student_count': total_students, - 'validation_error': 'batch_has_students', - 'batch_info': { - 'id': batch.id, - 'name': batch.name, - 'discipline': batch.discipline.name, - 'year': batch.year - } - }, status=400) - - except Exception as e: - # Proceed with caution - assume no students if check fails - pass - - # Store batch info for response before deletion - batch_info = { - 'id': batch.id, - 'name': batch.name, - 'discipline': batch.discipline.name, - 'discipline_acronym': batch.discipline.acronym, - 'year': batch.year, - 'total_seats': batch.total_seats, - 'curriculum': batch.curriculum.name if batch.curriculum else None, - 'running_batch': batch.running_batch - } - - # Delete the batch - batch.delete() - - return JsonResponse({ - 'success': True, - 'message': f'Successfully deleted batch "{batch_info["name"]} {batch_info["discipline_acronym"]} {batch_info["year"]}".', - 'deleted_batch': batch_info - }) - - except Exception as e: - import traceback - traceback.print_exc() - return JsonResponse({ - 'success': False, - 'message': f'Failed to delete batch: {str(e)}' - }, status=500) - - -@csrf_exempt -@require_http_methods(["DELETE", "POST"]) -def delete_batch_invalid(request, batch_id): - """ - Handle delete requests with invalid batch IDs (like 'undefined') - URL: /programme_curriculum/api/batches//delete/ - """ - # Check for common invalid values - if batch_id in ['undefined', 'null', 'NaN', '']: - return JsonResponse({ - 'success': False, - 'message': 'Please select a batch before attempting to delete. No batch was selected.', - 'error_type': 'no_batch_selected', - 'provided_id': batch_id, - 'user_action': 'Please click on a batch row to select it, then try deleting again.' - }, status=400) - - # Try to convert to integer in case it's a string number - try: - actual_batch_id = int(batch_id) - # Redirect to the proper delete function - return delete_batch(request, actual_batch_id) - except (ValueError, TypeError): - return JsonResponse({ - 'success': False, - 'message': f'Invalid batch ID format: "{batch_id}". The batch ID must be a number.', - 'error_type': 'invalid_batch_id_format', - 'provided_id': batch_id, - 'user_action': 'Please ensure you have selected a valid batch before attempting to delete.' - }, status=400) - - -# Utility functions for intelligent course versioning - -def levenshtein_distance(s1, s2): - """Calculate Levenshtein distance between two strings""" - if len(s1) < len(s2): - return levenshtein_distance(s2, s1) - - if len(s2) == 0: - return len(s1) - - previous_row = list(range(len(s2) + 1)) - for i, c1 in enumerate(s1): - current_row = [i + 1] - for j, c2 in enumerate(s2): - insertions = previous_row[j + 1] + 1 - deletions = current_row[j] + 1 - substitutions = previous_row[j] + (c1 != c2) - current_row.append(min(insertions, deletions, substitutions)) - previous_row = current_row - - return previous_row[-1] - - -def is_typo_correction(old_value, new_value, max_distance=3): - """Check if the change is likely a typo correction using Levenshtein distance""" - if not old_value or not new_value: - return False - - # Convert to string and strip whitespace - old_str = str(old_value).strip() - new_str = str(new_value).strip() - - # If they're the same after stripping, it's just whitespace change - if old_str == new_str: - return True - - # Calculate distance - distance = levenshtein_distance(old_str.lower(), new_str.lower()) - - # Consider it a typo if distance is small relative to string length - min_length = min(len(old_str), len(new_str)) - if min_length == 0: - return False - - # Allow up to max_distance characters difference, or 20% of string length, whichever is smaller - threshold = min(max_distance, max(1, int(min_length * 0.2))) - - return distance <= threshold - - -def determine_version_bump_type(old_course_data, new_course_data): - """ - Determine the type of version bump needed based on changed fields. - - Returns: - tuple: (bump_type, changed_academic_fields, reason) - bump_type: 'MAJOR', 'MINOR', 'PATCH', or 'NONE' - changed_academic_fields: list of academic fields that changed - reason: string explaining the decision - """ - - # Define version-controlled academic fields - VERSION_CONTROLLED_FIELDS = { - 'course_name': 'name', - 'course_code': 'code', - 'credit': 'credit', - 'lecture': 'lecture_hours', - 'tutorial': 'tutorial_hours', - 'practical': 'pratical_hours', - 'discussion_hours': 'discussion_hours', - 'practical_hours': 'project_hours', # Note: this maps to project_hours in model - 'pre_requisites': 'pre_requisits', - 'pre_requisite_course': 'pre_requisit_courses', - 'syllabus': 'syllabus', - 'references': 'ref_books', - 'quiz_1': 'percent_quiz_1', - 'quiz_2': 'percent_quiz_2', - 'midsem': 'percent_midsem', - 'endsem': 'percent_endsem', - 'project': 'percent_project', - 'lab': 'percent_lab_evaluation', - 'attendance': 'percent_course_attendance' - } - - # Fields that trigger different bump types - MAJOR_BUMP_FIELDS = ['name', 'code', 'credit', 'lecture_hours', 'tutorial_hours', 'pratical_hours', 'pre_requisits', 'pre_requisit_courses'] - MINOR_BUMP_FIELDS = ['percent_quiz_1', 'percent_quiz_2', 'percent_midsem', 'percent_endsem', 'percent_project', 'percent_lab_evaluation', 'percent_course_attendance', 'ref_books'] - PATCH_BUMP_FIELDS = ['syllabus', 'discussion_hours', 'project_hours'] - - changed_academic_fields = [] - major_changes = [] - minor_changes = [] - patch_changes = [] - - # Check each version-controlled field for changes - for field_name, model_field in VERSION_CONTROLLED_FIELDS.items(): - old_value = old_course_data.get(model_field) - new_value = new_course_data.get(model_field) - - if old_value != new_value: - # Special handling for course name - check if it's a typo - # Course codes always trigger version bump regardless of similarity - if model_field == 'name': - if is_typo_correction(old_value, new_value): - continue # Skip typo corrections - no version bump needed - - changed_academic_fields.append(field_name) - - if model_field in MAJOR_BUMP_FIELDS: - major_changes.append(field_name) - elif model_field in MINOR_BUMP_FIELDS: - minor_changes.append(field_name) - elif model_field in PATCH_BUMP_FIELDS: - patch_changes.append(field_name) - - # Determine bump type based on highest priority changes - if major_changes: - return ('MAJOR', changed_academic_fields, f"Major academic changes: {', '.join(major_changes)}") - elif minor_changes: - return ('MINOR', changed_academic_fields, f"Minor academic changes: {', '.join(minor_changes)}") - elif patch_changes: - return ('PATCH', changed_academic_fields, f"Minor syllabus/content changes: {', '.join(patch_changes)}") - else: - return ('NONE', changed_academic_fields, "No academic fields changed or only typo corrections detected") - - -def calculate_new_version(current_version, bump_type): - """Calculate the new version number based on bump type - all bumps increment by 0.1""" - if bump_type == 'NONE': - return current_version - - # Parse current version (e.g., "1.2" -> 1.2) - current_float = float(current_version) - - # All version bumps increment by 0.1 - if bump_type in ['MAJOR', 'MINOR', 'PATCH']: - new_version = current_float + 0.1 - # Format to 1 decimal place - return f"{new_version:.1f}" - - return current_version - - -def create_course_audit_log(course, user, action, old_data=None, new_data=None, - version_bump_type='NONE', old_version=None, new_version=None, - admin_override=False, reason=""): - """Create an audit log entry for course changes""" - - changed_fields = [] - if old_data and new_data: - # Find changed fields - for field, old_value in old_data.items(): - new_value = new_data.get(field) - if old_value != new_value: - changed_fields.append(field) - - audit_log = CourseAuditLog.objects.create( - course=course, - user=user, - action=action, - old_values=old_data, - new_values=new_data, - changed_fields=changed_fields, - version_bump_type=version_bump_type, - old_version=old_version, - new_version=new_version, - admin_override=admin_override, - reason=reason - ) - - return audit_log \ No newline at end of file diff --git a/FusionIIIT/applications/programme_curriculum/api/views_student_management.py b/FusionIIIT/applications/programme_curriculum/api/views_student_management.py deleted file mode 100644 index a959a85e1..000000000 --- a/FusionIIIT/applications/programme_curriculum/api/views_student_management.py +++ /dev/null @@ -1,5133 +0,0 @@ -import json -import pandas as pd -import openpyxl -import random -import secrets -import string -import sys -import os -from io import BytesIO -from datetime import datetime, date -from django.http import JsonResponse, HttpResponse -from django.views.decorators.csrf import csrf_exempt -from django.views.decorators.http import require_http_methods -from django.contrib.auth.decorators import login_required -from django.core.exceptions import ValidationError -from django.db import transaction, connection -from django.db.models import Q, Count -from django.conf import settings -from django.utils import timezone -from rest_framework.decorators import api_view, permission_classes -from rest_framework.permissions import IsAuthenticated -from rest_framework.response import Response -from rest_framework import status - -from applications.academic_information.models import Student as AcademicStudent -from applications.globals.models import ExtraInfo, Designation, HoldsDesignation -from django.contrib.auth.models import User -from applications.programme_curriculum.models import ( - Programme, Curriculum, Batch, Discipline -) - -from applications.programme_curriculum.views_password_email import send_password_email_smtp -from applications.programme_curriculum.models_password_email import PasswordEmailLog - -try: - from applications.programme_curriculum.models_student_management import ( - StudentBatchUpload, BatchConfiguration, StudentStatusLog - ) -except ImportError: - from django.db import models - from django.contrib.auth.models import User - pass - -def parse_request_data(request, field_mappings=None): - """ - Helper function to parse request data from various formats with field mapping - - Args: - request: Django request object - field_mappings: Dict mapping backend_field -> frontend_field(s) - e.g. {'year': ['year', 'batchYear'], 'total_seats': 'totalSeats'} - - Returns: - Parsed and mapped data dictionary - """ - try: - data = json.loads(request.body) - - if not isinstance(data, dict): - return {} - except json.JSONDecodeError: - # Fallback: accept form-encoded or multipart form data from frontend - try: - data = request.POST.dict() if hasattr(request, 'POST') else {} - except Exception: - data = {} - - # Apply field mappings if provided - if field_mappings: - mapped_data = {} - for backend_field, frontend_fields in field_mappings.items(): - if isinstance(frontend_fields, list): - for field in frontend_fields: - if data.get(field): - mapped_data[backend_field] = data.get(field) - break - else: - mapped_data[backend_field] = data.get(frontend_fields) or data.get(backend_field) - - mapped_frontend_fields = set() - for frontend_fields in field_mappings.values(): - if isinstance(frontend_fields, list): - mapped_frontend_fields.update(frontend_fields) - else: - mapped_frontend_fields.add(frontend_fields) - - for key, value in data.items(): - if key not in mapped_frontend_fields and key not in mapped_data: - mapped_data[key] = value - - return mapped_data - - return data - -def sanitize_phone_number(phone_value): - if phone_value is None: - return phone_value - phone_str = str(phone_value) - if phone_str.endswith('.0'): - phone_str = phone_str[:-2] - return phone_str - -def validate_phone_numbers(student_data, field_map=None): - """ - Returns (student_phone, father_phone, errors) tuple - """ - if field_map is None: - field_map = { - 'student': ['phone_number', 'Mobile No', 'phoneNumber'], - 'father': ['father_mobile', 'Father Mobile Number', 'fatherMobile'] - } - - errors = [] - student_phone = None - father_phone = None - - # Get student phone - student_phone_raw = None - for field in field_map['student']: - if student_data.get(field): - student_phone_raw = student_data.get(field) - break - - # Get father phone - father_phone_raw = None - for field in field_map['father']: - if student_data.get(field): - father_phone_raw = student_data.get(field) - break - - # Validate and sanitize phones - if student_phone_raw: - sp = sanitize_phone_number(student_phone_raw) - student_phone = sp.replace(' ', '').replace('-', '') if sp else None - if student_phone and (not student_phone.isdigit() or len(student_phone) != 10): - errors.append(f'Invalid student mobile: {student_phone_raw}') - - if father_phone_raw: - fp = sanitize_phone_number(father_phone_raw) - father_phone = fp.replace(' ', '').replace('-', '') if fp else None - if father_phone and (not father_phone.isdigit() or len(father_phone) != 10): - errors.append(f'Invalid father mobile: {father_phone_raw}') - - if student_phone and father_phone and student_phone == father_phone: - errors.append("Father's mobile number should not be same as student's mobile number") - - return student_phone, father_phone, errors - -def sanitize_rank_value(rank_value): - if rank_value is None or rank_value == '' or rank_value == 'null': - return None - rank_str = str(rank_value).strip() - if not rank_str or rank_str.lower() == 'nan': - return None - if rank_str.endswith('.0'): - rank_str = rank_str[:-2] - return rank_str - -def _safe_int_conversion(value): - """Convert a value to int, returning None for empty/invalid values""" - if value is None or value == '' or value == 'null': - return None - try: - value_str = str(value).replace(',', '').strip() - if value_str and value_str.isdigit(): - return int(value_str) - except (ValueError, TypeError): - pass - return None - -def parse_date_flexible(date_value): - if date_value is None or date_value == '': - return None - - try: - if hasattr(date_value, 'date'): - return date_value.date() - - date_str = str(date_value).strip() - if not date_str or date_str.lower() == 'nan': - return None - - date_formats = [ - '%d-%m-%Y', # dd-mm-yyyy (Excel common format) - '%d/%m/%Y', # dd/mm/yyyy - '%Y-%m-%d', # yyyy-mm-dd (ISO format) - '%m/%d/%Y', # mm/dd/yyyy (US format) - '%d-%m-%y', # dd-mm-yy - '%d/%m/%y', # dd/mm/yy - '%Y/%m/%d', # yyyy/mm/dd - ] - - for fmt in date_formats: - try: - return datetime.strptime(date_str, fmt).date() - except ValueError: - continue - - return None - - except Exception: - return None - -def get_batch_year_from_academic_year(academic_year): - if isinstance(academic_year, str): - if '-' in academic_year: - return int(academic_year.split('-')[0]) - else: - return int(academic_year) - return int(academic_year) - -def get_academic_year_from_batch_year(batch_year): - batch_year = int(batch_year) - next_year = batch_year + 1 - return f"{batch_year}-{str(next_year)[-2:]}" - -def calculate_batch_filled_seats(batch): - """ - Calculate filled seats for a batch using curriculum-based counting only. - """ - try: - from applications.academic_information.models import Student - if batch.curriculum: - curriculum_count = Student.objects.filter( - batch_id=batch - ).count() - return curriculum_count - else: - return 0 - - except Exception as e: - return 0 - -def normalize_year_input(year_input): - try: - if isinstance(year_input, str): - if '-' in year_input: - parts = year_input.split('-') - if len(parts) != 2: - raise ValueError(f"Invalid academic year format: {year_input}") - batch_year = int(parts[0]) - second_year = int(parts[1]) - if second_year != (batch_year + 1) % 100: - raise ValueError(f"Invalid academic year format: {year_input}") - academic_year = year_input - else: - batch_year = int(year_input) - academic_year = get_academic_year_from_batch_year(batch_year) - else: - batch_year = int(year_input) - academic_year = get_academic_year_from_batch_year(batch_year) - - return batch_year, academic_year - except (ValueError, TypeError) as e: - raise ValueError(f"Invalid year format: {year_input}. Expected formats: 2025, '2025', or '2025-26'") - -def get_current_academic_year(): - current_year = datetime.now().year - if datetime.now().month >= 7: - batch_year = current_year - else: - batch_year = current_year - 1 - - academic_year = get_academic_year_from_batch_year(batch_year) - return batch_year, academic_year - -def validate_and_normalize_year(selected_year): - """ - Returns (batch_year, academic_year) or raises JsonResponse error - """ - if selected_year: - try: - return normalize_year_input(selected_year) - except ValueError as e: - return JsonResponse({ - 'success': False, - 'message': f'Invalid year format: {str(e)}' - }, status=400) - else: - return get_current_academic_year() - -def validate_curriculum_exists(action_context="performing action"): - """ - Returns JsonResponse error if no curriculums found, None if valid - """ - from applications.programme_curriculum.models import Curriculum - - available_curriculums = Curriculum.objects.filter(working_curriculum=True) - if not available_curriculums.exists(): - return JsonResponse({ - 'success': False, - 'message': f'CURRICULUM REQUIRED: No working curriculums found. Please create a curriculum first before {action_context}.', - 'validation_error': 'missing_curriculum' - }, status=400) - return None - -def validate_batch_curriculum_requirements(batch_year, academic_year, action_context="performing action"): - """ - Returns JsonResponse error if validation fails, None if valid - """ - from applications.programme_curriculum.models import Batch - - existing_batches = Batch.objects.filter(year=batch_year, running_batch=True) - if not existing_batches.exists(): - return JsonResponse({ - 'success': False, - 'message': f'BATCH REQUIRED: No active batches found for academic year {academic_year} (batch year {batch_year}). Please create batches with assigned curriculums first before {action_context}.', - 'validation_error': 'missing_batch', - 'academic_year': academic_year, - 'batch_year': batch_year - }, status=400) - - batches_without_curriculum = existing_batches.filter(curriculum__isnull=True) - - if batches_without_curriculum.exists(): - batch_names = [f"{batch.name} {batch.discipline.acronym}" for batch in batches_without_curriculum] - return JsonResponse({ - 'success': False, - 'message': f'The following batches for {academic_year} exist but have no curriculum assigned: {", ".join(batch_names)}. Please assign a curriculum to all batches first.', - 'validation_error': 'batch_missing_curriculum', - 'batches_without_curriculum': batch_names, - 'academic_year': academic_year - }, status=400) - - return None - -def calculate_current_semester(academic_year, current_date=None): - if current_date is None: - current_date = timezone.now().date() - - current_year = current_date.year - current_month = current_date.month - - years_completed = 0 - - if current_month >= 8: - years_completed = current_year - academic_year - semester_in_year = 1 - else: - years_completed = current_year - academic_year - 1 - if current_month <= 5: - semester_in_year = 2 - else: - semester_in_year = 2 - - total_semester = (years_completed * 2) + semester_in_year - total_semester = max(1, min(total_semester, 8)) - - return total_semester - -@csrf_exempt -@require_http_methods(["GET"]) -def admin_batches_overview(request): - request.GET = request.GET.copy() - request.GET['format'] = 'nested' - return admin_batches_unified(request) - -def get_display_branch_name(discipline): - branch_mappings = { - 'Computer Science and Engineering': 'CSE', - 'Electronics and Communication Engineering': 'ECE', - 'Mechanical Engineering': 'ME', - 'Smart Manufacturing': 'SM', - 'Design': 'DES' - } - return branch_mappings.get(discipline, discipline) - -@csrf_exempt -@require_http_methods(["POST"]) -def process_excel_upload(request): - try: - if 'file' not in request.FILES: - return JsonResponse({ - 'success': False, - 'message': 'No file uploaded' - }, status=400) - - file = request.FILES['file'] - programme_type = request.POST.get('programme_type', 'ug') - - year_result = validate_and_normalize_year(request.POST.get('academic_year')) - if isinstance(year_result, JsonResponse): - return year_result - batch_year, academic_year = year_result - - curriculum_error = validate_curriculum_exists("uploading student data") - if curriculum_error: - return curriculum_error - - batch_error = validate_batch_curriculum_requirements(batch_year, academic_year, "uploading student data") - if batch_error: - return batch_error - - if not file.name.endswith(('.xlsx', '.xls')): - return JsonResponse({ - 'success': False, - 'message': 'Invalid file format. Please upload an Excel file (.xlsx or .xls)' - }, status=400) - - try: - if file.name.endswith('.xlsx'): - df = pd.read_excel(file, engine='openpyxl') - else: - df = pd.read_excel(file, engine='xlrd') - except Exception as e: - return JsonResponse({ - 'success': False, - 'message': f'Error reading Excel file: {str(e)}' - }, status=400) - - valid_students = [] - invalid_students = [] - - column_mapping = { - 'sno': ['sno', 's.no', 'serial number', 's no'], - 'jee_app_no': ['jee main application number', 'jee app. no./ccmt roll. no.', 'jee app. no./ccmt roll no.', 'jee application no./ccmt roll no.', 'jee application no./ccmt roll. no.', 'jee app. no.', 'jee application number', 'jee main app number', 'ccmt roll. no.', 'ccmt roll no', 'jee app no', 'rollno', 'jee app. no / ccmt roll no.', 'jee app. no / ccmt roll no', 'jee app no / ccmt roll no', 'jee app no / ccmt roll no.', 'jee app. no / ccmt roll no', 'jee app no / ccmt roll no', 'jee app. no./ccmt roll no', 'jee app no./ccmt roll no', 'jee app. no. / ccmt roll no.'], - 'roll_number': ['institute roll number', 'roll number', 'rollno', 'inst roll number'], - 'name': ['name', 'student name', 'full name'], - 'discipline': ['discipline', 'branch', 'department'], - 'specialization': ['specialization', 'spec', 'specialisation'], - 'gender': ['gender', 'sex'], - 'category': ['category', 'caste'], - 'pwd': ['pwd', 'disability', 'pwb'], - 'minority': ['minority', 'minority status'], - 'phone_number': ['mobileno', 'mobile', 'phone', 'mobile no'], - 'institute_email': ['institute email id', 'institute email', 'inst email id'], - 'personal_email': ['alternate email id', 'email', 'personal email'], - 'father_name': ['father\'s name', 'father name', 'fathers name'], - 'father_occupation': ['father\'s occupation', 'father occupation', 'fathers occupation'], - 'father_mobile': ['father mobile number', 'father mobile', 'fathers mobile'], - 'mother_name': ['mother\'s name', 'mother name', 'mothers name'], - 'mother_occupation': ['mother\'s occupation', 'mother occupation', 'mothers occupation'], - 'mother_mobile': ['mother mobile number', 'mother mobile', 'mothers mobile'], - 'date_of_birth': ['date of birth', 'dob', 'birth date'], - 'ai_rank': ['ai rank', 'jee rank', 'all india rank'], - 'category_rank': ['category rank', 'cat rank'], - 'allotted_category': ['allottedcat', 'allotted category', 'alloted category'], - 'allotted_gender': ['allotted gender', 'alloted gender'], - 'state': ['state'], - 'address': ['full address', 'address', 'complete address'], - 'parent_email': ['parent\'s email', 'parent email', 'parents email'], - 'country': ['country'], - 'nationality': ['nationality'], - 'blood_group': ['blood group', 'bloodgroup'], - 'blood_group_remarks': ['blood group remarks', 'bloodgroup remarks'], - 'pwd_category': ['pwd category', 'pwb category', 'disability category'], - 'pwd_category_remarks': ['pwd category remarks', 'pwb category remarks', 'disability remarks'], - 'admission_mode': ['admission mode', 'admission type'], - 'admission_mode_remarks': ['admission mode remarks', 'admission type remarks'], - 'income_group': ['income group', 'family income group'], - 'income': ['income', 'family income', 'annual income'] - } - - df.columns = df.columns.str.lower().str.strip() - - mapped_data = {} - for field, possible_columns in column_mapping.items(): - for col in possible_columns: - if col in df.columns: - mapped_data[field] = col - break - - for index, row in df.iterrows(): - try: - student_data = {} - - for field, excel_col in mapped_data.items(): - if excel_col in df.columns: - value = row[excel_col] - if pd.notna(value): - if field == 'minority': - student_data[field] = str(value).strip() - # Fix serial number to start from 1 - elif field == 'sno': - student_data[field] = str(index + 1) # Force serial number to be 1-based - elif field in ['phone_number', 'father_mobile', 'mother_mobile']: - student_data[field] = sanitize_phone_number(value) - elif field in ['ai_rank', 'category_rank']: - student_data[field] = sanitize_rank_value(value) - elif field == 'date_of_birth': - parsed_date = parse_date_flexible(value) - student_data[field] = parsed_date.isoformat() if parsed_date else str(value).strip() - else: - student_data[field] = str(value).strip() - - required_fields = ['name', 'discipline', 'roll_number', 'institute_email'] - missing_fields = [field for field in required_fields if not student_data.get(field)] - - errors = [] - - if student_data.get('gender'): - gender = student_data['gender'].lower() - if gender not in ['male', 'female', 'other']: - errors.append(f'Invalid gender: {student_data["gender"]}') - - if student_data.get('pwd'): - pwd = student_data['pwd'].upper() - if pwd not in ['YES', 'NO']: - errors.append(f'Invalid PWD value: {student_data["pwd"]}') - - - if student_data.get('institute_email'): - email = student_data['institute_email'] - if '@' not in email: - errors.append(f'Invalid institute email format: {email}') - - student_phone, father_phone, phone_errors = validate_phone_numbers(student_data) - errors.extend(phone_errors) - - if missing_fields or errors: - error_msg = [] - if missing_fields: - error_msg.append(f'Missing required fields: {", ".join(missing_fields)}') - if errors: - error_msg.extend(errors) - - invalid_students.append({ - 'row': index + 2, # Excel row number (1-indexed + header) - 'error': '; '.join(error_msg), - 'data': student_data - }) - else: - - cleaned_data = { - 'Sno': student_data.get('sno', ''), - 'jee_app_no': student_data.get('jee_app_no', ''), # Add this for frontend compatibility - 'JEE App. No./CCMT Roll. No.': student_data.get('jee_app_no', ''), - 'JEE App. No / CCMT Roll No': student_data.get('jee_app_no', ''), # Alternative format - 'Jee Main Application Number': student_data.get('jee_app_no', ''), # Alternative format - 'Institute Roll Number': student_data.get('roll_number', ''), - 'Name': student_data.get('name', ''), - 'Discipline': student_data.get('discipline', ''), - 'Specialization': student_data.get('specialization', ''), - 'Gender': student_data.get('gender', ''), - 'Category': student_data.get('category', ''), - 'PWD': student_data.get('pwd', ''), - 'Minority': student_data.get('minority', ''), - 'Mobile No': student_data.get('phone_number', ''), - 'Institute Email ID': student_data.get('institute_email', ''), - 'Alternate Email ID': student_data.get('personal_email', ''), - 'Father\'s Name': student_data.get('father_name', ''), - 'Father\'s Occupation': student_data.get('father_occupation', ''), - 'Father Mobile Number': student_data.get('father_mobile', ''), - 'Mother\'s Name': student_data.get('mother_name', ''), - 'Mother\'s Occupation': student_data.get('mother_occupation', ''), - 'Mother Mobile Number': student_data.get('mother_mobile', ''), - 'Date of Birth': student_data.get('date_of_birth', ''), - 'AI rank': student_data.get('ai_rank', ''), - 'Category Rank': student_data.get('category_rank', ''), - 'allottedcat': student_data.get('allotted_category', ''), - 'Allotted Gender': student_data.get('allotted_gender', ''), - 'State': student_data.get('state', ''), - 'Full Address': student_data.get('address', ''), - 'Parent\'s Email': student_data.get('parent_email', ''), - 'Country': student_data.get('country', ''), - 'Nationality': student_data.get('nationality', ''), - 'Blood Group': student_data.get('blood_group', ''), - 'Blood Group Remarks': student_data.get('blood_group_remarks', ''), - 'PwD Category': student_data.get('pwd_category', ''), - 'PwD Category Remarks': student_data.get('pwd_category_remarks', ''), - 'Admission Mode': student_data.get('admission_mode', ''), - 'Admission Mode Remarks': student_data.get('admission_mode_remarks', ''), - 'Income Group': student_data.get('income_group', ''), - 'Income': student_data.get('income', '') - } - valid_students.append(cleaned_data) - - except Exception as e: - invalid_students.append({ - 'row': index + 2, - 'error': f'Error processing row: {str(e)}', - 'data': {} - }) - - return JsonResponse({ - 'success': True, - 'valid_students': valid_students, - 'invalid_students': invalid_students, - 'valid_records': len(valid_students), - 'invalid_records': len(invalid_students), - 'total_records': len(df), - 'message': f'Excel file processed successfully. {len(valid_students)} valid records, {len(invalid_students)} invalid records.' - }) - - except Exception as e: - return JsonResponse({ - 'success': False, - 'message': f'Failed to process Excel file: {str(e)}' - }, status=500) - -# ============================================================================= -# STUDENT BATCH OPERATIONS -# ============================================================================= - -@csrf_exempt -@require_http_methods(["POST"]) -def check_student_duplicate(student, duplicate_check_fields): - - field_mapping = { - 'jeeAppNo': 'jee_app_no', - 'rollNumber': 'roll_number', - 'instituteEmail': 'institute_email', - 'fname': 'father_name', - 'personalEmail': 'personal_email', - 'mobile': 'mobile_number' - } - - try: - for field in duplicate_check_fields: - backend_field = field_mapping.get(field, field.lower()) - student_value = student.get(field) - - if not student_value: - continue - - if backend_field == 'jee_app_no': - existing = StudentBatchUpload.objects.filter(jee_app_no=student_value).first() - if existing: - return True, f"JEE Application Number {student_value} already exists for {existing.name}" - - elif backend_field == 'roll_number': - existing = StudentBatchUpload.objects.filter(roll_number=student_value).first() - if existing: - return True, f"Roll Number {student_value} already exists for {existing.name}" - - elif backend_field == 'institute_email': - existing = StudentBatchUpload.objects.filter(institute_email=student_value).first() - if existing: - return True, f"Institute Email {student_value} already exists for {existing.name}" - - elif backend_field == 'personal_email': - existing = StudentBatchUpload.objects.filter(personal_email=student_value).first() - if existing: - return True, f"Personal Email {student_value} already exists for {existing.name}" - - elif backend_field == 'mobile_number': - existing = StudentBatchUpload.objects.filter(mobile_number=student_value).first() - if existing: - return True, f"Mobile Number {student_value} already exists for {existing.name}" - - return False, "" - - except Exception as e: - return False, "" - - -@csrf_exempt -@require_http_methods(["POST"]) -def save_students_batch(request): - try: - data = json.loads(request.body) - students = data.get('students', []) - programme_type = data.get('programme_type', 'ug') - year_result = validate_and_normalize_year(data.get('academic_year')) - if isinstance(year_result, JsonResponse): - return year_result - batch_year, academic_year = year_result - - curriculum_error = validate_curriculum_exists("saving student data") - if curriculum_error: - return curriculum_error - - batch_error = validate_batch_curriculum_requirements(batch_year, academic_year, "saving student data") - if batch_error: - return batch_error - - skip_duplicates = data.get('skip_duplicates', False) - duplicate_check_fields = data.get('duplicate_check_fields', ['jeeAppNo', 'rollNumber', 'instituteEmail']) - - if not students: - return JsonResponse({ - 'success': False, - 'message': 'No student data provided' - }, status=400) - - # Initialize counters - successful_uploads = 0 - failed_uploads = 0 - skipped_duplicates = 0 - validation_errors = 0 - errors = [] - - # Filter out duplicates if requested - if skip_duplicates: - filtered_students = [] - duplicate_students = [] - - for student in students: - is_duplicate, duplicate_info = check_student_duplicate(student, duplicate_check_fields) - - if is_duplicate: - skipped_duplicates += 1 - duplicate_students.append({ - 'student': student, - 'duplicate_reason': duplicate_info - }) - else: - filtered_students.append(student) - - students = filtered_students - - # Filter out students with validation errors before processing - valid_students_only = [] - skipped_invalid = 0 - - for i, student in enumerate(students): - student_phone, father_phone, phone_errors = validate_phone_numbers(student) - if phone_errors: - skipped_invalid += 1 - errors.append(f"Skipped student {student.get('Name', 'Unknown')}: {'; '.join(phone_errors)}") - continue - - valid_students_only.append(student) - - processed_students = process_batch_allocation(valid_students_only, programme_type, batch_year) - - for i, student_data in enumerate(processed_students): - try: - with transaction.atomic(): - student_name = student_data.get('Name') or student_data.get('name', 'Unknown') - name = (student_data.get('Name') or student_data.get('name', '')).strip() - - if not name: - validation_errors += 1 - errors.append(f"Student at row has no name - skipping") - continue - - dob_value = student_data.get('Date of Birth') or student_data.get('dob') or student_data.get('date_of_birth') - dob = parse_date_flexible(dob_value) - - discipline_name = student_data.get('Discipline') or student_data.get('branch', '') - specialization = student_data.get('Specialization') or student_data.get('specialization', '') - - # For M.Tech students, use specialization-specific batch names - if programme_type == 'pg' and specialization: - if 'design' in discipline_name.lower(): - batch_name = 'M.Des' - elif specialization == 'Mechatronics': - batch_name = 'M.Tech' # Mechatronics batch is just named "M.Tech" - else: - batch_name = f'M.Tech {specialization}' - else: - batch_name = get_batch_name_from_discipline(discipline_name, programme_type) - - if programme_type == 'pg' and specialization: - if 'design' in discipline_name.lower(): - discipline_obj = get_or_create_discipline('Design') - elif specialization in ['Data Science', 'AI & ML']: - discipline_obj = get_or_create_discipline('Computer Science and Engineering') - elif specialization in ['Communication and Signal Processing', 'Nanoelectronics and VLSI Design', 'Power & Control']: - discipline_obj = get_or_create_discipline('Electronics and Communication Engineering') - elif specialization in ['Design', 'CAD/CAM', 'Manufacturing and Automation']: - discipline_obj = get_or_create_discipline('Mechanical Engineering') - elif specialization == 'Mechatronics': - discipline_obj = get_or_create_discipline('Mechatronics') - else: - discipline_obj = get_or_create_discipline(discipline_name) - else: - discipline_obj = get_or_create_discipline(discipline_name) - - try: - batch_obj = Batch.objects.get( - name=batch_name, - discipline=discipline_obj, - year=batch_year, - running_batch=True - ) - except Batch.DoesNotExist: - name_year_matches = Batch.objects.filter(name=batch_name, year=batch_year, running_batch=True) - discipline_year_matches = Batch.objects.filter(discipline=discipline_obj, year=batch_year, running_batch=True) - existing_batches = Batch.objects.filter(year=batch_year, running_batch=True) - - error_msg = f"No active batch exists for {batch_name} with discipline '{discipline_obj.name}' in Year-{batch_year}. Please create the batch via Admin Batch Management first." - failed_uploads += 1 - errors.append({ - 'student': student_data.get('Name', 'Unknown'), - 'roll_number': student_data.get('Institute Roll Number', ''), - 'error': error_msg, - 'validation_error': 'missing_batch', - 'required_batch': f"{batch_name} {discipline_obj.name} {batch_year}", - 'required_action': f'Create batch for {batch_name} {discipline_obj.name} Year-{batch_year} via Admin Batch Management first' - }) - continue - - student_name = student_data.get('Name') or student_data.get('name', 'Unknown') - - student_upload = StudentBatchUpload.objects.create( - # Core identification - handle both field name formats - jee_app_no=student_data.get('JEE App. No./CCMT Roll. No.') or student_data.get('JEE App. No / CCMT Roll No') or student_data.get('Jee Main Application Number') or student_data.get('jee_app_no') or student_data.get('jeeAppNo') or None, - roll_number=student_data.get('Institute Roll Number') or student_data.get('rollNumber', ''), - institute_email=student_data.get('Institute Email ID') or student_data.get('instituteEmail', ''), - - name=student_data.get('Name') or student_data.get('name', ''), - father_name=student_data.get("Father's Name") or student_data.get('fname', ''), - mother_name=student_data.get("Mother's Name") or student_data.get('mname', ''), - gender=student_data.get('Gender') or student_data.get('gender', ''), - category=student_data.get('Category') or student_data.get('category', ''), - pwd=student_data.get('PWD') or student_data.get('pwd', 'NO'), - minority=student_data.get('Minority') or student_data.get('minority', ''), - - phone_number=sanitize_phone_number(student_data.get('Mobile No') or student_data.get('phoneNumber', '')), - personal_email=student_data.get('Alternate Email ID') or student_data.get('email', '') or student_data.get('alternateEmail', '') or student_data.get('personalEmail', '') or student_data.get('personal_email', ''), - parent_email=student_data.get('Parent Email') or student_data.get('parentEmail', '') or student_data.get('parent_email', ''), - address=student_data.get('Full Address') or student_data.get('address', ''), - state=student_data.get('State') or student_data.get('state', ''), - country=student_data.get('Country') or student_data.get('country', 'India'), - nationality=student_data.get('Nationality') or student_data.get('nationality', 'Indian'), - blood_group=student_data.get('Blood Group') or student_data.get('bloodGroup', ''), - blood_group_remarks=student_data.get('Blood Group Remarks') or student_data.get('bloodGroupRemarks', ''), - pwd_category=student_data.get('PWD Category') or student_data.get('pwdCategory', ''), - pwd_category_remarks=student_data.get('PWD Category Remarks') or student_data.get('pwdCategoryRemarks', ''), - admission_mode=student_data.get('Admission Mode') or student_data.get('admissionMode', ''), - admission_mode_remarks=student_data.get('Admission Mode Remarks') or student_data.get('admissionModeRemarks', ''), - income_group=student_data.get('Income Group') or student_data.get('incomeGroup', ''), - income=student_data.get('Income') or student_data.get('income', None), - - branch=student_data.get('Discipline') or student_data.get('branch', ''), - specialization=student_data.get('Specialization') or student_data.get('specialization', ''), - date_of_birth=dob, - ai_rank=_safe_int_conversion(sanitize_rank_value(student_data.get('AI rank') or student_data.get('jeeRank'))), - category_rank=_safe_int_conversion(sanitize_rank_value(student_data.get('Category Rank') or student_data.get('categoryRank'))), - - father_occupation=student_data.get("Father's Occupation") or student_data.get('fatherOccupation', ''), - father_mobile=sanitize_phone_number(student_data.get('Father Mobile Number') or student_data.get('fatherMobile', '')), - mother_occupation=student_data.get("Mother's Occupation") or student_data.get('motherOccupation', ''), - mother_mobile=sanitize_phone_number(student_data.get('Mother Mobile Number') or student_data.get('motherMobile', '')), - - allotted_category=student_data.get('allottedcat') or student_data.get('allottedCategory', ''), - allotted_gender=student_data.get('Allotted Gender') or student_data.get('allottedGender', ''), - - year=batch_year, - academic_year=academic_year, # Use the normalized academic year - programme_type=programme_type, - reported_status='NOT_REPORTED', - source='excel_upload', # Track that this came from Excel upload - uploaded_by=request.user if request.user.is_authenticated else None - ) - - # AUTOMATIC USER ACCOUNT CREATION with HASHED PASSWORD - if student_upload.roll_number: - try: - user_account, plain_password = student_upload.create_user_account() - if user_account and plain_password: - # Store password for potential emailing (temporarily) - student_data['auto_generated_password'] = plain_password - student_data['user_created'] = True - else: - student_data['user_created'] = False - except Exception as user_error: - student_data['user_created'] = False - else: - student_data['user_created'] = False - - if student_data.get('Institute Roll Number'): - create_or_update_main_student_record(student_data, batch_obj, batch_year) - - successful_uploads += 1 - - except Exception as e: - failed_uploads += 1 - error_msg = f"Failed to save student {student_name}: {str(e)}" - errors.append(error_msg) - - total_processed = successful_uploads + failed_uploads + validation_errors + skipped_invalid - response_data = { - 'success': True, - 'data': { - 'successful_uploads': successful_uploads, - 'failed_uploads': failed_uploads, - 'skipped_duplicates': skipped_duplicates, - 'validation_errors': validation_errors, - 'skipped_invalid': skipped_invalid, - 'total_processed': total_processed, - 'original_count': len(data.get('students', [])) - }, - 'students': processed_students, - 'summary': get_allocation_summary(processed_students, programme_type), - 'errors': errors - } - - messages = [] - if successful_uploads > 0: - messages.append(f'{successful_uploads} students uploaded successfully') - if skipped_duplicates > 0: - messages.append(f'{skipped_duplicates} duplicates skipped') - if skipped_invalid > 0: - messages.append(f'{skipped_invalid} students with validation errors skipped') - if failed_uploads > 0: - messages.append(f'{failed_uploads} uploads failed') - if validation_errors > 0: - messages.append(f'{validation_errors} validation errors') - - response_data['message'] = '. '.join(messages) + '.' - - return JsonResponse(response_data) - - except Exception as e: - return JsonResponse({ - 'success': False, - 'message': f'Failed to save students batch: {str(e)}' - }, status=500) - -def process_batch_allocation(students, programme_type, batch_year=None): - """Process batch allocation algorithm""" - - if batch_year is None: - current_year = datetime.now().year - batch_year = current_year if datetime.now().month >= 7 else current_year - 1 - - display_year = batch_year - - branch_groups = {} - - for i, student in enumerate(students): - branch_field = student.get('Discipline', '') or student.get('branch', '') - branch_code = get_branch_code(branch_field, programme_type) - - if branch_code not in branch_groups: - branch_groups[branch_code] = [] - - branch_groups[branch_code].append({ - **student, - 'branch_code': branch_code, - 'display_branch': get_display_branch_name(branch_field) - }) - - for branch_code in branch_groups: - branch_groups[branch_code].sort(key=lambda x: x.get('Name', '') or x.get('name', '')) - - # Allocate roll numbers - processed_students = [] - branch_counters = {} - - for branch_code, students_in_branch in branch_groups.items(): - for student in students_in_branch: - processed_students.append({ - **student, - # Use provided roll number and email from Excel/manual entry with correct field names - 'roll_number': student.get('Institute Roll Number', '') or student.get('rollNumber', ''), - 'institute_email': student.get('Institute Email ID', '') or student.get('instituteEmail', ''), - 'year': display_year, - 'programme': programme_type.upper(), - 'allocation_date': timezone.now().isoformat(), - 'status': 'ALLOCATED', - 'reported_status': 'NOT_REPORTED' # Password will be generated when this changes to REPORTED - }) - - - processed_students.sort(key=lambda x: x['roll_number']) - - return processed_students - -def get_branch_code(branch_name, programme_type): - branch_lower = branch_name.lower() - - if programme_type == 'ug': - if 'computer' in branch_lower or 'cse' in branch_lower: - return 'BCS' - elif 'electronics' in branch_lower or 'ece' in branch_lower: - return 'BEC' - elif 'mechanical' in branch_lower or 'me' in branch_lower: - return 'BME' - elif 'smart' in branch_lower or 'manufacturing' in branch_lower: - return 'BSM' - elif 'design' in branch_lower: - return 'BDS' - elif programme_type == 'pg': - if 'computer' in branch_lower or 'cse' in branch_lower: - return 'MCS' - elif 'electronics' in branch_lower or 'ece' in branch_lower: - return 'MEC' - elif 'mechanical' in branch_lower or 'me' in branch_lower: - return 'MME' - elif 'smart' in branch_lower or 'manufacturing' in branch_lower: - return 'MSM' - elif 'design' in branch_lower: - return 'MDS' - elif programme_type == 'phd': - if 'computer' in branch_lower or 'cse' in branch_lower: - return 'PCS' - elif 'electronics' in branch_lower or 'ece' in branch_lower: - return 'PEC' - elif 'mechanical' in branch_lower or 'me' in branch_lower: - return 'PME' - elif 'smart' in branch_lower or 'manufacturing' in branch_lower: - return 'PSM' - elif 'design' in branch_lower: - return 'PDS' - - return 'UNK' # Unknown - -def generate_roll_number(branch_code, year, sequence): - year_suffix = str(year)[-2:] - sequence_str = str(sequence).zfill(3) - return f"{year_suffix}{branch_code}{sequence_str}" - -def generate_institute_email(roll_number): - return f"{roll_number.lower()}@iiitdmj.ac.in" - -def generate_password(): - """Generate a cryptographically secure password""" - import secrets - import string - length = 12 - charset = string.ascii_letters + string.digits + "!@#$%" - password = ''.join(secrets.choice(charset) for _ in range(length)) - - return password - -def get_allocation_summary(students, programme_type): - branch_counts = {} - for student in students: - branch = student.get('branch_code', 'Unknown') - branch_counts[branch] = branch_counts.get(branch, 0) + 1 - - return { - 'total_students': len(students), - 'branch_counts': branch_counts, - 'programme': programme_type.upper(), - 'allocation_date': timezone.now().isoformat() - } - -# ============================================================================= -# SINGLE STUDENT OPERATIONS -# ============================================================================= - -@csrf_exempt -@require_http_methods(["POST"]) -def add_single_student(request): - try: - data = json.loads(request.body) - programme_type = data.get('programme_type', 'ug') - - year_result = validate_and_normalize_year(data.get('academic_year')) - if isinstance(year_result, JsonResponse): - return year_result - batch_year, academic_year = year_result - - curriculum_error = validate_curriculum_exists("adding student") - if curriculum_error: - return curriculum_error - - batch_error = validate_batch_curriculum_requirements(batch_year, academic_year, "adding student") - if batch_error: - return batch_error - - field_mapping = { - 'fname': 'father_name', - 'mname': 'mother_name', - 'jeeAppNo': 'jee_app_no', - 'phoneNumber': 'phone_number', - 'email': 'personal_email', - 'dob': 'date_of_birth', - 'aadharNumber': 'aadhar_number', - 'tenthMarks': 'tenth_marks', - 'twelfthMarks': 'twelfth_marks', - 'jeeRank': 'ai_rank', - 'categoryRank': 'category_rank', - 'allottedGender': 'allotted_gender', - 'allottedCategory': 'allotted_category', - 'fatherOccupation': 'father_occupation', - 'fatherMobile': 'father_mobile', - 'motherOccupation': 'mother_occupation', - 'motherMobile': 'mother_mobile', - 'parentEmail': 'parent_email', - 'bloodGroup': 'blood_group', - 'bloodGroupRemarks': 'blood_group_remarks', - 'pwdCategory': 'pwd_category', - 'pwdCategoryRemarks': 'pwd_category_remarks', - 'admissionMode': 'admission_mode', - 'admissionModeRemarks': 'admission_mode_remarks', - 'incomeGroup': 'income_group', - } - - mapped_data = {} - for key, value in data.items(): - mapped_key = field_mapping.get(key, key) - mapped_data[mapped_key] = value - - if key in ['rollNumber', 'instituteEmail']: - mapped_data[key] = value # Keep original name too - - data = mapped_data - - required_fields = ['name', 'father_name', 'mother_name', 'branch', 'gender', 'category', 'pwd', 'address'] - missing_fields = [field for field in required_fields if not data.get(field)] - - if missing_fields: - return JsonResponse({ - 'success': False, - 'message': f'Missing required fields: {", ".join(missing_fields)}' - }, status=400) - - processed_students = process_batch_allocation([data], programme_type, batch_year) - - if not processed_students: - return JsonResponse({ - 'success': False, - 'message': 'Failed to process student data' - }, status=400) - - student_data = processed_students[0] - - jee_app_no = data.get('jee_app_no') - if jee_app_no: - existing_student = StudentBatchUpload.objects.filter(jee_app_no=jee_app_no).first() - if existing_student: - return JsonResponse({ - 'success': False, - 'message': f'Student with JEE Application Number {jee_app_no} already exists (Roll Number: {existing_student.roll_number})' - }, status=400) - - dob = parse_date_flexible(data.get('date_of_birth')) - - # Save to database with ALL Excel-equivalent fields for complete synchronization - with transaction.atomic(): - student = StudentBatchUpload.objects.create( - name=student_data.get('name'), - jee_app_no=student_data.get('jee_app_no'), - roll_number=student_data.get('roll_number'), - institute_email=student_data.get('institute_email'), - - father_name=student_data.get('father_name'), - mother_name=student_data.get('mother_name'), - gender=student_data.get('gender'), - category=student_data.get('category'), - pwd=student_data.get('pwd'), - minority=data.get('minority', ''), - date_of_birth=dob or data.get('date_of_birth'), - - phone_number=sanitize_phone_number(data.get('phone_number', '') or data.get('MobileNo', '')), - personal_email=data.get('personal_email', '') or data.get('email', '') or data.get('alternateEmail', '') or data.get('Alternate Email ID', ''), - parent_email=data.get('parent_email', '') or data.get('parentEmail', ''), - address=student_data.get('address'), - state=data.get('state', '') or data.get('State', ''), - country=data.get('country', 'India'), - nationality=data.get('nationality', 'Indian'), - blood_group=data.get('blood_group', '') or data.get('bloodGroup', ''), - blood_group_remarks=data.get('blood_group_remarks', '') or data.get('bloodGroupRemarks', ''), - pwd_category=data.get('pwd_category', '') or data.get('pwdCategory', ''), - pwd_category_remarks=data.get('pwd_category_remarks', '') or data.get('pwdCategoryRemarks', ''), - admission_mode=data.get('admission_mode', '') or data.get('admissionMode', ''), - admission_mode_remarks=data.get('admission_mode_remarks', '') or data.get('admissionModeRemarks', ''), - income_group=data.get('income_group', '') or data.get('incomeGroup', ''), - income=data.get('income', None), - - father_occupation=data.get('father_occupation', '') or data.get("Father's Occupation", ''), - father_mobile=sanitize_phone_number(data.get('father_mobile', '') or data.get('Father Mobile Number', '')), - mother_occupation=data.get('mother_occupation', '') or data.get("Mother's Occupation", ''), - mother_mobile=sanitize_phone_number(data.get('mother_mobile', '') or data.get('Mother Mobile Number', '')), - - branch=student_data.get('branch'), - specialization=data.get('specialization', ''), - ai_rank=sanitize_rank_value(data.get('ai_rank') or data.get('AI rank')), - category_rank=sanitize_rank_value(data.get('category_rank') or data.get('Category Rank')), - - allotted_category=data.get('allotted_category', '') or data.get('allottedcat', ''), - allotted_gender=data.get('allotted_gender', '') or data.get('allottedGender', ''), - - year=batch_year, - programme_type=programme_type, - reported_status='NOT_REPORTED', - academic_year=academic_year, - allocation_status='ALLOCATED', - source='manual_entry' - ) - - return JsonResponse({ - 'success': True, - 'data': { - 'student_id': student.id, - 'roll_number': student.roll_number or student_data.get('roll_number'), - 'institute_email': student.institute_email or student_data.get('institute_email'), - 'name': student.name, - 'personal_email': student.personal_email, - 'parent_email': student.parent_email, - 'blood_group': student.blood_group, - 'country': student.country, - 'nationality': student.nationality - }, - 'message': f'Student {student.name} added successfully. Use "Update Status" to transfer to main academic system when ready.' - }) - - except json.JSONDecodeError as e: - return JsonResponse({ - 'success': False, - 'message': f'Invalid JSON data: {str(e)}' - }, status=400) - except Exception as e: - return JsonResponse({ - 'success': False, - 'message': f'Failed to add student: {str(e)}' - }, status=500) - -# ============================================================================= -# SEAT MANAGEMENT -# ============================================================================= - -@csrf_exempt -@require_http_methods(["PUT"]) -def set_total_seats(request): - try: - data = json.loads(request.body) - - programme = data.get('programme') - discipline = data.get('discipline') - year = data.get('year') - total_seats = data.get('total_seats') - - if not all([programme, discipline, year, total_seats]): - return JsonResponse({ - 'success': False, - 'message': 'Missing required fields: programme, discipline, year, total_seats' - }, status=400) - - batch_config, created = BatchConfiguration.objects.update_or_create( - programme=programme, - discipline=discipline, - year=year, - defaults={'total_seats': total_seats} - ) - - batch_config.calculate_seats() - - return JsonResponse({ - 'success': True, - 'message': f'Total seats updated to {total_seats} for {programme} - {discipline}', - 'data': { - 'programme': programme, - 'discipline': discipline, - 'year': year, - 'total_seats': total_seats, - 'filled_seats': batch_config.filled_seats, - 'available_seats': batch_config.available_seats - } - }) - - except Exception as e: - return JsonResponse({ - 'success': False, - 'message': f'Failed to update total seats: {str(e)}' - }, status=500) - -# ============================================================================= -# STUDENT STATUS MANAGEMENT -# ============================================================================= - -@csrf_exempt -@require_http_methods(["PUT", "POST", "OPTIONS"]) -def update_student_status(request): - """ - Update student reported status - """ - - if request.method == 'OPTIONS': - response = JsonResponse({}) - response['Access-Control-Allow-Origin'] = '*' - response['Access-Control-Allow-Methods'] = 'PUT, POST, OPTIONS' - response['Access-Control-Allow-Headers'] = 'Content-Type, X-CSRFToken, Authorization' - return response - - try: - data = json.loads(request.body) - - student_id = data.get('studentId') - reported_status = data.get('reportedStatus') - - if not student_id or not reported_status: - return JsonResponse({ - 'success': False, - 'message': 'Missing required fields: studentId, reportedStatus' - }, status=400) - - # Validate reported_status - if reported_status not in ['REPORTED', 'NOT_REPORTED', 'WITHDRAWAL', 'PENDING']: - return JsonResponse({ - 'success': False, - 'message': 'Invalid reportedStatus. Must be REPORTED, NOT_REPORTED, or PENDING' - }, status=400) - - try: - student = StudentBatchUpload.objects.get(id=student_id) - except StudentBatchUpload.DoesNotExist: - return JsonResponse({ - 'success': False, - 'message': 'Student not found' - }, status=404) - - old_status = student.reported_status - student.reported_status = reported_status - student.save() - - # Auto-transfer to main tables when status becomes REPORTED - # OR remove from main tables when status becomes NOT_REPORTED - transfer_success = False - transfer_message = "" - user_created = False - auto_generated_password = None - - if reported_status == 'REPORTED' and old_status != 'REPORTED': - try: - # Simple transfer to main tables - from applications.globals.models import ExtraInfo, DepartmentInfo - from applications.academic_information.models import Student as AcademicStudent - from applications.programme_curriculum.models import Batch, Curriculum - from django.db import transaction - - with transaction.atomic(): - - if not student.user: - try: - user, password = student.create_user_account() - auto_generated_password = password - user_created = True - except Exception as user_error: - user_created = False - else: - if not student.email_password: - auto_generated_password = student.generate_secure_password() - student.email_password = auto_generated_password - student.password_generated_at = timezone.now() - student.password_email_sent = False - student.save() - else: - auto_generated_password = student.email_password - - # Create ExtraInfo if not exists - if student.roll_number: - from applications.globals.models import DepartmentInfo - branch_field = student.branch or '' - branch_upper = branch_field.upper() - - if 'MECHATRONICS' in branch_upper or student.specialization == 'Mechatronics': - dept_name = 'MT' - discipline_name = 'Mechatronics' - elif 'COMPUTER SCIENCE' in branch_upper or 'CSE' in branch_upper: - dept_name = 'CSE' - discipline_name = 'Computer Science and Engineering' - elif 'ELECTRONICS' in branch_upper or 'ECE' in branch_upper: - dept_name = 'ECE' - discipline_name = 'Electronics and Communication Engineering' - elif 'MECHANICAL' in branch_upper or 'ME' in branch_upper: - dept_name = 'ME' - discipline_name = 'Mechanical Engineering' - elif 'SMART MANUFACTURING' in branch_upper or 'SMART' in branch_upper or 'SM' in branch_upper: - dept_name = 'SM' - discipline_name = 'Smart Manufacturing' - elif 'DESIGN' in branch_upper or 'DES' in branch_upper or 'B.DES' in branch_upper: - dept_name = 'Design' # Exact database name - discipline_name = 'Design' - discipline_acronym = 'Des.' # Use existing acronym - else: - # Default fallback - dept_name = 'CSE' - discipline_name = 'Computer Science and Engineering' - - try: - department = DepartmentInfo.objects.get(name=dept_name) - except DepartmentInfo.DoesNotExist: - department = DepartmentInfo.objects.create(name=dept_name) - - extra_info, created = ExtraInfo.objects.get_or_create( - id=student.roll_number, - defaults={ - 'user': student.user, - 'title': 'Mr.' if student.gender == 'Male' else ('Ms.' if student.gender == 'Female' else 'Mr.'), - 'sex': 'M' if student.gender == 'Male' else ('F' if student.gender == 'Female' else 'O'), - 'date_of_birth': student.date_of_birth or timezone.now().date(), - 'address': student.address or '', - 'phone_no': int(sanitize_phone_number(student.phone_number)) if student.phone_number and sanitize_phone_number(student.phone_number).isdigit() else 9999999999, - 'user_type': 'student', # CRITICAL: Must be 'student' for proper access - 'department': department - } - ) - - # IMPORTANT: Update ALL fields even if ExtraInfo already existed - if not created: # If record already existed, update ALL relevant fields - extra_info.department = department - extra_info.user = student.user # Also ensure user is set - extra_info.title = 'Mr.' if student.gender == 'Male' else ('Ms.' if student.gender == 'Female' else 'Mr.') - extra_info.sex = 'M' if student.gender == 'Male' else ('F' if student.gender == 'Female' else 'O') - extra_info.date_of_birth = student.date_of_birth or extra_info.date_of_birth - extra_info.address = student.address or extra_info.address - sanitized_phone = sanitize_phone_number(student.phone_number) - if sanitized_phone and sanitized_phone.isdigit(): - extra_info.phone_no = int(sanitized_phone) - extra_info.user_type = 'student' # CRITICAL: Ensure user_type is always 'student' - extra_info.save() - - # FETCH from existing main tables instead of creating new ones - from applications.programme_curriculum.models import Batch, Discipline, Curriculum, Programme - from applications.globals.models import DepartmentInfo - - # FIND existing discipline based on department mapping - discipline = None - try: - # For PG students with specialization, use the same mapping as upload logic - if student.programme_type == 'pg' and student.specialization: - if student.specialization in ['Data Science', 'AI & ML']: - discipline_name = 'Computer Science and Engineering' - elif student.specialization in ['Communication and Signal Processing', 'Nanoelectronics and VLSI Design', 'Power & Control']: - discipline_name = 'Electronics and Communication Engineering' - elif student.specialization == 'Design': - discipline_name = 'Design' - elif student.specialization in ['CAD/CAM', 'Manufacturing and Automation']: - discipline_name = 'Mechanical Engineering' - elif student.specialization == 'Mechatronics': - discipline_name = 'Mechatronics' - else: - branch_field = student.branch or '' - branch_upper = branch_field.upper() - if 'COMPUTER SCIENCE' in branch_upper or 'CSE' in branch_upper: - discipline_name = 'Computer Science and Engineering' - elif 'ELECTRONICS' in branch_upper or 'ECE' in branch_upper: - discipline_name = 'Electronics and Communication Engineering' - elif 'MECHANICAL' in branch_upper or 'ME' in branch_upper: - discipline_name = 'Mechanical Engineering' - elif 'DESIGN' in branch_upper or 'DES' in branch_upper: - discipline_name = 'Design' - else: - discipline_name = 'Computer Science and Engineering' - - discipline = Discipline.objects.filter(name__exact=discipline_name).first() - if not discipline: - discipline = Discipline.objects.filter(name__icontains=discipline_name.split()[0]).first() - else: - branch_field = student.branch or '' - branch_upper = branch_field.upper() - - if 'MECHATRONICS' in branch_upper or (student.specialization and student.specialization == 'Mechatronics'): - dept_name = 'MT' - discipline_name = 'Mechatronics' - elif 'COMPUTER SCIENCE' in branch_upper or 'CSE' in branch_upper: - dept_name = 'CSE' - discipline_name = 'Computer Science and Engineering' - elif 'ELECTRONICS' in branch_upper or 'ECE' in branch_upper: - dept_name = 'ECE' - discipline_name = 'Electronics and Communication Engineering' - elif 'MECHANICAL' in branch_upper or 'ME' in branch_upper: - dept_name = 'ME' - discipline_name = 'Mechanical Engineering' - elif 'SMART MANUFACTURING' in branch_upper or 'SMART' in branch_upper or 'SM' in branch_upper: - dept_name = 'SM' - discipline_name = 'Smart Manufacturing' - elif 'DESIGN' in branch_upper or 'DES' in branch_upper or 'B.DES' in branch_upper: - dept_name = 'Design' # Exact database name - discipline_name = 'Design' - discipline_acronym = 'Des.' # Use existing acronym - else: - dept_name = 'CSE' - discipline_name = 'Computer Science and Engineering' - - if dept_name == 'Design': - discipline = Discipline.objects.filter( - acronym='Des.', - name='Design' - ).first() - - if not discipline: - discipline = Discipline.objects.filter(name__icontains='design').first() - else: - discipline = Discipline.objects.filter( - acronym=dept_name, - name__exact=discipline_name - ).first() - - if not discipline: - disciplines = Discipline.objects.filter(acronym=dept_name).order_by('name') - discipline = disciplines.first() - - except Exception as e: - pass - - if not discipline: - if student.programme_type == 'pg' and student.specialization: - discipline = Discipline.objects.filter(name__icontains='Computer Science').first() # Default fallback - else: - discipline = Discipline.objects.filter(acronym='CSE').first() # Default fallback - - # FIND existing programme based on correct naming pattern - if student.programme_type == 'ug': - if 'design' in student.branch.lower(): - programme_name = 'B.Des' # Match the actual batch name in database - else: - programme_name = 'B.Tech' - programme_category = 'UG' - elif student.programme_type == 'pg': - if 'design' in student.branch.lower(): - programme_name = 'M.Des' # Match the actual batch name for Design PG - else: - programme_name = 'M.Tech' - programme_category = 'PG' - else: - programme_name = 'Ph.D' - programme_category = 'PhD' - - # FIND existing programme (look for existing ones first) - programme = None - try: - if dept_name == 'Design': - programme = Programme.objects.filter( - name=programme_name, # Use the correct programme_name (B.Des for UG, M.Des for PG) - category=programme_category, - discipline=discipline - ).first() - else: - programme_search_name = f"{programme_name} {dept_name}" - programme = Programme.objects.filter( - name__icontains=programme_search_name, - category=programme_category - ).first() - - if not programme: - programme = Programme.objects.filter( - name__icontains=dept_name, - category=programme_category - ).first() - - if not programme: - programme = Programme.objects.get_or_create( - name=programme_name, - category=programme_category, - defaults={'discipline': discipline} - )[0] - except Exception as e: - programme = Programme.objects.get_or_create( - name=programme_name, - category=programme_category, - defaults={'discipline': discipline} - )[0] - - from applications.programme_curriculum.models import Curriculum - - discipline_from_excel = student.branch or '' - - # For PG students, use the actual specialization field; for UG students, map from discipline - if student.programme_type == 'pg' and student.specialization: - specialization = student.specialization[:35] # Limit to 40 chars as per model field limit - else: - if 'Computer Science' in discipline_from_excel or 'CSE' in discipline_from_excel: - specialization = 'Computer Science and Engineering' - elif 'Electronics' in discipline_from_excel or 'ECE' in discipline_from_excel: - specialization = 'Electronics and Communication' - elif 'Mechanical' in discipline_from_excel or 'ME' in discipline_from_excel: - specialization = 'Mechanical Engineering' - elif 'Smart Manufacturing' in discipline_from_excel or 'SM' in discipline_from_excel: - specialization = 'Smart Manufacturing' - elif 'Design' in discipline_from_excel: - specialization = 'Design' - else: - specialization = discipline_from_excel[:35] # Fallback - - transfer_message_addition = "" - - curriculum_assigned = False - if student.programme_type == 'pg': - curriculum_id = get_curriculum_by_specialization(student.specialization, discipline) - - if curriculum_id: - try: - from applications.programme_curriculum.models import Curriculum - student_specific_curriculum = Curriculum.objects.get(id=curriculum_id, working_curriculum=True) - curriculum_assigned = True - specialization_display = student.specialization if student.specialization else 'No specialization' - transfer_message_addition += f" | Curriculum: {student_specific_curriculum.name} (Specialization: {specialization_display})" - except Curriculum.DoesNotExist: - transfer_message_addition += f" | WARNING: Curriculum with ID {curriculum_id} not found" - else: - specialization_display = student.specialization if student.specialization else 'empty/blank' - transfer_message_addition += f" | No curriculum mapping found for specialization '{specialization_display}'" - else: - student_specific_curriculum = None - - - try: - batch_obj = None - batch_created = False - - # For PG students with specialization, only use existing batches - if programme_category == 'PG' and student.specialization and student_specific_curriculum: - if student.specialization == 'Design': - batch_obj = Batch.objects.filter( - name=programme_name, # M.Des - year=student.year, - discipline=discipline, - running_batch=True - ).first() - elif student.specialization == 'Mechatronics': - # Look for existing M.Tech batch for Mechatronics - batch_obj = Batch.objects.filter( - name='M.Tech', - year=student.year, - discipline=discipline, - running_batch=True - ).first() - else: - specialization_batch_name = f"{programme_name} {student.specialization}" - batch_obj = Batch.objects.filter( - name=specialization_batch_name, - year=student.year, - discipline=discipline, - running_batch=True - ).first() - - if not batch_obj: - batch_obj = Batch.objects.filter( - name=programme_name, - year=student.year, - discipline=discipline, - running_batch=True - ).first() - - if not batch_obj: - return JsonResponse({ - 'success': False, - 'message': f'No batch found for {programme_name} {discipline.name} Year-{student.year} with specialization {student.specialization}. Please create the required batch manually first.', - 'error_code': 'BATCH_NOT_FOUND', - 'required_batch': f'{programme_name} {student.specialization}' if student.specialization != 'Design' else programme_name, - 'discipline': discipline.name, - 'year': student.year - }, status=400) - - if not batch_obj: - batch_obj = Batch.objects.filter( - name=programme_name, - year=student.year, - discipline=discipline, - running_batch=True - ).first() - - if not batch_obj and programme_category == 'PG': - batch_obj = Batch.objects.filter( - year=student.year, - discipline=discipline, - running_batch=True - ).first() - - if not batch_obj and programme_category == 'PG': - batch_obj = Batch.objects.filter( - year=student.year, - running_batch=True - ).first() - - if not batch_obj and programme_category == 'PG': - batch_obj = Batch.objects.filter( - discipline=discipline, - running_batch=True - ).first() - - if not batch_obj and programme_category == 'PG': - batch_obj = Batch.objects.filter( - running_batch=True - ).first() - elif not batch_obj and programme_category == 'UG': - batch_obj = Batch.objects.filter( - year=student.year, - discipline=discipline, - running_batch=True - ).first() - - if not batch_obj: - batch_obj = Batch.objects.filter( - year=student.year, - running_batch=True - ).first() - - except Exception as e: - if programme_category == 'PG': - batch_obj = Batch.objects.filter( - running_batch=True - ).first() - elif programme_category == 'UG': - batch_obj = Batch.objects.filter( - running_batch=True - ).first() - else: - batch_obj = Batch.objects.filter(running_batch=True).first() - transfer_message_addition = f" | Warning: Batch matching error: {str(e)}, using fallback batch" - - transfer_message_addition = transfer_message_addition if 'transfer_message_addition' in locals() else "" - - current_semester = calculate_current_semester(int(student.year)) - - # USE EXISTING BATCH (NO AUTOMATIC BATCH CREATION) - final_batch = batch_obj - - final_batch = batch_obj - - if batch_created: - transfer_message_addition += f" | Created new batch: {final_batch.name} with curriculum: {final_batch.curriculum.name}" - elif final_batch.curriculum: - transfer_message_addition += f" | Using batch: {final_batch.name} with curriculum: {final_batch.curriculum.name}" - else: - transfer_message_addition += f" | Using batch: {final_batch.name} (no curriculum assigned to batch)" - - academic_student, created = AcademicStudent.objects.get_or_create( - id=extra_info, - defaults={ - 'batch_id': final_batch, - 'specialization': specialization, - 'programme': programme_name, - 'batch': student.year, - 'father_name': student.father_name or '', - 'mother_name': student.mother_name or '', - 'category': student.category or '', - 'cpi': 0.0, - 'curr_semester_no': current_semester, - 'hall_no': 0, - 'room_no': '', - } - ) - - # IMPORTANT: Update ALL fields even if Student already existed - if not created: - academic_student.specialization = specialization - academic_student.batch_id = final_batch - academic_student.programme = programme_name - academic_student.batch = student.year - academic_student.father_name = student.father_name or '' - academic_student.mother_name = student.mother_name or '' - academic_student.category = student.category or '' - academic_student.curr_semester_no = current_semester - from applications.academic_information.models import Constants - valid_choices = [choice[0] for choice in Constants.MTechSpecialization] - - try: - academic_student.save() - except Exception as save_error: - pass - else: - pass - - academic_student.refresh_from_db() - - try: - pass - except Exception as e: - pass - - # CREATE STUDENT DESIGNATION - try: - if student.user: - student_designation, created = Designation.objects.get_or_create( - name='student', - defaults={ - 'full_name': 'Student', - 'type': 'Academic' - } - ) - - existing_designation = HoldsDesignation.objects.filter( - user=student.user, - designation=student_designation - ).first() - - if not existing_designation: - holds_designation = HoldsDesignation.objects.create( - user=student.user, - working=student.user, - designation=student_designation, - held_at=timezone.now() - ) - transfer_message_addition = f" | Student designation assigned" - else: - transfer_message_addition = f" | Student designation already exists" - - except Exception as designation_error: - transfer_message_addition = f" | Designation error: {str(designation_error)}" - - # COURSE REGISTRATION - MANUAL PROCESS - - course_registration_message = " | Course registration: Manual process (to be done by academic admin)" - - transfer_message_addition += course_registration_message - - # COMPREHENSIVE VALIDATION - Check for missing critical components - validation_warnings = [] - try: - if hasattr(extra_info, 'student') and extra_info.student: - pass - else: - validation_warnings.append("Missing ExtraInfo.student relationship") - - if hasattr(student.user, 'extrainfo') and student.user.extrainfo: - pass - else: - validation_warnings.append("Missing User.extrainfo relationship") - - if academic_student.batch_id and academic_student.batch_id.curriculum: - pass - else: - validation_warnings.append("Missing batch curriculum assignment") - - if academic_student.curr_semester_no >= 1: - pass - else: - validation_warnings.append("Invalid semester number") - - designations = student.user.holds_designations.all() - if designations.filter(designation__name='student').exists(): - pass - else: - validation_warnings.append("Missing student designation access") - - if validation_warnings: - transfer_message_addition += f" | Validation warnings: {'; '.join(validation_warnings)}" - - except Exception as validation_error: - transfer_message_addition += f" | Validation check failed: {str(validation_error)}" - - transfer_success = True - - curriculum_status = "" - if batch_obj and batch_obj.curriculum: - curriculum_status = f" | Batch: {batch_obj.name} {batch_obj.discipline.acronym} {batch_obj.year} | Curriculum: {batch_obj.curriculum.name}" - elif batch_obj: - curriculum_status = f" | Batch: {batch_obj.name} {batch_obj.discipline.acronym} {batch_obj.year} | NO CURRICULUM ASSIGNED - Please assign via Batch Form!" - else: - curriculum_status = f" | NO BATCH FOUND - Critical error!" - - transfer_message = f"Student successfully transferred to main academic tables. Roll: {student.roll_number}{curriculum_status}{transfer_message_addition}" - - # Send welcome email with login credentials using existing email system - email_password = auto_generated_password or student.email_password - if email_password and not student.password_email_sent: - try: - - email_sent, email_message = send_password_email_smtp( - student_email=(student.institute_email or student.personal_email or '').lower(), - student_name=student.name, - password=email_password, - roll_number=student.roll_number, - student=student - ) - - if email_sent: - student.password_email_sent = True - student.save() - transfer_message += f" | Email sent to {student.institute_email or student.personal_email}" - else: - transfer_message += f" | Email failed: {email_message}" - - except Exception as email_error: - transfer_message += f" | Email error: {str(email_error)}" - else: - transfer_message = "Transfer skipped: No roll number assigned" - - except Exception as e: - transfer_message = f"Status updated but transfer failed: {str(e)}" - - # REVERT: Remove from main tables when status becomes NOT_REPORTED - elif reported_status in ['NOT_REPORTED', 'PENDING'] and old_status == 'REPORTED': - try: - from applications.globals.models import ExtraInfo - from applications.academic_information.models import Student as AcademicStudent - from django.db import transaction - - with transaction.atomic(): - if student.roll_number: - try: - extra_info = ExtraInfo.objects.get(id=student.roll_number) - academic_student = AcademicStudent.objects.get(id=extra_info) - - batch_to_check = academic_student.batch_id - - academic_student.delete() - transfer_message += f"Removed AcademicStudent record for {student.roll_number}. " - - if batch_to_check: - remaining_students = AcademicStudent.objects.filter(batch_id=batch_to_check).count() - if remaining_students == 0: - transfer_message += f"Batch {batch_to_check.name} now empty. " - - except AcademicStudent.DoesNotExist: - transfer_message += f"No AcademicStudent record found for {student.roll_number}. " - except ExtraInfo.DoesNotExist: - transfer_message += f"No ExtraInfo record found for {student.roll_number}. " - - try: - extra_info = ExtraInfo.objects.get(id=student.roll_number) - extra_info.delete() - transfer_message += f"Removed ExtraInfo record for {student.roll_number}. " - except ExtraInfo.DoesNotExist: - transfer_message += f"No ExtraInfo record found for {student.roll_number}. " - - if student.user: - try: - username = student.roll_number if student.roll_number else f"temp_{student.jee_app_no}" - if student.user.username == username: - user_to_delete = student.user - student.user = None # Clear the reference first - student.save() - user_to_delete.delete() - transfer_message += f"Removed User account for {username}. " - else: - student.user = None - student.save() - transfer_message += f"Cleared User reference for {student.roll_number}. " - except Exception as e: - transfer_message += f"Error removing User: {str(e)}. " - - try: - if student.user: - student_designation = Designation.objects.get(name='student') - holds_designation = HoldsDesignation.objects.filter( - user=student.user, - designation=student_designation - ).first() - - if holds_designation: - holds_designation.delete() - transfer_message += f"Removed student designation for {student.roll_number}. " - else: - transfer_message += f"No student designation found for {student.roll_number}. " - - except Designation.DoesNotExist: - transfer_message += f"Student designation type does not exist. " - except Exception as designation_error: - transfer_message += f"Error removing designation: {str(designation_error)}. " - - try: - pass - - except Exception as e: - transfer_message += f"Additional cleanup warning: {str(e)}. " - - transfer_success = True - if not transfer_message: - transfer_message = f"Student {student.roll_number} successfully removed from main academic tables" - else: - transfer_message = "Revert skipped: No roll number assigned" - - except Exception as e: - transfer_message = f"Status updated but revert failed: {str(e)}" - - # Log the status change - try: - from django.contrib.auth.models import User - user = None - if hasattr(request, 'user') and request.user.is_authenticated: - user = request.user - - except Exception: - pass - - # Prepare descriptive response message - if reported_status == 'REPORTED' and old_status != 'REPORTED': - main_message = f'Student status updated to {reported_status} and transferred to main academic system' - elif reported_status in ['NOT_REPORTED', 'PENDING'] and old_status == 'REPORTED': - main_message = f'Student status reverted to {reported_status} and removed from main academic system' - else: - main_message = f'Student status updated to {reported_status}' - - response_data = { - 'success': True, - 'message': main_message, - 'data': { - 'student_id': student.id, - 'roll_number': student.roll_number, - 'old_status': old_status, - 'new_status': reported_status, - 'transfer_success': transfer_success, - 'transfer_message': transfer_message, - 'user_created': user_created, - 'password_generated': auto_generated_password if auto_generated_password else None, - 'email_address': student.institute_email or student.personal_email, - 'username': student.roll_number if user_created else None - } - } - - # Update batch seat calculations after status change - try: - from applications.programme_curriculum.models_student_management import BatchConfiguration - from django.db.models import Q - - discipline_q = Q() - if student.branch: - branch_upper = student.branch.upper() - if 'COMPUTER SCIENCE' in branch_upper or 'CSE' in branch_upper: - discipline_q = Q(discipline='Computer Science and Engineering') - elif 'ELECTRONICS' in branch_upper or 'ECE' in branch_upper: - discipline_q = Q(discipline='Electronics and Communication Engineering') - elif 'DESIGN' in branch_upper: - discipline_q = Q(discipline='Design') - elif 'MECHANICAL' in branch_upper: - discipline_q = Q(discipline='Mechanical Engineering') - elif 'SMART' in branch_upper: - discipline_q = Q(discipline='Smart Manufacturing') - - # Update seat calculations for matching batches - if discipline_q: - matching_batches = BatchConfiguration.objects.filter( - discipline_q, - year=student.year - ) - for batch in matching_batches: - batch.calculate_seats() - except Exception as e: - pass - - response = JsonResponse(response_data, status=200) - - response['Access-Control-Allow-Origin'] = '*' - return response - - except json.JSONDecodeError: - return JsonResponse({ - 'success': False, - 'message': 'Invalid JSON data' - }, status=400) - except Exception as e: - return JsonResponse({ - 'success': False, - 'message': f'Failed to update student status: {str(e)}' - }, status=500) - -# ============================================================================= -# EXPORT FUNCTIONALITY -# ============================================================================= - -@csrf_exempt -@require_http_methods(["GET"]) -def export_students(request, programme_type): - """ - Export student data to Excel - """ - try: - students = StudentBatchUpload.objects.filter(programme_type=programme_type).order_by('roll_number') - - if not students.exists(): - return JsonResponse({ - 'success': False, - 'message': f'No students found for programme type: {programme_type}' - }, status=404) - - output = BytesIO() - workbook = openpyxl.Workbook() - worksheet = workbook.active - worksheet.title = f'{programme_type.upper()} Students' - - headers = [ - 'S.No', 'JEE App. No./CCMT Roll. No.', 'Institute Roll Number', 'Name', - 'Discipline', 'Specialization', 'Gender', 'Category', 'PWD', 'Minority', 'Mobile No', - 'Institute Email ID', 'Alternate Email ID', 'Father\'s Name', - 'Father\'s Occupation', 'Father Mobile Number', 'Mother\'s Name', - 'Mother\'s Occupation', 'Mother Mobile Number', 'Date of Birth', - 'AI Rank', 'Category Rank', 'Allotted Category', 'Allotted Gender', - 'State', 'Full Address', 'Reported Status' - ] - - for col, header in enumerate(headers, 1): - worksheet.cell(row=1, column=col, value=header) - - for row, student in enumerate(students, 2): - data = [ - row - 1, # S.No - student.jee_app_no, - student.roll_number, - student.name, - student.branch, - getattr(student, 'specialization', ''), - student.gender, - student.category, - student.pwd, - getattr(student, 'minority', ''), - student.phone_number, - student.institute_email, - student.personal_email, - student.father_name, - getattr(student, 'father_occupation', ''), - getattr(student, 'father_mobile', ''), - student.mother_name, - getattr(student, 'mother_occupation', ''), - getattr(student, 'mother_mobile', ''), - student.date_of_birth.strftime('%d/%m/%Y') if student.date_of_birth else '', - student.ai_rank, - student.category_rank, - getattr(student, 'allotted_category', ''), - getattr(student, 'allotted_gender', ''), - getattr(student, 'state', ''), - student.address, - student.reported_status - ] - - for col, value in enumerate(data, 1): - worksheet.cell(row=row, column=col, value=value) - - workbook.save(output) - output.seek(0) - - response = HttpResponse( - output.getvalue(), - content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' - ) - response['Content-Disposition'] = f'attachment; filename="{programme_type}_students_{datetime.now().strftime("%Y%m%d")}.xlsx"' - - return response - - except Exception as e: - return JsonResponse({ - 'success': False, - 'message': f'Failed to export student data: {str(e)}' - }, status=500) - -# ============================================================================= -# UPLOAD HISTORY -# ============================================================================= - -@csrf_exempt -@require_http_methods(["GET"]) -def upload_history(request): - """ - Get upload history - """ - try: - from django.db.models import Count - - history = StudentBatchUpload.objects.values('created_at__date', 'programme_type').annotate( - count=Count('id') - ).order_by('-created_at__date') - - return JsonResponse({ - 'success': True, - 'data': list(history), - 'message': 'Upload history fetched successfully' - }) - - except Exception as e: - return JsonResponse({ - 'success': False, - 'message': f'Failed to fetch upload history: {str(e)}' - }, status=500) - -# ============================================================================= -# STUDENT LISTING -# ============================================================================= - -@csrf_exempt -@require_http_methods(["GET"]) -def list_students(request): - """ - List students with filters - """ - try: - programme_type = request.GET.get('programme_type') - batch_id = request.GET.get('batch_id') - year = request.GET.get('year') - discipline = request.GET.get('discipline') - specialization = request.GET.get('specialization') - - students = StudentBatchUpload.objects.all() - - if programme_type: - students = students.filter(programme_type=programme_type) - - if year: - students = students.filter(year=year) - - # For PG students, filter by specialization first, then discipline - if programme_type == 'pg': - if specialization: - students = students.filter(specialization__icontains=specialization) - elif discipline: - # Handle discipline filtering with typo tolerance - discipline_filters = Q() - discipline_filters |= Q(branch__icontains=discipline) - - if 'Engineering' in discipline: - typo_name = discipline.replace('Engineering', 'Enginnering') - discipline_filters |= Q(branch__icontains=typo_name) - elif 'Enginnering' in discipline: - correct_name = discipline.replace('Enginnering', 'Engineering') - discipline_filters |= Q(branch__icontains=correct_name) - - students = students.filter(discipline_filters) - elif discipline: # For UG or general discipline filter - discipline_filters = Q() - discipline_filters |= Q(branch__icontains=discipline) - - if 'Engineering' in discipline: - typo_name = discipline.replace('Engineering', 'Enginnering') - discipline_filters |= Q(branch__icontains=typo_name) - elif 'Enginnering' in discipline: - correct_name = discipline.replace('Enginnering', 'Engineering') - discipline_filters |= Q(branch__icontains=correct_name) - - students = students.filter(discipline_filters) - - if batch_id: - students = students.filter(id=batch_id) - - students = students.order_by('roll_number') - - student_list = [] - for student in students: - student_list.append({ - 'id': student.id, - 'sno': getattr(student, 'id', ''), - 'jee_app_no': getattr(student, 'jee_app_no', ''), - 'JEE App. No / CCMT Roll No': getattr(student, 'jee_app_no', ''), - 'Jee Main Application Number': getattr(student, 'jee_app_no', ''), - 'name': student.name, - 'roll_number': student.roll_number, - 'institute_email': student.institute_email, - 'father_name': student.father_name, - 'category': student.category, - 'pwd': student.pwd, - 'minority': getattr(student, 'minority', ''), - 'reported_status': student.reported_status, - 'branch': student.branch, - 'specialization': getattr(student, 'specialization', ''), - 'year': student.year, - 'source': getattr(student, 'source', 'unknown'), # Include source field - 'parent_email': getattr(student, 'parent_email', ''), - 'parentEmail': getattr(student, 'parent_email', ''), # Camel case for frontend - 'alternateEmail': getattr(student, 'personal_email', ''), - 'country': getattr(student, 'country', ''), - 'nationality': getattr(student, 'nationality', ''), - 'blood_group': getattr(student, 'blood_group', ''), - 'bloodGroup': getattr(student, 'blood_group', ''), # Camel case for frontend - 'blood_group_remarks': getattr(student, 'blood_group_remarks', ''), - 'bloodGroupRemarks': getattr(student, 'blood_group_remarks', ''), # Camel case for frontend - 'pwd_category': getattr(student, 'pwd_category', ''), - 'pwdCategory': getattr(student, 'pwd_category', ''), # Camel case for frontend - 'pwd_category_remarks': getattr(student, 'pwd_category_remarks', ''), - 'pwdCategoryRemarks': getattr(student, 'pwd_category_remarks', ''), # Camel case for frontend - 'admission_mode': getattr(student, 'admission_mode', ''), - 'admissionMode': getattr(student, 'admission_mode', ''), # Camel case for frontend - 'admission_mode_remarks': getattr(student, 'admission_mode_remarks', ''), - 'admissionModeRemarks': getattr(student, 'admission_mode_remarks', ''), # Camel case for frontend - 'income_group': getattr(student, 'income_group', ''), - 'incomeGroup': getattr(student, 'income_group', ''), # Camel case for frontend - 'income': getattr(student, 'income', '') - }) - - return JsonResponse({ - 'success': True, - 'data': student_list, - 'total': len(student_list), - 'message': f'Students listed successfully - Found {len(student_list)} students' - }) - - except Exception as e: - return JsonResponse({ - 'success': False, - 'message': f'Failed to list students: {str(e)}' - }, status=500) - -# ============================================================================= -# BATCH CRUD OPERATIONS -# ============================================================================= - -@csrf_exempt -@require_http_methods(["POST"]) -def create_batch(request): - """ - Create new batch - """ - try: - field_mappings = { - 'programme': ['programme', 'batch_name'], - 'discipline': 'discipline', - 'year': ['year', 'batchYear'], - 'total_seats': ['total_seats', 'totalSeats'], - 'curriculum_data': ['curriculum', 'curriculum_id', 'disciplineBatch'], - 'specialization': 'specialization' - } - - data = parse_request_data(request, field_mappings) - - # Extract mapped fields - programme = data.get('programme') - discipline = data.get('discipline') - year = data.get('year') - total_seats = data.get('total_seats') - curriculum_data = data.get('curriculum_data') - specialization = data.get('specialization', '') - - if not all([programme, discipline, year, total_seats]): - missing_fields = [] - if not programme: missing_fields.append('programme') - if not discipline: missing_fields.append('discipline') - if not year: missing_fields.append('year') - if not total_seats: missing_fields.append('total_seats/totalSeats') - - return JsonResponse({ - 'success': False, - 'message': f'Missing required fields: {", ".join(missing_fields)}' - }, status=400) - - from applications.programme_curriculum.models import Curriculum - - curriculum_obj = None - if curriculum_data: - curriculum_id = None - - if isinstance(curriculum_data, list) and curriculum_data: - curriculum_id = str(curriculum_data[0]) # Take first one for single selection - elif isinstance(curriculum_data, str): - curriculum_id = curriculum_data.strip() - else: - curriculum_id = str(curriculum_data) - - if curriculum_id and curriculum_id not in ['null', 'undefined', '']: - try: - curriculum_obj = Curriculum.objects.get(id=int(curriculum_id), working_curriculum=True) - except (Curriculum.DoesNotExist, ValueError) as e: - return JsonResponse({ - 'success': False, - 'message': f'Invalid curriculum ID: {curriculum_id}. Curriculum not found or not working.', - 'validation_error': 'invalid_curriculum' - }, status=400) - - from applications.programme_curriculum.models import Discipline - - try: - discipline_obj = Discipline.objects.get(id=discipline) - except Discipline.DoesNotExist: - return JsonResponse({ - 'success': False, - 'message': f'Invalid discipline ID: {discipline}' - }, status=400) - - batch_name = programme - existing_batch = Batch.objects.filter( - name=batch_name, - discipline=discipline_obj, - year=year, - running_batch=True - ).first() - - if existing_batch: - return JsonResponse({ - 'success': False, - 'message': f'Batch "{existing_batch.name}" already exists for {existing_batch.discipline.acronym} {existing_batch.year}', - 'validation_error': 'duplicate_batch' - }, status=400) - - # Validate data types - try: - year = int(year) - total_seats = int(total_seats) - except (ValueError, TypeError): - return JsonResponse({ - 'success': False, - 'message': 'Year and total_seats must be integers' - }, status=400) - - try: - batch = Batch.objects.create( - name=batch_name, - discipline=discipline_obj, - year=year, - curriculum=curriculum_obj, - total_seats=total_seats, - running_batch=True - ) - - except Exception as batch_error: - return JsonResponse({ - 'success': False, - 'message': f'Failed to create batch: {str(batch_error)}' - }, status=500) - - if curriculum_obj: - success_message = f'Batch created successfully with curriculum: {curriculum_obj.name}' - curriculum_info = { - 'curriculum': curriculum_obj.name, - 'curriculum_id': curriculum_obj.id - } - else: - success_message = 'Batch created successfully (no curriculum assigned)' - curriculum_info = { - 'curriculum': None, - 'curriculum_id': None - } - - return JsonResponse({ - 'success': True, - 'data': { - 'id': batch.id, - 'programme': batch.name, - 'discipline': batch.discipline.name, - 'disciplineAcronym': batch.discipline.acronym, - 'year': batch.year, - 'total_seats': batch.total_seats, - 'totalSeats': batch.total_seats, # Return both formats - 'specialization': specialization, - 'running_batch': batch.running_batch, - **curriculum_info # Merge curriculum info into response - }, - 'message': success_message - }) - - except json.JSONDecodeError as e: - return JsonResponse({ - 'success': False, - 'message': f'Invalid JSON data: {str(e)}' - }, status=400) - except Exception as e: - return JsonResponse({ - 'success': False, - 'message': f'Failed to create batch: {str(e)}' - }, status=500) - -@csrf_exempt -@require_http_methods(["PUT"]) -def update_batch(request, batch_id): - """ - Update existing batch - """ - try: - data = json.loads(request.body) - - try: - batch = BatchConfiguration.objects.get(id=batch_id) - except BatchConfiguration.DoesNotExist: - # Try to find it in regular Batch model - try: - from applications.programme_curriculum.models import Batch - regular_batch = Batch.objects.get(id=batch_id) - - # Create a corresponding BatchConfiguration if it doesn't exist - batch_config = BatchConfiguration.objects.create( - programme=regular_batch.name, - discipline=regular_batch.discipline.name if regular_batch.discipline else "Unknown", - year=regular_batch.year, - total_seats=regular_batch.total_seats - ) - batch_config.id = batch_id # Force the same ID - batch_config.save() - batch = batch_config - - except: - return JsonResponse({ - 'success': False, - 'message': f'Batch with ID {batch_id} not found in any model' - }, status=404) - - # Update fields - if 'programme' in data: - batch.programme = data['programme'] - if 'discipline' in data: - batch.discipline = data['discipline'] - if 'year' in data: - batch.year = data['year'] - if 'total_seats' in data or 'totalSeats' in data: - new_total_seats = data.get('total_seats') or data.get('totalSeats') - batch.total_seats = new_total_seats - - if 'displayBranch' in data: - batch.discipline = data['displayBranch'] - - batch.save() - - return JsonResponse({ - 'success': True, - 'message': 'Batch updated successfully', - 'batch': { - 'id': batch.id, - 'programme': batch.programme, - 'discipline': batch.discipline, - 'year': batch.year, - 'total_seats': batch.total_seats - } - }) - - except Exception as e: - return JsonResponse({ - 'success': False, - 'message': f'Failed to update batch: {str(e)}' - }, status=500) - - -@csrf_exempt -@require_http_methods(["GET"]) -def list_batches_with_status(request): - """ - List all batches with status - """ - try: - batches = BatchConfiguration.objects.all().order_by('programme', 'discipline', 'year') - - batch_list = [] - for batch in batches: - batch_list.append({ - 'id': batch.id, - 'programme': batch.programme, - 'discipline': batch.discipline, - 'year': batch.year, - 'total_seats': batch.total_seats, - 'filled_seats': batch.filled_seats, - 'available_seats': batch.available_seats - }) - - return JsonResponse({ - 'success': True, - 'data': batch_list, - 'message': 'Batches listed successfully' - }) - - except Exception as e: - return JsonResponse({ - 'success': False, - 'message': f'Failed to list batches: {str(e)}' - }, status=500) - -# ============================================================================= -# STUDENT STATUS CRUD -# ============================================================================= - -@csrf_exempt -@require_http_methods(["PUT"]) -def update_student_status_crud(request, student_id): - """ - Update student status - """ - try: - data = json.loads(request.body) - reported_status = data.get('reported_status') - - if not reported_status: - return JsonResponse({ - 'success': False, - 'message': 'Missing reported_status field' - }, status=400) - - try: - student = StudentBatchUpload.objects.get(id=student_id) - except StudentBatchUpload.DoesNotExist: - return JsonResponse({ - 'success': False, - 'message': 'Student not found' - }, status=404) - - old_status = student.reported_status - student.reported_status = reported_status - student.save() - - try: - from applications.programme_curriculum.models_student_management import BatchConfiguration - from django.db.models import Q - - discipline_q = Q() - if student.branch: - branch_upper = student.branch.upper() - if 'COMPUTER SCIENCE' in branch_upper or 'CSE' in branch_upper: - discipline_q = Q(discipline='Computer Science and Engineering') - elif 'ELECTRONICS' in branch_upper or 'ECE' in branch_upper: - discipline_q = Q(discipline='Electronics and Communication Engineering') - elif 'DESIGN' in branch_upper: - discipline_q = Q(discipline='Design') - elif 'MECHANICAL' in branch_upper: - discipline_q = Q(discipline='Mechanical Engineering') - elif 'SMART' in branch_upper: - discipline_q = Q(discipline='Smart Manufacturing') - - if discipline_q: - matching_batches = BatchConfiguration.objects.filter( - discipline_q, - year=student.year - ) - for batch in matching_batches: - batch.calculate_seats() - except Exception as e: - pass - - return JsonResponse({ - 'success': True, - 'message': f'Student status updated from {old_status} to {reported_status}' - }) - - except Exception as e: - return JsonResponse({ - 'success': False, - 'message': f'Failed to update student status: {str(e)}' - }, status=500) - -# ============================================================================= -# PASSWORD MANAGEMENT -# ============================================================================= - -@csrf_exempt -@require_http_methods(["POST"]) -def auto_generate_passwords_for_batch(request): - """ - Auto-generate passwords for a batch - """ - try: - data = json.loads(request.body) - batch_id = data.get('batch_id') - - if not batch_id: - return JsonResponse({ - 'success': False, - 'message': 'Missing batch_id' - }, status=400) - - try: - batch = BatchConfiguration.objects.get(id=batch_id) - except BatchConfiguration.DoesNotExist: - return JsonResponse({ - 'success': False, - 'message': 'Batch not found' - }, status=404) - - students = StudentBatchUpload.objects.filter( - branch__icontains=batch.discipline, - year=batch.year - ) - - updated_count = 0 - for student in students: - if not hasattr(student, 'password') or not student.password: - student.password = generate_password() - student.save() - updated_count += 1 - - return JsonResponse({ - 'success': True, - 'message': f'Generated passwords for {updated_count} students', - 'data': { - 'updated_count': updated_count, - 'total_students': students.count() - } - }) - - except Exception as e: - return JsonResponse({ - 'success': False, - 'message': f'Failed to generate passwords: {str(e)}' - }, status=500) - -# ============================================================================= -# HELPER FUNCTIONS FOR STUDENT MANAGEMENT -# ============================================================================= - -def get_available_curriculums_for_batch(batch_obj): - """ - Get available curriculums for a batch based on programme and discipline - For multi-curriculum PG batches (M.Tech, M.Des), return all relevant curriculums - """ - if not batch_obj: - return [] - - from applications.programme_curriculum.models import Curriculum - - if hasattr(batch_obj, 'curriculum_options') and batch_obj.curriculum_options: - return batch_obj.curriculum_options - - # Check if this is a multi-curriculum PG batch (M.Tech, M.Des, PhD, etc.) - if batch_obj.name in ['M.Tech', 'M.Des', 'Phd'] and not batch_obj.curriculum: - # Get all working curriculums for this discipline and programme - available_curriculums = Curriculum.objects.filter( - working_curriculum=True, - programme__name__icontains=batch_obj.name - ) - - # Filter by discipline if possible (based on common patterns) - discipline_name = batch_obj.discipline.name.lower() - discipline_filtered = available_curriculums - - # Apply discipline-specific filtering for various disciplines - if 'computer science' in discipline_name or 'cse' in discipline_name: - discipline_filtered = available_curriculums.filter( - Q(name__icontains='AI') | - Q(name__icontains='Data Science') | - Q(name__icontains='Computer Science') - ) - elif 'electronics' in discipline_name or 'ece' in discipline_name: - discipline_filtered = available_curriculums.filter( - Q(name__icontains='Communication and Signal Processing') | - Q(name__icontains='Nanoelectronics and VLSI Design') | - Q(name__icontains='Power & Control') - ) - elif 'mechanical engineering' in discipline_name or 'me' in discipline_name: - discipline_filtered = available_curriculums.filter( - Q(name__icontains='Design') | - Q(name__icontains='CAD/CAM') | - Q(name__icontains='Manufacturing and Automation') - ) - elif 'mechatronics' in discipline_name or 'mt' in discipline_name: - discipline_filtered = available_curriculums.filter( - Q(name__icontains='Mechatronics') - ) - elif 'design' in discipline_name or 'des' in discipline_name: - discipline_filtered = available_curriculums.filter( - Q(name__icontains='Design') - ) - final_curriculums = discipline_filtered if discipline_filtered.exists() else available_curriculums - return [{'id': c.id, 'name': c.name, 'version': c.version} for c in final_curriculums] - - elif batch_obj.curriculum: - # Single curriculum batch - return [{'id': batch_obj.curriculum.id, 'name': batch_obj.curriculum.name, 'version': batch_obj.curriculum.version}] - - return [] - -def get_batch_curriculum_display(batch_obj): - """ - Get curriculum display information for a batch - Returns appropriate curriculum display text based on batch type - """ - if not batch_obj: - return "No curriculum assigned" - - # Get available curriculums dynamically - available_curriculums = get_available_curriculums_for_batch(batch_obj) - if len(available_curriculums) > 1: - curriculum_names = [curr['name'] for curr in available_curriculums] - return f"{len(curriculum_names)} curriculums: {', '.join(curriculum_names)}" - elif len(available_curriculums) == 1: - return available_curriculums[0]['name'] - elif batch_obj.curriculum: - return batch_obj.curriculum.name - - return "Multi-curriculum batch (curriculums assigned based on specialization)" - -def get_curriculum_by_specialization(specialization, discipline_obj): - """ - Map student specialization to appropriate curriculum based on IIITDMJ official specializations - Returns curriculum ID based on specialization and discipline - """ - if not specialization or not discipline_obj: - return None - - from applications.programme_curriculum.models import Curriculum - from django.db.models import Q - - specialization_lower = specialization.lower().strip() - discipline_name = discipline_obj.name.lower() - - # M.Tech Computer Science & Engineering (CSE) specializations - if 'computer science' in discipline_name or 'cse' in discipline_name: - # AI & ML specialization patterns - ai_ml_patterns = ['ai & ml', 'ai and ml', 'artificial intelligence', 'machine learning', 'ai/ml'] - if any(pattern in specialization_lower for pattern in ai_ml_patterns): - try: - curriculum = Curriculum.objects.filter( - Q(name__icontains='AI & ML') | - Q(name__icontains='AI and ML') | - (Q(name__icontains='AI') & Q(name__icontains='ML')), - working_curriculum=True, - programme__name__icontains='M.Tech' - ).first() - if curriculum: - return curriculum.id - except: - pass - - # Data Science specialization patterns - data_science_patterns = ['data science', 'data analytics', 'big data'] - if any(pattern in specialization_lower for pattern in data_science_patterns): - try: - curriculum = Curriculum.objects.filter( - Q(name__icontains='Data Science') | - Q(name__icontains='Data Analytics'), - working_curriculum=True, - programme__name__icontains='M.Tech' - ).first() - if curriculum: - return curriculum.id - except: - pass - - # General CSE patterns - general_cse_patterns = ['total (cse)', 'total cse', 'computer science', 'general cse'] - if any(pattern in specialization_lower for pattern in general_cse_patterns): - try: - curriculum = Curriculum.objects.filter( - working_curriculum=True, - programme__name__icontains='M.Tech', - programme__discipline=discipline_obj - ).exclude( - Q(name__icontains='AI') | Q(name__icontains='Data Science') - ).first() - if curriculum: - return curriculum.id - except: - pass - - # M.Tech Electronics and Communication Engineering (ECE) specializations - elif 'electronics' in discipline_name or 'ece' in discipline_name: - # Communication and Signal Processing patterns - comm_patterns = ['communication and signal processing', 'signal processing', 'communication', 'dsp'] - if any(pattern in specialization_lower for pattern in comm_patterns): - try: - curriculum = Curriculum.objects.filter( - Q(name__icontains='Communication') | - Q(name__icontains='Signal Processing'), - working_curriculum=True, - programme__name__icontains='M.Tech' - ).first() - if curriculum: - return curriculum.id - except: - pass - - # VLSI and Nanoelectronics patterns - vlsi_patterns = ['nanoelectronics and vlsi design', 'vlsi', 'nanoelectronics', 'chip design'] - if any(pattern in specialization_lower for pattern in vlsi_patterns): - try: - curriculum = Curriculum.objects.filter( - Q(name__icontains='VLSI') | - Q(name__icontains='Nanoelectronics'), - working_curriculum=True, - programme__name__icontains='M.Tech' - ).first() - if curriculum: - return curriculum.id - except: - pass - - # Power & Control patterns - power_patterns = ['power & control', 'power and control', 'power systems', 'control systems'] - if any(pattern in specialization_lower for pattern in power_patterns): - try: - curriculum = Curriculum.objects.filter( - Q(name__icontains='Power') | - Q(name__icontains='Control'), - working_curriculum=True, - programme__name__icontains='M.Tech' - ).first() - if curriculum: - return curriculum.id - except: - pass - - # General ECE patterns - general_ece_patterns = ['total (ece)', 'total ece', 'electronics', 'general ece'] - if any(pattern in specialization_lower for pattern in general_ece_patterns): - try: - curriculum = Curriculum.objects.filter( - working_curriculum=True, - programme__name__icontains='M.Tech', - programme__discipline=discipline_obj - ).first() - if curriculum: - return curriculum.id - except: - pass - - elif 'mechatronics' in discipline_name or 'mt' in discipline_name: - mechatronics_patterns = ['mechatronics', 'robotics', 'automation'] - if any(pattern in specialization_lower for pattern in mechatronics_patterns): - try: - curriculum = Curriculum.objects.filter( - Q(name__icontains='MT PG') | Q(name__icontains='Mechatronics'), - working_curriculum=True, - programme__name__icontains='M.Tech' - ).first() - if curriculum: - return curriculum.id - except Exception as e: - pass - - # M.Tech Mechanical Engineering (ME) specializations - elif 'mechanical' in discipline_name or 'me' in discipline_name: - # Only match mechanical-specific design patterns, not generic "design" - design_patterns = ['product design', 'engineering design', 'mechanical design'] - if any(pattern in specialization_lower for pattern in design_patterns): - try: - curriculum = Curriculum.objects.filter( - name__icontains='Design', - working_curriculum=True, - programme__name__icontains='M.Tech' - ).first() - if curriculum: - return curriculum.id - except: - pass - - # CAD/CAM patterns - cad_patterns = ['cad/cam', 'cad', 'cam', 'computer aided'] - if any(pattern in specialization_lower for pattern in cad_patterns): - try: - curriculum = Curriculum.objects.filter( - Q(name__icontains='CAD') | Q(name__icontains='CAM'), - working_curriculum=True, - programme__name__icontains='M.Tech' - ).first() - if curriculum: - return curriculum.id - except: - pass - - # Manufacturing patterns - manufacturing_patterns = ['manufacturing and automation', 'manufacturing', 'automation', 'production'] - if any(pattern in specialization_lower for pattern in manufacturing_patterns): - try: - curriculum = Curriculum.objects.filter( - Q(name__icontains='Manufacturing') | Q(name__icontains='Automation'), - working_curriculum=True, - programme__name__icontains='M.Tech' - ).first() - if curriculum: - return curriculum.id - except: - pass - - # General ME patterns - general_me_patterns = ['total (me)', 'total me', 'mechanical', 'general me'] - if any(pattern in specialization_lower for pattern in general_me_patterns): - try: - curriculum = Curriculum.objects.filter( - working_curriculum=True, - programme__name__icontains='M.Tech', - programme__discipline=discipline_obj - ).first() - if curriculum: - return curriculum.id - except: - pass - # Design specializations - Handle Design discipline with Design specialization - elif 'design' in discipline_name: - try: - curriculum = Curriculum.objects.filter( - name__icontains='Design', - working_curriculum=True, - programme__name__icontains='M.Des', - programme__discipline=discipline_obj - ).first() - if curriculum: - return curriculum.id - # Fallback to M.Tech Design if M.Des not found - curriculum = Curriculum.objects.filter( - name__icontains='Design', - working_curriculum=True, - programme__name__icontains='M.Tech', - programme__discipline=discipline_obj - ).first() - if curriculum: - return curriculum.id - except: - pass - - # No fallback - only assign curriculum for exact specialization matches - return None - - -def get_batch_name_from_discipline(discipline_name, programme_type): - if programme_type == 'ug': - if 'design' in discipline_name.lower(): - batch_name = 'B.Des' - else: - batch_name = 'B.Tech' - elif programme_type == 'pg': - if 'design' in discipline_name.lower(): - batch_name = 'M.Des' - else: - batch_name = 'M.Tech' - else: - batch_name = programme_type.upper() - - return batch_name - -def get_or_create_discipline(discipline_name): - from applications.programme_curriculum.models import Discipline - - normalized_name = discipline_name.strip() - discipline_lower = normalized_name.lower() - - discipline_mapping = { - 'computer science and engineering': 'Computer Science and Engineering', - 'electronics and communication engineering': 'Electronics and Communication Engineering', - 'mechanical engineering': 'Mechanical Engineering', - 'smart manufacturing': 'Smart Manufacturing', - 'design': 'Design', - 'mechatronics': 'Mechatronics' - } - - database_discipline_name = None - for key, value in discipline_mapping.items(): - if key in discipline_lower: - database_discipline_name = value - break - - if not database_discipline_name: - database_discipline_name = normalized_name - - try: - discipline = Discipline.objects.get(name=database_discipline_name) - return discipline - except Discipline.DoesNotExist: - # Create new discipline as fallback - discipline = Discipline.objects.create( - name=database_discipline_name, - acronym=get_discipline_acronym(database_discipline_name) - ) - return discipline - -def get_discipline_acronym(discipline_name): - - discipline_mappings = { - 'Computer Science and Engineering': 'CSE', - 'Electronics and Communication Engineering': 'ECE', - 'Mechanical Engineering': 'ME', - 'Smart Manufacturing': 'SM', - 'Design': 'DES', - 'Mechatronics': 'MT' - } - - for full_name, acronym in discipline_mappings.items(): - if full_name.lower() in discipline_name.lower(): - return acronym - - words = discipline_name.split() - return ''.join([word[0].upper() for word in words if word])[:5] - -def create_or_update_main_student_record(student_data, batch_obj, batch_year): - - try: - roll_number = student_data.get('Institute Roll Number', '').strip() - if not roll_number: - return None - - try: - user = User.objects.get(username=roll_number) - except User.DoesNotExist: - full_name = student_data.get('Name', '') - name_parts = full_name.split() if full_name else [''] - user = User.objects.create_user( - username=roll_number, - first_name=name_parts[0] if name_parts else '', - last_name=' '.join(name_parts[1:]) if len(name_parts) > 1 else '', - email=student_data.get('Institute Email ID', ''), - password='student123' # Default password - ) - - extra_info, created = ExtraInfo.objects.get_or_create( - user=user, - defaults={ - 'id': roll_number, - 'user_type': 'student', - 'phone_no': student_data.get('Mobile No', ''), - 'address': student_data.get('Full Address', ''), - 'sex': 'M' if student_data.get('Gender', '').lower() == 'male' else 'F' if student_data.get('Gender', '').lower() == 'female' else 'O', - 'date_of_birth': None # Will be set later if date parsing works - } - ) - - dob_value = student_data.get('Date of Birth') or student_data.get('date_of_birth') or student_data.get('dob') - if dob_value: - dob = parse_date_flexible(dob_value) - if dob: - extra_info.date_of_birth = dob - extra_info.save() - - category_mapping = { - 'General': 'GEN', - 'GEN': 'GEN', - 'OBC': 'OBC', - 'SC': 'SC', - 'ST': 'ST' - } - category = category_mapping.get(student_data.get('Category', 'GEN'), 'GEN') - - current_semester = calculate_current_semester(batch_year) - - academic_student, created = AcademicStudent.objects.get_or_create( - id=extra_info, - defaults={ - 'programme': batch_obj.name, - 'batch': batch_year, - 'batch_id': batch_obj, - 'category': category, - 'father_name': student_data.get("Father's Name", ''), - 'mother_name': student_data.get("Mother's Name", ''), - 'cpi': 0.0, - 'curr_semester_no': current_semester # Dynamic semester calculation - } - ) - - return academic_student - - except Exception as e: - return None - - -# ============================================================================= -# INDIVIDUAL STUDENT CRUD OPERATIONS -# ============================================================================= - -@csrf_exempt -@require_http_methods(["GET"]) -def get_student(request, student_id): - """ - Get a single student by ID - """ - try: - student = StudentBatchUpload.objects.get(id=student_id) - - student_data = { - # Core identification - 'id': student.id, - 'jee_app_no': student.jee_app_no, - 'jeeAppNo': student.jee_app_no, - 'roll_number': student.roll_number, - 'rollNumber': student.roll_number, - 'institute_email': student.institute_email, - 'instituteEmail': student.institute_email, - - 'name': student.name, - 'father_name': student.father_name, - 'fatherName': student.father_name, - 'fname': student.father_name, # Additional alias - 'mother_name': student.mother_name, - 'motherName': student.mother_name, - 'gender': student.gender, - 'category': student.category, - 'pwd': student.pwd, - 'minority': getattr(student, 'minority', ''), - 'date_of_birth': student.date_of_birth.isoformat() if student.date_of_birth else '', - 'dateOfBirth': student.date_of_birth.isoformat() if student.date_of_birth else '', - - 'phone_number': student.phone_number, - 'phoneNumber': student.phone_number, - 'mobile': student.phone_number, # Additional alias - 'personalEmail': student.personal_email, - 'address': student.address, - 'state': student.state, - - 'branch': student.branch, - 'specialization': getattr(student, 'specialization', ''), - 'ai_rank': student.ai_rank, - 'aiRank': student.ai_rank, - 'category_rank': student.category_rank, - 'categoryRank': student.category_rank, - 'tenth_marks': student.tenth_marks, - 'tenthMarks': student.tenth_marks, - 'twelfth_marks': student.twelfth_marks, - 'twelfthMarks': student.twelfth_marks, - - 'father_occupation': student.father_occupation, - 'fatherOccupation': student.father_occupation, - 'father_mobile': student.father_mobile, - 'fatherMobile': student.father_mobile, - 'mother_occupation': student.mother_occupation, - 'motherOccupation': student.mother_occupation, - 'mother_mobile': student.mother_mobile, - 'motherMobile': student.mother_mobile, - - 'allotted_category': student.allotted_category, - 'allottedCategory': student.allotted_category, - 'allotted_gender': student.allotted_gender, - 'allottedGender': student.allotted_gender, - 'aadhar_number': student.aadhar_number, - 'aadharNumber': student.aadhar_number, - 'parent_email': getattr(student, 'parent_email', ''), - 'parentEmail': getattr(student, 'parent_email', ''), # Camel case for frontend - 'alternateEmail': getattr(student, 'personal_email', ''), - 'nationality': getattr(student, 'nationality', ''), - 'blood_group': getattr(student, 'blood_group', ''), - 'bloodGroup': getattr(student, 'blood_group', ''), # Camel case for frontend - 'blood_group_remarks': getattr(student, 'blood_group_remarks', ''), - 'bloodGroupRemarks': getattr(student, 'blood_group_remarks', ''), # Camel case for frontend - 'pwd_category': getattr(student, 'pwd_category', ''), - 'pwdCategory': getattr(student, 'pwd_category', ''), # Camel case for frontend - 'pwd_category_remarks': getattr(student, 'pwd_category_remarks', ''), - 'pwdCategoryRemarks': getattr(student, 'pwd_category_remarks', ''), # Camel case for frontend - 'admission_mode': getattr(student, 'admission_mode', ''), - 'admissionMode': getattr(student, 'admission_mode', ''), # Camel case for frontend - 'admission_mode_remarks': getattr(student, 'admission_mode_remarks', ''), - 'admissionModeRemarks': getattr(student, 'admission_mode_remarks', ''), # Camel case for frontend - 'income_group': getattr(student, 'income_group', ''), - 'incomeGroup': getattr(student, 'income_group', ''), # Camel case for frontend - 'income': getattr(student, 'income', ''), - - 'reported_status': student.reported_status, - 'reportedStatus': student.reported_status, - 'status': student.reported_status, # Additional alias - 'year': student.year, - 'programme_type': student.programme_type, - 'programmeType': student.programme_type, - 'created_at': student.created_at.isoformat() if hasattr(student, 'created_at') and student.created_at else '', - 'createdAt': student.created_at.isoformat() if hasattr(student, 'created_at') and student.created_at else '', - 'updated_at': student.updated_at.isoformat() if hasattr(student, 'updated_at') and student.updated_at else '', - 'updatedAt': student.updated_at.isoformat() if hasattr(student, 'updated_at') and student.updated_at else '', - } - - return JsonResponse({ - 'success': True, - 'student': student_data, - 'message': f'Student {student.name} retrieved successfully' - }) - - except StudentBatchUpload.DoesNotExist: - return JsonResponse({ - 'success': False, - 'message': f'Student with ID {student_id} not found' - }, status=404) - - except Exception as e: - return JsonResponse({ - 'success': False, - 'message': f'Failed to retrieve student: {str(e)}' - }, status=500) - - -@csrf_exempt -@require_http_methods(["PUT", "POST"]) -def update_student(request, student_id): - """ - Update a student by ID - """ - try: - student = StudentBatchUpload.objects.get(id=student_id) - data = json.loads(request.body) - old_discipline = student.branch - discipline_changed = False - - field_mapping = { - # Identification fields - 'jeeAppNo': 'jee_app_no', - 'rollNumber': 'roll_number', - 'instituteEmail': 'institute_email', - - # Names - 'fatherName': 'father_name', - 'fname': 'father_name', - 'mname': 'mother_name', - 'motherName': 'mother_name', - - # Dates & phones - 'dateOfBirth': 'date_of_birth', - 'dob': 'date_of_birth', - 'phoneNumber': 'phone_number', - 'mobile': 'phone_number', - - # Email / alternate - 'email': 'personal_email', - 'alternateEmail': 'personal_email', - - # Academic ranks - 'jeeRank': 'ai_rank', - 'categoryRank': 'category_rank', - 'tenthMarks': 'tenth_marks', - 'twelfthMarks': 'twelfth_marks', - - # Parents / contacts - 'fatherOccupation': 'father_occupation', - 'fatherMobile': 'father_mobile', - 'motherOccupation': 'mother_occupation', - 'motherMobile': 'mother_mobile', - 'parentEmail': 'parent_email', - 'parent_email': 'parent_email', - - # Allotment - 'allottedCategory': 'allotted_category', - 'allottedGender': 'allotted_gender', - - # New fields added - 'country': 'country', - 'nationality': 'nationality', - 'bloodGroup': 'blood_group', - 'blood_group': 'blood_group', - 'bloodGroupRemarks': 'blood_group_remarks', - 'blood_group_remarks': 'blood_group_remarks', - 'pwdCategory': 'pwd_category', - 'pwd_category': 'pwd_category', - 'pwdCategoryRemarks': 'pwd_category_remarks', - 'admissionMode': 'admission_mode', - 'admissionModeRemarks': 'admission_mode_remarks', - 'incomeGroup': 'income_group', - 'income': 'income', - - 'aadharNumber': 'aadhar_number', - 'reportedStatus': 'reported_status', - 'programmeType': 'programme_type' - } - - - jee_app_no = data.get('jeeAppNo') or data.get('jee_app_no') - roll_number = data.get('rollNumber') or data.get('roll_number') - institute_email = data.get('instituteEmail') or data.get('institute_email') - - if jee_app_no and jee_app_no != student.jee_app_no: - if StudentBatchUpload.objects.filter(jee_app_no=jee_app_no).exclude(id=student_id).exists(): - return JsonResponse({ - 'success': False, - 'message': f'JEE Application Number {jee_app_no} already exists for another student' - }, status=400) - - if roll_number and roll_number != student.roll_number: - if StudentBatchUpload.objects.filter(roll_number=roll_number).exclude(id=student_id).exists(): - return JsonResponse({ - 'success': False, - 'message': f'Roll Number {roll_number} already exists for another student' - }, status=400) - - if institute_email and institute_email != student.institute_email: - if StudentBatchUpload.objects.filter(institute_email=institute_email).exclude(id=student_id).exists(): - return JsonResponse({ - 'success': False, - 'message': f'Institute Email {institute_email} already exists for another student' - }, status=400) - - for frontend_field, backend_field in field_mapping.items(): - if frontend_field in data: - value = data[frontend_field] - if hasattr(student, backend_field): - setattr(student, backend_field, value) - - direct_fields = [ - 'name', 'gender', 'category', 'pwd', 'minority', 'address', 'state', 'branch', 'specialization', - 'personal_email', 'parent_email', 'country', 'nationality', - 'blood_group', 'blood_group_remarks', 'pwd_category', 'pwd_category_remarks', - 'admission_mode', 'admission_mode_remarks', 'income_group', 'income' - ] - - for field in direct_fields: - if field in data: - if field == 'branch' and data[field] != old_discipline: - discipline_changed = True - setattr(student, field, data[field]) - - dob_value = data.get('dob') or data.get('dateOfBirth') or data.get('date_of_birth') - if dob_value: - try: - if isinstance(dob_value, str) and dob_value.strip(): - if '/' in dob_value: - student.date_of_birth = datetime.strptime(dob_value, '%m/%d/%Y').date() - elif '-' in dob_value: - student.date_of_birth = datetime.strptime(dob_value, '%Y-%m-%d').date() - else: - student.date_of_birth = dob_value - except Exception as e: - pass - - jee_rank_value = data.get('jeeRank') or data.get('aiRank') or data.get('ai_rank') - if jee_rank_value: - try: - sanitized_rank = sanitize_rank_value(jee_rank_value) - student.ai_rank = int(sanitized_rank.replace(',', '')) if sanitized_rank.replace(',', '').isdigit() else None - except (ValueError, TypeError) as e: - student.ai_rank = None - - rank_fields = ['category_rank', 'tenth_marks', 'twelfth_marks'] - for field in rank_fields: - frontend_field = field - if field == 'category_rank': - frontend_field = 'categoryRank' - elif field == 'tenth_marks': - frontend_field = 'tenthMarks' - elif field == 'twelfth_marks': - frontend_field = 'twelfthMarks' - - # Get value from either frontend field name or backend field name - value = data.get(frontend_field, data.get(field)) - if value is None or value == '' or value == 'null': - setattr(student, field, None) - else: - try: - if field == 'category_rank': - sanitized_value = sanitize_rank_value(value) - if sanitized_value and sanitized_value.replace(',', '').isdigit(): - setattr(student, field, int(sanitized_value.replace(',', ''))) - else: - setattr(student, field, None) - else: - value_str = str(value).replace(',', '') - if value_str.isdigit(): - setattr(student, field, int(value_str)) - else: - setattr(student, field, None) - except (ValueError, TypeError): - setattr(student, field, None) - - # Update timestamp if field exists - if hasattr(student, 'updated_at'): - student.updated_at = timezone.now() - - student.save() - - # Handle discipline change using existing batch change API logic if discipline was changed - if discipline_changed and student.reported_status == 'REPORTED': - try: - from applications.academic_information.models import Student as AcademicStudent - from applications.programme_curriculum.models import Batch, Discipline - from applications.academic_procedures.models import BatchChangeHistory - - academic_student = AcademicStudent.objects.filter(id__id=student.roll_number).first() - - if academic_student: - new_discipline_obj = Discipline.objects.filter(name__iexact=student.branch).first() - if not new_discipline_obj: - discipline_upper = student.branch.upper() - if 'COMPUTER SCIENCE' in discipline_upper or 'CSE' in discipline_upper: - new_discipline_obj = Discipline.objects.filter(name__icontains='Computer Science').first() - elif 'ELECTRONICS' in discipline_upper or 'ECE' in discipline_upper: - new_discipline_obj = Discipline.objects.filter(name__icontains='Electronics').first() - elif 'MECHANICAL' in discipline_upper or 'ME' in discipline_upper: - new_discipline_obj = Discipline.objects.filter(name__icontains='Mechanical').first() - elif 'SMART' in discipline_upper or 'MANUFACTURING' in discipline_upper: - new_discipline_obj = Discipline.objects.filter(name__icontains='Smart Manufacturing').first() - elif 'DESIGN' in discipline_upper: - new_discipline_obj = Discipline.objects.filter(name__icontains='Design').first() - - if new_discipline_obj: - new_batch = Batch.objects.filter( - discipline=new_discipline_obj, - year=student.year, - running_batch=True - ).first() - - if new_batch and academic_student.batch_id != new_batch: - old_batch = academic_student.batch_id - BatchChangeHistory.objects.create( - student=academic_student, - old_batch=old_batch, - new_batch=new_batch, - ) - - academic_student.batch_id = new_batch - academic_student.save() - - # Update department and specialization - try: - from applications.globals.models import DepartmentInfo - dept_name = new_batch.discipline.acronym - department = DepartmentInfo.objects.filter(name=dept_name).first() - if department: - academic_student.id.department = department - academic_student.id.save() - academic_student.specialization = dept_name - academic_student.save() - except: - pass - - except Exception as batch_change_error: - pass - - return JsonResponse({ - 'success': True, - 'message': f'Student {student.name} updated successfully', - 'student_id': student.id, - 'discipline_changed': discipline_changed, - 'old_discipline': old_discipline if discipline_changed else None, - 'new_discipline': student.branch if discipline_changed else None - }) - - except StudentBatchUpload.DoesNotExist: - return JsonResponse({ - 'success': False, - 'message': f'Student with ID {student_id} not found' - }, status=404) - - except json.JSONDecodeError: - return JsonResponse({ - 'success': False, - 'message': 'Invalid JSON data provided' - }, status=400) - - except Exception as e: - return JsonResponse({ - 'success': False, - 'message': f'Failed to update student: {str(e)}' - }, status=500) - - -@csrf_exempt -@require_http_methods(["DELETE", "POST"]) -def delete_student(request, student_id): - """ - Delete a student from the batch upload table AND perform cascade deletion from main academic tables - URL: /programme_curriculum/api/student/{id}/delete/ - - This performs COMPLETE CASCADE DELETION: - - Removes from StudentBatchUpload (source table) - - Removes from AcademicStudent (main academic table) - - Removes from ExtraInfo (personal info table) - - Removes from User (authentication table) - """ - try: - from applications.academic_information.models import Student as AcademicStudent - from applications.globals.models import ExtraInfo - from django.contrib.auth.models import User - from django.db import transaction - - student = StudentBatchUpload.objects.get(id=student_id) - student_name = student.name - student_roll = student.roll_number - - deletion_info = { - 'id': student.id, - 'name': student_name, - 'roll_number': student_roll, - 'jee_app_no': student.jee_app_no, - 'deleted_by': getattr(request, 'user', None) and hasattr(request.user, 'username') and request.user.username or 'Unknown', - 'deleted_at': timezone.now().isoformat() - } - - deleted_records = { - 'student_batch_upload': False, - 'academic_student': False, - 'extra_info': False, - 'user_account': False - } - - # Perform CASCADE DELETION in transaction with proper error handling - try: - with transaction.atomic(): - if student_roll: - try: - academic_student = AcademicStudent.objects.get(id__id=student_roll) - academic_student.delete() - deleted_records['academic_student'] = True - except AcademicStudent.DoesNotExist: - pass - - if student_roll: - try: - extra_info = ExtraInfo.objects.get(id=student_roll) - extra_info.delete() - deleted_records['extra_info'] = True - except ExtraInfo.DoesNotExist: - pass - - user_to_delete = None - if student.user: - user_to_delete = student.user - student.user = None - student.save() - elif student_roll: - try: - user_to_delete = User.objects.get(username=student_roll) - except User.DoesNotExist: - pass - student.delete() - deleted_records['student_batch_upload'] = True - - except Exception as transaction_error: - return JsonResponse({ - 'success': False, - 'message': f'Failed to delete student records: {str(transaction_error)}' - }, status=500) - # Handle User deletion separately to avoid transaction conflicts - if user_to_delete: - try: - try: - remaining_students = StudentBatchUpload.objects.filter(user=user_to_delete) - if remaining_students.exists(): - remaining_students.update(user=None) - except Exception as cleanup_error: - pass - - with transaction.atomic(): - user_to_delete.delete() - deleted_records['user_account'] = True - - except Exception as user_deletion_error: - pass - - # Build deletion summary - deletion_summary = [] - if deleted_records['student_batch_upload']: - deletion_summary.append("StudentBatchUpload") - if deleted_records['academic_student']: - deletion_summary.append("AcademicStudent") - if deleted_records['extra_info']: - deletion_summary.append("ExtraInfo") - if deleted_records['user_account']: - deletion_summary.append("User Account") - - return JsonResponse({ - 'success': True, - 'message': f'Student {student_name} completely deleted from all systems', - 'cascade_deletion': True, - 'deleted_from': deletion_summary, - 'deleted_student': { - 'id': deletion_info['id'], - 'name': deletion_info['name'], - 'roll': deletion_info['roll_number'] - }, - 'deletion_details': deleted_records - }, status=200) - - except StudentBatchUpload.DoesNotExist: - return JsonResponse({ - 'success': False, - 'message': f'Student with ID {student_id} not found' - }, status=404) - - except Exception as e: - import traceback - error_details = traceback.format_exc() - - return JsonResponse({ - 'success': False, - 'message': f'Failed to delete student: {str(e)}', - 'error_details': str(e) - }, status=500) - - -# ============================================================================= -# BULK STATUS UPDATE FUNCTIONALITY -# ============================================================================= - -@csrf_exempt -@require_http_methods(["POST"]) -def bulk_update_student_status(request): - """ - Bulk update status for multiple students with automatic curriculum assignment - URL: /programme_curriculum/api/bulk_update_student_status/ - - Payload: { - "student_ids": [1, 2, 3], - "reported_status": "REPORTED" - } - """ - try: - data = json.loads(request.body) - student_ids = data.get('student_ids', []) - new_status = data.get('reported_status', '') - - if not student_ids: - return JsonResponse({ - 'success': False, - 'message': 'No student IDs provided' - }, status=400) - - if new_status not in ['REPORTED', 'NOT_REPORTED', 'WITHDRAWAL', 'PENDING']: - return JsonResponse({ - 'success': False, - 'message': 'Invalid status. Must be REPORTED, NOT_REPORTED, WITHDRAWAL, or PENDING' - }, status=400) - - results = [] - success_count = 0 - error_count = 0 - curriculum_assignments = {} - - for student_id in student_ids: - try: - # USE DATABASE TRANSACTION ISOLATION FOR EACH STUDENT - from django.db import transaction - with transaction.atomic(): - # Call individual update_student_status function for each student in isolation - # This ensures curriculum assignment logic is applied consistently without interference - from django.test import RequestFactory - factory = RequestFactory() - individual_data = {'studentId': student_id, 'reportedStatus': new_status} - individual_request = factory.post('/api/update/', - data=json.dumps(individual_data), - content_type='application/json') - individual_request.user = request.user - - response = update_student_status(individual_request) - response_data = json.loads(response.content.decode('utf-8')) - - if response_data.get('success'): - success_count += 1 - - # Track curriculum assignments for summary - if new_status == 'REPORTED': - transfer_message = response_data.get('data', {}).get('transfer_message', '') - if 'Curriculum:' in transfer_message: - # Extract curriculum info from transfer message - curriculum_part = transfer_message.split('Curriculum:')[1].split('|')[0].strip() - if curriculum_part not in curriculum_assignments: - curriculum_assignments[curriculum_part] = 0 - curriculum_assignments[curriculum_part] += 1 - - results.append({ - 'student_id': student_id, - 'status': 'success', - 'message': response_data.get('message', 'Status updated successfully') - }) - else: - error_count += 1 - results.append({ - 'student_id': student_id, - 'status': 'error', - 'message': response_data.get('message', 'Unknown error') - }) - - except Exception as individual_error: - error_count += 1 - results.append({ - 'student_id': student_id, - 'status': 'error', - 'message': f'Processing error: {str(individual_error)}' - }) - - # Build summary message - summary_message = f"Bulk status update completed: {success_count} successful, {error_count} failed" - if curriculum_assignments: - summary_message += f". Curriculum assignments: {dict(curriculum_assignments)}" - - return JsonResponse({ - 'success': success_count > 0, - 'message': summary_message, - 'data': { - 'total_processed': len(student_ids), - 'success_count': success_count, - 'error_count': error_count, - 'new_status': new_status, - 'curriculum_assignments': curriculum_assignments, - 'individual_results': results - } - }) - - except json.JSONDecodeError: - return JsonResponse({ - 'success': False, - 'message': 'Invalid JSON data provided' - }, status=400) - except Exception as e: - return JsonResponse({ - 'success': False, - 'message': f'Bulk update failed: {str(e)}' - }, status=500) - - -# ============================================================================= -# STUDENT TRANSFER TO MAIN TABLES -# ============================================================================= - -@csrf_exempt -@require_http_methods(["POST"]) -def bulk_transfer_students(request): - """ - Bulk transfer all reported students to main academic tables with curriculum assignment - ENHANCED: Now uses the same curriculum assignment logic as individual transfers - """ - try: - data = json.loads(request.body) - year_filter = data.get('year') - - queryset = StudentBatchUpload.objects.filter( - reported_status='REPORTED', - user__isnull=True # Not yet transferred - ) - - if year_filter: - queryset = queryset.filter(year=year_filter) - - total_count = queryset.count() - - if total_count == 0: - return JsonResponse({ - 'success': True, - 'message': 'No students found to transfer', - 'data': { - 'total_processed': 0, - 'success_count': 0, - 'error_count': 0, - 'errors': [] - } - }) - - # Use the new bulk update function to transfer all at once with curriculum assignment - student_ids = list(queryset.values_list('id', flat=True)) - - from django.test import RequestFactory - factory = RequestFactory() - bulk_data = {'student_ids': student_ids, 'reported_status': 'REPORTED'} - bulk_request = factory.post('/api/bulk_update/', - data=json.dumps(bulk_data), - content_type='application/json') - bulk_request.user = request.user - - response = bulk_update_student_status(bulk_request) - response_data = json.loads(response.content.decode('utf-8')) - - return JsonResponse({ - 'success': response_data.get('success', False), - 'message': f"Bulk transfer completed with curriculum assignment: {response_data.get('data', {}).get('success_count', 0)} students transferred successfully", - 'data': response_data.get('data', {}) - }) - - except json.JSONDecodeError: - return JsonResponse({ - 'success': False, - 'message': 'Invalid JSON data' - }, status=400) - except Exception as e: - return JsonResponse({ - 'success': False, - 'message': f'Bulk transfer failed: {str(e)}' - }, status=500) - - -@csrf_exempt -@require_http_methods(["POST"]) -def manual_transfer_student(request): - """ - Manually transfer a specific student to main academic tables - """ - try: - data = json.loads(request.body) - student_id = data.get('studentId') - - if not student_id: - return JsonResponse({ - 'success': False, - 'message': 'Student ID is required' - }, status=400) - - try: - student = StudentBatchUpload.objects.get(id=student_id) - except StudentBatchUpload.DoesNotExist: - return JsonResponse({ - 'success': False, - 'message': 'Student not found' - }, status=404) - - if student.reported_status != 'REPORTED': - return JsonResponse({ - 'success': False, - 'message': 'Student must have REPORTED status to be transferred' - }, status=400) - - if student.user: - return JsonResponse({ - 'success': False, - 'message': 'Student already transferred to main tables' - }, status=400) - - # Manual transfer not yet implemented - use status update API instead - return JsonResponse({ - 'success': False, - 'message': 'Manual transfer not yet implemented. Use the status update API to change student status to REPORTED for automatic transfer.', - 'data': { - 'student_id': student.id, - 'roll_number': student.roll_number, - 'suggestion': 'Use /programme_curriculum/api/update_student_status/ with status REPORTED' - } - }) - - except json.JSONDecodeError: - return JsonResponse({ - 'success': False, - 'message': 'Invalid JSON data' - }, status=400) - except Exception as e: - return JsonResponse({ - 'success': False, - 'message': f'Transfer failed: {str(e)}' - }, status=500) - - -@csrf_exempt -@require_http_methods(["GET"]) -def check_transfer_status(request): - """ - Check transfer status of students - how many are transferred vs pending - """ - try: - year = request.GET.get('year') - - queryset = StudentBatchUpload.objects.all() - if year: - queryset = queryset.filter(year=year) - - stats = { - 'total_students': queryset.count(), - 'reported_students': queryset.filter(reported_status='REPORTED').count(), - 'not_reported_students': queryset.filter(reported_status='NOT_REPORTED').count(), - 'transferred_students': queryset.filter(reported_status='REPORTED', user__isnull=False).count(), - 'pending_transfer': queryset.filter(reported_status='REPORTED', user__isnull=True).count(), - } - - return JsonResponse({ - 'success': True, - 'data': stats - }) - - except Exception as e: - return JsonResponse({ - 'success': False, - 'message': f'Failed to get transfer status: {str(e)}' - }, status=500) - - -@csrf_exempt -@require_http_methods(["GET"]) -def get_batch_students(request, batch_id): - """ - Get students for a specific batch - ONLY from StudentBatchUpload table - This is the primary source for upcoming batches admin management. - When students are marked as REPORTED, they get transferred to academic_information tables. - URL: /programme_curriculum/api/batches/{batch_id}/students/ - """ - try: - from applications.programme_curriculum.models import Batch - - try: - batch = Batch.objects.get(id=batch_id) - except Batch.DoesNotExist: - return JsonResponse({ - 'success': False, - 'message': f'Batch with ID {batch_id} not found' - }, status=404) - - programme_type = 'ug' if batch.name.startswith('B.') else 'pg' if batch.name.startswith('M.') else 'phd' - specialization = request.GET.get('specialization') - discipline = request.GET.get('discipline') - - students = StudentBatchUpload.objects.filter( - year=batch.year, - programme_type=programme_type - ) - - # Apply disciplinary filtering based on programme type - if programme_type == 'pg': - if specialization: - students = students.filter(specialization__icontains=specialization) - else: - - discipline_name = batch.discipline.name - discipline_filters = Q() - - discipline_filters |= Q(branch__icontains=discipline_name) - - if 'Engineering' in discipline_name: - typo_name = discipline_name.replace('Engineering', 'Enginnering') - discipline_filters |= Q(branch__icontains=typo_name) - - if 'Computer Science' in discipline_name: - discipline_filters |= Q(branch__icontains='CSE') - discipline_filters |= Q(branch__icontains='Computer Science') - elif 'Electronics and Communication' in discipline_name: - discipline_filters |= Q(branch__icontains='ECE') - discipline_filters |= Q(branch__icontains='Electronics') - elif 'Mechanical' in discipline_name: - discipline_filters |= Q(branch__icontains='ME') - discipline_filters |= Q(branch__icontains='Mechanical') - - students = students.filter(discipline_filters) - else: - discipline_name = batch.discipline.name - discipline_filters = Q() - - discipline_filters |= Q(branch__icontains=discipline_name) - - if 'Engineering' in discipline_name: - typo_name = discipline_name.replace('Engineering', 'Enginnering') - discipline_filters |= Q(branch__icontains=typo_name) - - students = students.filter(discipline_filters) - - students = students.order_by('roll_number') - - upload_students = [] - for student in students: - upload_students.append({ - 'id': student.id, - 'name': student.name, - 'roll_number': student.roll_number, - 'institute_email': student.institute_email, - 'jee_app_no': student.jee_app_no, - - 'father_name': student.father_name, - 'mother_name': getattr(student, 'mother_name', ''), - 'gender': getattr(student, 'gender', ''), - 'category': student.category, - 'pwd': getattr(student, 'pwd', ''), - 'minority': getattr(student, 'minority', ''), - 'date_of_birth': student.date_of_birth.isoformat() if student.date_of_birth else '', - - 'phone_number': getattr(student, 'phone_number', ''), - 'address': getattr(student, 'address', ''), - 'state': getattr(student, 'state', ''), - - 'branch': student.branch, - 'specialization': getattr(student, 'specialization', ''), - 'ai_rank': getattr(student, 'ai_rank', None), - 'category_rank': getattr(student, 'category_rank', None), - 'tenth_marks': getattr(student, 'tenth_marks', None), - 'twelfth_marks': getattr(student, 'twelfth_marks', None), - - 'father_occupation': getattr(student, 'father_occupation', ''), - 'father_mobile': getattr(student, 'father_mobile', ''), - 'mother_occupation': getattr(student, 'mother_occupation', ''), - 'mother_mobile': getattr(student, 'mother_mobile', ''), - 'aadhar_number': getattr(student, 'aadhar_number', ''), - - 'allotted_category': getattr(student, 'allotted_category', ''), - 'allotted_gender': getattr(student, 'allotted_gender', ''), - 'parent_email': getattr(student, 'parent_email', ''), - 'parentEmail': getattr(student, 'parent_email', ''), # Camel case for frontend - 'alternateEmail': getattr(student, 'personal_email', ''), - 'country': getattr(student, 'country', ''), - 'nationality': getattr(student, 'nationality', ''), - 'blood_group': getattr(student, 'blood_group', ''), - 'bloodGroup': getattr(student, 'blood_group', ''), # Camel case for frontend - 'blood_group_remarks': getattr(student, 'blood_group_remarks', ''), - 'bloodGroupRemarks': getattr(student, 'blood_group_remarks', ''), # Camel case for frontend - 'pwd_category': getattr(student, 'pwd_category', ''), - 'pwdCategory': getattr(student, 'pwd_category', ''), # Camel case for frontend - 'pwd_category_remarks': getattr(student, 'pwd_category_remarks', ''), - 'pwdCategoryRemarks': getattr(student, 'pwd_category_remarks', ''), # Camel case for frontend - 'admission_mode': getattr(student, 'admission_mode', ''), - 'admissionMode': getattr(student, 'admission_mode', ''), # Camel case for frontend - 'admission_mode_remarks': getattr(student, 'admission_mode_remarks', ''), - 'admissionModeRemarks': getattr(student, 'admission_mode_remarks', ''), # Camel case for frontend - 'income_group': getattr(student, 'income_group', ''), - 'incomeGroup': getattr(student, 'income_group', ''), # Camel case for frontend - 'income': getattr(student, 'income', ''), - - 'year': student.year, - 'academic_year': getattr(student, 'academic_year', ''), - 'programme_type': getattr(student, 'programme_type', ''), - 'allocation_status': getattr(student, 'allocation_status', ''), - 'reported_status': student.reported_status, - 'status_display': 'Reported' if student.reported_status == 'REPORTED' else 'Not Reported', - 'created_at': student.created_at.isoformat() if hasattr(student, 'created_at') else '', - 'updated_at': student.updated_at.isoformat() if hasattr(student, 'updated_at') else '', - 'source': getattr(student, 'source', 'unknown') # Track actual source from model - }) - - academic_students = [] - try: - from applications.academic_information.models import Student as AcademicStudent - from applications.globals.models import ExtraInfo - - academic_student_records = AcademicStudent.objects.filter( - batch=batch.year, - programme__icontains=programme_type.upper() - ) - - if batch.discipline: - academic_student_records = academic_student_records.filter( - id__department__name__icontains=batch.discipline.name - ) - - for academic_student in academic_student_records: - try: - assigned_batch = academic_student.batch_id - batch_info = { - 'name': assigned_batch.name if assigned_batch else 'No batch assigned', - 'curriculum': assigned_batch.curriculum.name if assigned_batch and assigned_batch.curriculum else 'No curriculum', - 'id': assigned_batch.id if assigned_batch else None - } - academic_students.append({ - 'id': f"academic_{academic_student.id.id}", # ExtraInfo ID - 'name': f"{academic_student.id.user.first_name} {academic_student.id.user.last_name}".strip(), - 'roll_number': academic_student.id.id, - 'institute_email': academic_student.id.user.email, - 'specialization': academic_student.specialization or '', - 'batch_assigned': batch_info, - 'programme': academic_student.programme, - 'batch_year': academic_student.batch, - 'cpi': academic_student.cpi, - 'curr_semester_no': academic_student.curr_semester_no, - 'reported_status': 'REPORTED', - 'status_display': 'Reported (Academic System)', - 'source': 'academic_system' - }) - except Exception as e: - pass - continue - - except Exception as e: - pass - - all_students = upload_students + academic_students - - response_data = { - 'success': True, - 'batch': { - 'id': batch.id, - 'name': batch.name, - 'discipline': batch.discipline.acronym, - 'year': batch.year, - 'curriculum': batch.curriculum.name if batch.curriculum else None - }, - 'students': all_students, - 'total_students': len(all_students), - 'count': len(all_students), - - 'uploadStudents': upload_students, - 'upload_students': upload_students, - 'academicStudents': academic_students, - 'academic_students': academic_students, - 'combinedBeforeDedup': all_students, - 'combined_before_dedup': all_students, - 'totalStudentsAfterDedup': len(all_students), - 'total_students_after_dedup': len(all_students), - 'totalCount': len(all_students), - 'total_count': len(all_students), - - 'data': all_students, - 'results': all_students, - 'items': all_students, - 'student_list': all_students, - 'studentList': all_students - } - - return JsonResponse(response_data) - - except Exception as e: - import traceback - return JsonResponse({ - 'success': False, - 'message': f'Failed to fetch students: {str(e)}' - }, status=500) - - -# ============================================================================= -# BATCH SYNCHRONIZATION AND WORKFLOW ENFORCEMENT -# ============================================================================= - -@api_view(['GET']) -@permission_classes([IsAuthenticated]) -def admin_batches_unified(request): - """ - UNIFIED API for both 'Batches' tab and 'Upcoming Batches' tab - Returns the same data structure for consistent frontend handling - URL: /programme_curriculum/api/admin_batches/ - """ - try: - from applications.programme_curriculum.models import Batch - - from applications.globals.models import ExtraInfo, HoldsDesignation - user_details = ExtraInfo.objects.get(user=request.user) - des = HoldsDesignation.objects.all().filter(user=request.user).first() - - if des and des.designation.name not in ['acadadmin', 'studentacadadmin']: - return JsonResponse({ - 'success': False, - 'message': 'Access denied. Only academic admins can view batch data.', - 'batches': [] - }, status=403) - - all_batches = Batch.objects.filter(running_batch=True).select_related( - 'discipline', 'curriculum' - ).order_by('-year', 'discipline__name') - - unified_batches = [] - - for batch in all_batches: - # Use centralized filled seats calculation function - actual_filled = calculate_batch_filled_seats(batch) - - if hasattr(batch, 'total_seats') and batch.total_seats: - total_seats = batch.total_seats - else: - default_seats = { - 'CSE': 300, - 'ECE': 120, - 'ME': 120, - 'SM': 60, - 'Des.': 80, - 'Design': 80 - } - total_seats = default_seats.get(batch.discipline.acronym, 100) - if hasattr(batch, 'total_seats'): - batch.total_seats = total_seats - batch.save() - - available_seats = max(0, total_seats - actual_filled) - - students_data = [] - if request.GET.get('include_students') == 'true': - try: - students = StudentBatchUpload.objects.filter( - year=batch.year, - branch__icontains=batch.discipline.name - ).order_by('roll_number') - - for student in students: - students_data.append({ - 'id': student.id, - 'name': student.name, - 'roll_number': student.roll_number, - 'institute_email': student.institute_email, - 'reported_status': student.reported_status, - 'status_display': 'Reported' if student.reported_status == 'REPORTED' else 'Not Reported' - }) - except Exception as e: - students_data = [] - - unified_batch = { - 'id': batch.id, - 'name': batch.name, - 'programme': batch.name, - - 'discipline': batch.discipline.acronym, - 'disciplineName': batch.discipline.name, - 'disciplineId': batch.discipline.id, - - 'year': batch.year, - 'batchYear': batch.year, - 'academicYear': f"{batch.year}-{str(batch.year + 1)[-2:]}", - - 'curriculum': batch.curriculum.name if batch.curriculum else None, - 'curriculumId': batch.curriculum.id if batch.curriculum else None, - 'curriculum_id': batch.curriculum.id if batch.curriculum else None, - 'curriculumVersion': batch.curriculum.version if batch.curriculum else None, - 'curriculum_display': get_batch_curriculum_display(batch), - 'available_curriculums': [ - {'id': c.id, 'name': c.name} for c in get_available_curriculums_for_batch(batch) - ], - 'hasCurriculum': True if (batch.curriculum or len(get_available_curriculums_for_batch(batch)) > 0) else False, - - 'totalSeats': total_seats, - 'total_seats': total_seats, - 'filledSeats': actual_filled, - 'filled_seats': actual_filled, - 'availableSeats': available_seats, - 'available_seats': available_seats, - 'student_count': actual_filled, - - 'running_batch': batch.running_batch, - 'status': 'READY' if (batch.curriculum or len(get_available_curriculums_for_batch(batch)) > 0) else 'NEEDS_CURRICULUM', - 'canAcceptStudents': True if (batch.curriculum or len(get_available_curriculums_for_batch(batch)) > 0) else False, - - 'students': students_data, - 'studentCount': len(students_data), - - 'displayBranch': batch.discipline.acronym, - 'fullDisciplineName': batch.discipline.name, - } - - unified_batches.append(unified_batch) - - if request.GET.get('format') == 'nested': - ug_batches = [b for b in unified_batches if b['name'].startswith('B.')] - pg_batches = [b for b in unified_batches if b['name'].startswith('M.')] - phd_batches = [b for b in unified_batches if b['name'] == 'PhD'] - - return JsonResponse({ - 'success': True, - 'data': { - 'ug': ug_batches, - 'pg': pg_batches, - 'phd': phd_batches - }, - 'batches': unified_batches, - 'message': 'Batch data fetched successfully' - }) - - response_data = { - 'success': True, - 'batches': unified_batches, - 'total_batches': len(unified_batches), - 'message': 'Unified batch data fetched successfully' - } - - return JsonResponse(response_data) - - except Exception as e: - import traceback - - return JsonResponse({ - 'success': False, - 'message': f'Failed to fetch batch data: {str(e)}', - 'batches': [], - 'error_details': str(e) - }, status=500) - - -@csrf_exempt -@require_http_methods(["GET"]) -def sync_batch_data(request): - """ - Sync total seats and curriculum between 'Batches' tab and 'Upcoming Batches' tab - URL: /programme_curriculum/api/batches/sync/ - """ - try: - from applications.programme_curriculum.models import Batch - from applications.academic_information.models import Student - - all_batches = Batch.objects.filter(running_batch=True).select_related( - 'discipline', 'curriculum' - ).order_by('year', 'discipline__name') - - sync_results = [] - - for batch in all_batches: - # Use centralized filled seats calculation function - actual_filled = calculate_batch_filled_seats(batch) - - if hasattr(batch, 'total_seats') and batch.total_seats: - available_seats = max(0, batch.total_seats - actual_filled) - else: - - default_seats = { - 'CSE': 300, - 'ECE': 120, - 'ME': 80, - 'SM': 80, - 'Des.': 80, - } - batch.total_seats = default_seats.get(batch.discipline.acronym, 100) - batch.save() - available_seats = max(0, batch.total_seats - actual_filled) - - curriculum_display = get_batch_curriculum_display(batch) - - sync_results.append({ - 'batch_id': batch.id, - 'name': batch.name, - 'discipline': batch.discipline.acronym, - 'discipline_name': batch.discipline.name, - 'year': batch.year, - 'total_seats': batch.total_seats, - 'filled_seats': actual_filled, - 'available_seats': available_seats, - 'curriculum': curriculum_display, - 'curriculum_display': curriculum_display, - 'curriculum_id': batch.curriculum.id if batch.curriculum else None, - 'status': 'READY' - }) - - return JsonResponse({ - 'success': True, - 'message': 'Batch data synchronized successfully', - 'batches': sync_results, - 'total_batches': len(sync_results) - }) - - except Exception as e: - return JsonResponse({ - 'success': False, - 'message': f'Failed to sync batch data: {str(e)}' - }, status=500) - - -@csrf_exempt -@require_http_methods(["POST"]) -def validate_batch_prerequisites(request): - """ - Validate that required batches exist before allowing student upload - Enforces workflow: Batches must be created first, then students added - URL: /programme_curriculum/api/batches/validate_prerequisites/ - """ - try: - data = json.loads(request.body) - academic_year = data.get('academic_year') # e.g., 2025 - - if not academic_year: - return JsonResponse({ - 'success': False, - 'message': 'Academic year is required' - }, status=400) - - from applications.programme_curriculum.models import Batch, Discipline - - required_disciplines = ['Computer Science and Engineering', 'Electronics and Communication Engineering', - 'Mechanical Engineering', 'Smart Manufacturing', 'Design'] - - missing_batches = [] - existing_batches = [] - - for discipline_name in required_disciplines: - try: - discipline = Discipline.objects.filter(name=discipline_name).first() - if discipline: - batch = Batch.objects.filter( - year=academic_year, - discipline=discipline, - running_batch=True - ).first() - - if batch: - existing_batches.append({ - 'discipline': discipline.name, - 'acronym': discipline.acronym, - 'batch_id': batch.id, - 'curriculum_assigned': batch.curriculum is not None, - 'curriculum_name': batch.curriculum.name if batch.curriculum else None, - 'total_seats': getattr(batch, 'total_seats', 0) - }) - else: - missing_batches.append({ - 'discipline': discipline.name, - 'acronym': discipline.acronym, - 'action_required': f'Create {discipline.acronym} batch for year {academic_year}' - }) - except Exception as e: - pass - - can_upload_students = len(missing_batches) == 0 - - return JsonResponse({ - 'success': True, - 'can_upload_students': can_upload_students, - 'academic_year': academic_year, - 'existing_batches': existing_batches, - 'missing_batches': missing_batches, - 'message': 'All required batches exist' if can_upload_students else 'Some batches are missing', - 'workflow_status': 'READY' if can_upload_students else 'BATCHES_REQUIRED' - }) - - except Exception as e: - return JsonResponse({ - 'success': False, - 'message': f'Failed to validate prerequisites: {str(e)}' - }, status=500) - - -# ============================================================================= -# BATCH CURRICULUM STATUS CHECK -# ============================================================================= - -@csrf_exempt -@require_http_methods(["GET"]) -def check_batches_curriculum_status(request): - """ - Check curriculum assignment status for all batches - Helps admins identify which batches need curriculum assignment - URL: /programme_curriculum/api/batches/curriculum_status/ - """ - try: - from applications.programme_curriculum.models import Batch - - batches = Batch.objects.filter(running_batch=True).select_related( - 'discipline', 'curriculum' - ).order_by('-year', 'discipline__name') - - batch_status = [] - - for batch in batches: - student_count = StudentBatchUpload.objects.filter( - year=batch.year, - branch__icontains=batch.discipline.name - ).count() - - reported_students = StudentBatchUpload.objects.filter( - year=batch.year, - branch__icontains=batch.discipline.name, - reported_status='REPORTED' - ).count() - - batch_info = { - 'batch_id': batch.id, - 'batch_name': batch.name, - 'discipline': batch.discipline.name, - 'discipline_acronym': batch.discipline.acronym, - 'year': batch.year, - 'total_seats': batch.total_seats, - 'student_count': student_count, - 'reported_students': reported_students, - 'has_curriculum': batch.curriculum is not None, - 'curriculum_name': batch.curriculum.name if batch.curriculum else None, - 'curriculum_id': batch.curriculum.id if batch.curriculum else None, - 'status': 'READY' if batch.curriculum else 'NEEDS_CURRICULUM', - 'warning': None if batch.curriculum else 'Curriculum assignment required before student upload/entry' - } - - batch_status.append(batch_info) - - # Summary statistics - summary = { - 'total_batches': len(batch_status), - 'batches_with_curriculum': len([b for b in batch_status if b['has_curriculum']]), - 'batches_without_curriculum': len([b for b in batch_status if not b['has_curriculum']]), - 'total_students': sum(b['student_count'] for b in batch_status), - 'reported_students': sum(b['reported_students'] for b in batch_status) - } - - return JsonResponse({ - 'success': True, - 'data': { - 'summary': summary, - 'batches': batch_status - } - }) - - except Exception as e: - return JsonResponse({ - 'success': False, - 'message': f'Failed to check batch curriculum status: {str(e)}' - }, status=500) - - -@csrf_exempt -@require_http_methods(["GET"]) -def validate_student_upload_prerequisites(request): - """ - Validate if all prerequisites are met for student data upload - URL: /programme_curriculum/api/validate_student_upload_prerequisites/ - """ - try: - academic_year_param = request.GET.get('academic_year') - if academic_year_param: - try: - batch_year, academic_year = normalize_year_input(academic_year_param) - except ValueError as e: - return JsonResponse({ - 'success': False, - 'message': f'Invalid year format: {str(e)}' - }, status=400) - else: - batch_year, academic_year = get_current_academic_year() - - from applications.programme_curriculum.models import Curriculum, Batch - - available_curriculums = Curriculum.objects.filter(working_curriculum=True) - curriculum_check = { - 'exists': available_curriculums.exists(), - 'count': available_curriculums.count(), - 'status': 'PASS' if available_curriculums.exists() else 'FAIL', - 'message': f'Found {available_curriculums.count()} working curriculums' if available_curriculums.exists() - else 'No working curriculums found - please create curriculums first' - } - - existing_batches = Batch.objects.filter(year=batch_year, running_batch=True) - batch_check = { - 'exists': existing_batches.exists(), - 'count': existing_batches.count(), - 'year': batch_year, - 'academic_year': academic_year, - 'status': 'PASS' if existing_batches.exists() else 'FAIL', - 'message': f'Found {existing_batches.count()} active batches for {academic_year}' if existing_batches.exists() - else f'No active batches found for {academic_year} - please create batches first' - } - - batches_with_curriculum = existing_batches.filter(curriculum__isnull=False) - batches_without_curriculum = existing_batches.filter(curriculum__isnull=True) - - curriculum_assignment_check = { - 'all_assigned': not batches_without_curriculum.exists(), - 'assigned_count': batches_with_curriculum.count(), - 'unassigned_count': batches_without_curriculum.count(), - 'status': 'PASS' if not batches_without_curriculum.exists() else 'FAIL', - 'message': f'All {batches_with_curriculum.count()} batches have curriculums assigned' if not batches_without_curriculum.exists() - else f'{batches_without_curriculum.count()} batches need curriculum assignment', - 'unassigned_batches': [f"{batch.name} {batch.discipline.acronym}" for batch in batches_without_curriculum] - } - - overall_status = 'PASS' if (curriculum_check['status'] == 'PASS' and - batch_check['status'] == 'PASS' and - curriculum_assignment_check['status'] == 'PASS') else 'FAIL' - - validation_result = { - 'overall_status': overall_status, - 'can_upload_students': overall_status == 'PASS', - 'academic_year': academic_year, - 'batch_year': batch_year, - 'checks': { - 'curriculum_exists': curriculum_check, - 'batches_exist': batch_check, - 'curriculums_assigned': curriculum_assignment_check - }, - 'requirements': { - 'step_1': 'Create working curriculums', - 'step_2': 'Create batches for the academic year', - 'step_3': 'Assign curriculums to all batches', - 'step_4': 'Upload/enter student data' - } - } - - return JsonResponse({ - 'success': True, - 'data': validation_result - }) - - except Exception as e: - return JsonResponse({ - 'success': False, - 'message': f'Failed to validate prerequisites: {str(e)}' - }, status=500) - - -# ============================================================================= -# CURRICULUM REDUNDANCY CLEANUP UTILITIES -# ============================================================================= - -@csrf_exempt -@require_http_methods(["GET"]) -def find_duplicate_curriculums(request): - """ - Find duplicate curriculums that have the same name and programme - URL: /programme_curriculum/api/find_duplicate_curriculums/ - """ - try: - from applications.programme_curriculum.models import Curriculum - from django.db.models import Count - - duplicates = (Curriculum.objects - .values('name', 'programme__name', 'programme__id') - .annotate(count=Count('id')) - .filter(count__gt=1, working_curriculum=True) - .order_by('name')) - - duplicate_details = [] - for dup in duplicates: - curriculum_name = dup['name'] - programme_name = dup['programme__name'] - programme_id = dup['programme__id'] - count = dup['count'] - - curricula = Curriculum.objects.filter( - name=curriculum_name, - programme__id=programme_id, - working_curriculum=True - ).order_by('version') - - curriculum_list = [] - for curr in curricula: - batch_count = curr.batches.count() - curriculum_list.append({ - 'id': curr.id, - 'version': str(curr.version), - 'latest_version': curr.latest_version, - 'batch_count': batch_count, - 'created_date': curr.id - }) - - duplicate_details.append({ - 'curriculum_name': curriculum_name, - 'programme_name': programme_name, - 'programme_id': programme_id, - 'duplicate_count': count, - 'curricula': curriculum_list - }) - - return JsonResponse({ - 'success': True, - 'total_duplicate_groups': len(duplicate_details), - 'duplicates': duplicate_details - }) - - except Exception as e: - return JsonResponse({ - 'success': False, - 'message': f'Failed to find duplicate curriculums: {str(e)}' - }, status=500) - - -@csrf_exempt -@require_http_methods(["POST"]) -def consolidate_duplicate_curriculums(request): - """ - Consolidate duplicate curriculums by keeping the latest version and reassigning batches - URL: /programme_curriculum/api/consolidate_duplicate_curriculums/ - """ - try: - data = json.loads(request.body) - curriculum_name = data.get('curriculum_name') - programme_id = data.get('programme_id') - keep_curriculum_id = data.get('keep_curriculum_id') - - if not all([curriculum_name, programme_id, keep_curriculum_id]): - return JsonResponse({ - 'success': False, - 'message': 'Missing required fields: curriculum_name, programme_id, keep_curriculum_id' - }, status=400) - - from applications.programme_curriculum.models import Curriculum, Batch - - try: - keep_curriculum = Curriculum.objects.get(id=keep_curriculum_id) - except Curriculum.DoesNotExist: - return JsonResponse({ - 'success': False, - 'message': f'Curriculum with ID {keep_curriculum_id} not found' - }, status=400) - - duplicate_curriculums = Curriculum.objects.filter( - name=curriculum_name, - programme__id=programme_id, - working_curriculum=True - ).exclude(id=keep_curriculum_id) - - reassigned_batches = 0 - for dup_curriculum in duplicate_curriculums: - batches = dup_curriculum.batches.all() - for batch in batches: - batch.curriculum = keep_curriculum - batch.save() - reassigned_batches += 1 - - duplicate_count = duplicate_curriculums.count() - duplicate_curriculums.update(working_curriculum=False, latest_version=False) - - keep_curriculum.latest_version = True - keep_curriculum.save() - - return JsonResponse({ - 'success': True, - 'message': f'Consolidated {duplicate_count} duplicate curriculums. Reassigned {reassigned_batches} batches to "{keep_curriculum.name} v{keep_curriculum.version}".', - 'kept_curriculum': { - 'id': keep_curriculum.id, - 'name': keep_curriculum.name, - 'version': str(keep_curriculum.version) - }, - 'removed_duplicates': duplicate_count, - 'reassigned_batches': reassigned_batches - }) - - except Exception as e: - return JsonResponse({ - 'success': False, - 'message': f'Failed to consolidate curriculums: {str(e)}' - }, status=500) - - -# ============================================================================= -# BATCH REDUNDANCY CLEANUP UTILITIES -# ============================================================================= - -@csrf_exempt -@require_http_methods(["GET"]) -def find_duplicate_batches(request): - """ - Find duplicate batches that have the same programme+discipline+year combination - URL: /programme_curriculum/api/find_duplicate_batches/ - """ - try: - from applications.programme_curriculum.models import Batch - from django.db.models import Count - - duplicates = (Batch.objects - .values('name', 'discipline__name', 'discipline__acronym', 'discipline__id', 'year') - .annotate(count=Count('id')) - .filter(count__gt=1, running_batch=True) - .order_by('year', 'discipline__name')) - - duplicate_details = [] - for dup in duplicates: - programme_name = dup['name'] - discipline_name = dup['discipline__name'] - discipline_acronym = dup['discipline__acronym'] - discipline_id = dup['discipline__id'] - year = dup['year'] - count = dup['count'] - - batches = Batch.objects.filter( - name=programme_name, - discipline__id=discipline_id, - year=year, - running_batch=True - ).order_by('id') - - batch_list = [] - for batch in batches: - try: - student_count = StudentBatchUpload.objects.filter( - year=batch.year, - branch__icontains=batch.discipline.name - ).count() - except: - student_count = 0 - - batch_list.append({ - 'id': batch.id, - 'total_seats': batch.total_seats, - 'student_count': student_count, - 'curriculum': batch.curriculum.name if batch.curriculum else None, - 'curriculum_id': batch.curriculum.id if batch.curriculum else None, - 'created_order': batch.id - }) - - duplicate_details.append({ - 'programme_name': programme_name, - 'discipline_name': discipline_name, - 'discipline_acronym': discipline_acronym, - 'discipline_id': discipline_id, - 'year': year, - 'duplicate_count': count, - 'batches': batch_list - }) - - return JsonResponse({ - 'success': True, - 'total_duplicate_groups': len(duplicate_details), - 'duplicates': duplicate_details - }) - - except Exception as e: - return JsonResponse({ - 'success': False, - 'message': f'Failed to find duplicate batches: {str(e)}' - }, status=500) - - -@csrf_exempt -@require_http_methods(["POST"]) -def consolidate_duplicate_batches(request): - """ - Consolidate duplicate batches by keeping one and reassigning students/data - URL: /programme_curriculum/api/consolidate_duplicate_batches/ - """ - try: - data = json.loads(request.body) - programme_name = data.get('programme_name') - discipline_id = data.get('discipline_id') - year = data.get('year') - keep_batch_id = data.get('keep_batch_id') - - if not all([programme_name, discipline_id, year, keep_batch_id]): - return JsonResponse({ - 'success': False, - 'message': 'Missing required fields: programme_name, discipline_id, year, keep_batch_id' - }, status=400) - - from applications.programme_curriculum.models import Batch - - try: - keep_batch = Batch.objects.get(id=keep_batch_id) - except Batch.DoesNotExist: - return JsonResponse({ - 'success': False, - 'message': f'Batch with ID {keep_batch_id} not found' - }, status=400) - - duplicate_batches = Batch.objects.filter( - name=programme_name, - discipline__id=discipline_id, - year=year, - running_batch=True - ).exclude(id=keep_batch_id) - - reassigned_students = 0 - total_seats_added = 0 - - for dup_batch in duplicate_batches: - if dup_batch.total_seats > 0: - keep_batch.total_seats += dup_batch.total_seats - total_seats_added += dup_batch.total_seats - - if dup_batch.curriculum and not keep_batch.curriculum: - keep_batch.curriculum = dup_batch.curriculum - - try: - from applications.academic_information.models import Student - students = Student.objects.filter(batch_id=dup_batch) - for student in students: - student.batch_id = keep_batch - student.save() - reassigned_students += 1 - except: - pass - - keep_batch.save() - - duplicate_count = duplicate_batches.count() - duplicate_batches.update(running_batch=False) - - return JsonResponse({ - 'success': True, - 'message': f'Consolidated {duplicate_count} duplicate batches into "{keep_batch.name} {keep_batch.discipline.acronym} {keep_batch.year}". Added {total_seats_added} seats, reassigned {reassigned_students} students.', - 'kept_batch': { - 'id': keep_batch.id, - 'name': keep_batch.name, - 'discipline': keep_batch.discipline.acronym, - 'year': keep_batch.year, - 'total_seats': keep_batch.total_seats, - 'curriculum': keep_batch.curriculum.name if keep_batch.curriculum else None - }, - 'removed_duplicates': duplicate_count, - 'total_seats_added': total_seats_added, - 'reassigned_students': reassigned_students - }) - - except Exception as e: - return JsonResponse({ - 'success': False, - 'message': f'Failed to consolidate batches: {str(e)}' - }, status=500) - - -@csrf_exempt -@require_http_methods(["POST"]) -def fix_stuck_reported_students(request): - """ - Fix students that are stuck in REPORTED status and should be transferred to academic system - """ - try: - stuck_students = StudentBatchUpload.objects.filter( - reported_status='REPORTED' - ).exclude( - Q(first_name__isnull=True) | Q(first_name='') | - Q(last_name__isnull=True) | Q(last_name='') | - Q(roll_number__isnull=True) | Q(roll_number='') - ) - - if not stuck_students.exists(): - return JsonResponse({ - 'success': True, - 'message': 'No stuck students found', - 'data': { - 'total_stuck': 0, - 'fixed_count': 0, - 'error_count': 0, - 'errors': [] - } - }) - - fixed_count = 0 - errors = [] - - with transaction.atomic(): - for student in stuck_students: - try: - # Transfer student to academic system - transfer_result = transfer_student_to_academic_system(student) - - if transfer_result.get('success'): - # Update status to transferred - student.reported_status = 'TRANSFERRED' - student.save() - fixed_count += 1 - else: - errors.append(f"Student {student.first_name} {student.last_name} ({student.roll_number}): {transfer_result.get('message', 'Unknown error')}") - - except Exception as e: - errors.append(f"Student {student.first_name} {student.last_name} ({student.roll_number}): {str(e)}") - - return JsonResponse({ - 'success': True, - 'message': f'Fixed {fixed_count} stuck students', - 'data': { - 'total_stuck': stuck_students.count(), - 'fixed_count': fixed_count, - 'error_count': len(errors), - 'errors': errors[:10] - } - }) - - except Exception as e: - return JsonResponse({ - 'success': False, - 'message': f'Failed to fix stuck students: {str(e)}' - }, status=500) - - -def transfer_student_to_academic_system(student): - """ - Transfer a student from StudentBatchUpload to the academic_information.Student table - This function handles the complete transfer process including User and ExtraInfo creation - """ - try: - from applications.academic_information.models import Student as AcademicStudent - from applications.globals.models import ExtraInfo, Designation, HoldsDesignation - from django.contrib.auth.models import User - - existing_academic_student = AcademicStudent.objects.filter( - id__id=student.roll_number - ).first() - - if existing_academic_student: - return { - 'success': True, - 'message': 'Student already exists in academic system', - 'user_id': existing_academic_student.id.user.id, - 'extra_info_id': existing_academic_student.id.id, - 'academic_student_id': existing_academic_student.id.id - } - - existing_user = User.objects.filter(username=student.roll_number).first() - - if existing_user: - user = existing_user - else: - user = User.objects.create_user( - username=student.roll_number, - email=student.institute_email, - first_name=student.first_name, - last_name=student.last_name, - password=student.password if student.password else 'changeme123' - ) - - existing_extra_info = ExtraInfo.objects.filter(id=student.roll_number).first() - - if existing_extra_info: - extra_info = existing_extra_info - else: - extra_info = ExtraInfo.objects.create( - id=student.roll_number, - user=user, - title='Mr' if student.gender == 'M' else 'Ms', - sex='M' if student.gender == 'M' else 'F', - date_of_birth=student.date_of_birth, - user_type='student', - address=student.permanent_address or '', - phone_no=student.phone_number or '', - department=student.batch.discipline if student.batch else None, - profile_picture=None - ) - - academic_student = AcademicStudent.objects.create( - id=extra_info, - programme=student.programme, - discipline=student.batch.discipline if student.batch else None, - batch=student.batch, - current_semester=calculate_current_semester(student.academic_year), - current_year=student.academic_year, - cpi=0.0, - category=student.category or 'General', - father_name=student.father_name or '', - mother_name=student.mother_name or '', - hall_no=0, - room_no='', - specialization=student.specialization or '' - ) - - student_designation = Designation.objects.filter(name='student').first() - if student_designation: - holds_designation, created = HoldsDesignation.objects.get_or_create( - user=user, - designation=student_designation, - defaults={'working': user} - ) - - return { - 'success': True, - 'message': 'Student transferred successfully', - 'user_id': user.id, - 'extra_info_id': extra_info.id, - 'academic_student_id': academic_student.id.id - } - - except Exception as e: - import traceback - return { - 'success': False, - 'message': f'Transfer failed: {str(e)}' - } - - -@csrf_exempt -@require_http_methods(["POST"]) -def sync_batches_to_configuration(request): - """ - Sync batches from Batch model to BatchConfiguration model - This ensures that all batches are available in the BatchConfiguration model - """ - try: - from applications.programme_curriculum.models import Batch - - synced_count = 0 - created_count = 0 - updated_count = 0 - - for batch in Batch.objects.all(): - # Check if BatchConfiguration already exists with this ID - try: - batch_config = BatchConfiguration.objects.get(id=batch.id) - # Update existing - batch_config.programme = batch.name - batch_config.discipline = batch.discipline.name if batch.discipline else "Unknown" - batch_config.year = batch.year - batch_config.total_seats = batch.total_seats - batch_config.save() - updated_count += 1 - - except BatchConfiguration.DoesNotExist: - # Create new - batch_config = BatchConfiguration.objects.create( - programme=batch.name, - discipline=batch.discipline.name if batch.discipline else "Unknown", - year=batch.year, - total_seats=batch.total_seats - ) - # Force the same ID to maintain consistency - batch_config.id = batch.id - batch_config.save() - created_count += 1 - - synced_count += 1 - - return JsonResponse({ - 'success': True, - 'message': f'Synced {synced_count} batches. Created: {created_count}, Updated: {updated_count}', - 'stats': { - 'total_synced': synced_count, - 'created': created_count, - 'updated': updated_count - } - }) - - except Exception as e: - return JsonResponse({ - 'success': False, - 'message': f'Sync failed: {str(e)}' - }, status=500) - - -def ensure_default_curriculum_exists(discipline_obj, programme_name): - """ - Ensure that a default curriculum exists for the given discipline and programme. - This function creates a basic curriculum if none exists. - - Args: - discipline_obj: Discipline instance - programme_name: Programme name (e.g., 'M.Tech', 'B.Tech') - - Returns: - curriculum_id: ID of the curriculum (existing or newly created), or None if failed - """ - try: - from applications.programme_curriculum.models import Curriculum, Programme - - # First, try to find an existing curriculum - existing_curriculum = Curriculum.objects.filter( - programme__name__icontains=programme_name, - programme__discipline=discipline_obj, - working_curriculum=True - ).first() - - if existing_curriculum: - return existing_curriculum.id - - # If no curriculum exists, create a default one - # First, ensure the programme exists - programme_category = 'PG' if programme_name in ['M.Tech', 'M.Des'] else ('UG' if programme_name in ['B.Tech', 'B.Des'] else 'PHD') - - programme, created = Programme.objects.get_or_create( - name=programme_name, - category=programme_category, - defaults={'programme_begin_year': 2024} - ) - - # Add discipline to programme if not already associated - if not discipline_obj.programmes.filter(id=programme.id).exists(): - discipline_obj.programmes.add(programme) - - # Create a default curriculum - default_curriculum = Curriculum.objects.create( - programme=programme, - name=f"{programme_name} {discipline_obj.acronym} Default Curriculum", - version=1.0, - working_curriculum=True, - no_of_semester=8 if programme_category in ['UG', 'PG'] else 12, - min_credit=120 if programme_category == 'UG' else (60 if programme_category == 'PG' else 180) - ) - - # Log the creation for audit purposes - import logging - pass - - return default_curriculum.id - - except Exception as e: - # Log the error but don't raise it to avoid breaking the student reporting process - pass - return None diff --git a/FusionIIIT/applications/programme_curriculum/filters.py b/FusionIIIT/applications/programme_curriculum/filters.py index 6499607f8..2959c61b9 100644 --- a/FusionIIIT/applications/programme_curriculum/filters.py +++ b/FusionIIIT/applications/programme_curriculum/filters.py @@ -1,14 +1,12 @@ import django_filters from django import forms -from django.db.models import Q -from .models import Programme, Discipline, Curriculum, Semester, Course, Batch, CourseSlot, PROGRAMME_CATEGORY_CHOICES,CourseInstructor +from .models import Programme, Discipline, Curriculum, Semester, Course, Batch, CourseSlot, PROGRAMME_CATEGORY_CHOICES class CourseFilter(django_filters.FilterSet): class Meta: model = Course fields = {'code': ['icontains'], 'name': ['icontains'], - 'version': ['exact'], 'working_course': ['exact'], 'disciplines': ['exact'], } @@ -16,7 +14,6 @@ class Meta: 'code' : django_filters.CharFilter(forms.TextInput(attrs={'placeholder': 'Course Code','max_length': 10,})), 'name' : forms.TextInput(attrs={'placeholder': 'Course/Project Name','max_length': 100,}), 'name' : django_filters.CharFilter(forms.TextInput(attrs={'placeholder': 'Course/Project Name','max_length': 100,})), - 'code' : django_filters.CharFilter(forms.TextInput(attrs={'placeholder': 'Version','max_length': 10,})), 'working_course' : django_filters.CharFilter(forms.CheckboxInput(attrs={'class': 'ui checkbox'})), 'disciplines' : django_filters.CharFilter(forms.SelectMultiple(attrs={'class':'ui fluid search selection dropdown',})), } @@ -35,40 +32,4 @@ class CurriculumFilter(django_filters.FilterSet): class Meta: model = Curriculum fields = {'name': ['icontains'], - 'version': ['exact'], - } - widgets = { - 'name' : forms.TextInput(attrs={'placeholder': 'Course/Project Name','max_length': 100,}), - 'name' : django_filters.CharFilter(forms.TextInput(attrs={'placeholder': 'Course/Project Name','max_length': 100,})), - 'code' : django_filters.CharFilter(forms.TextInput(attrs={'placeholder': 'Version','max_length': 10,})), - } - -class CourseInstructorFilter(django_filters.FilterSet): - course_id = django_filters.CharFilter( - field_name='course_id__code', - lookup_expr='icontains', - label='Course Code' - ) - - instructor_name = django_filters.CharFilter( - method='filter_instructor_name', - label='Instructor Name' - ) - - year = django_filters.NumberFilter( - field_name='year', - lookup_expr='exact', - label='Year' - ) - class Meta: - model = CourseInstructor - fields = ['course_id', 'instructor_name', 'year'] - - def filter_instructor_name(self, queryset, name, value): - """ - Custom filter method to filter by first_name and last_name. - """ - return queryset.filter( - Q(instructor_id__id__user__first_name__icontains=value) | - Q(instructor_id__id__user__last_name__icontains=value) - ) + } \ No newline at end of file diff --git a/FusionIIIT/applications/programme_curriculum/forms.py b/FusionIIIT/applications/programme_curriculum/forms.py index 49189b282..72ccfa2b2 100644 --- a/FusionIIIT/applications/programme_curriculum/forms.py +++ b/FusionIIIT/applications/programme_curriculum/forms.py @@ -3,14 +3,8 @@ from django.forms import ModelForm, widgets from django.forms import Form, ValidationError from django.forms.models import ModelChoiceField -from .models import Programme, Discipline, Curriculum, Semester, Course, Batch, CourseSlot, PROGRAMME_CATEGORY_CHOICES,NewProposalFile,Proposal_Tracking, CourseInstructor +from .models import Programme, Discipline, Curriculum, Semester, Course, Batch, CourseSlot, PROGRAMME_CATEGORY_CHOICES from django.utils.translation import gettext_lazy as _ -from django.contrib.auth.models import User -from applications.globals.models import (DepartmentInfo, Designation,ExtraInfo, Faculty, HoldsDesignation) -from applications.filetracking.sdk.methods import * -from django.db.models import Q -from datetime import datetime -from django.db.models import Max class ProgrammeForm(ModelForm): class Meta: @@ -24,7 +18,8 @@ class Meta: 'category' : 'Programme Category', 'name': 'Programme Name' } - + + class DisciplineForm(ModelForm): class Meta: model = Discipline @@ -40,20 +35,6 @@ class Meta: 'programmes': 'Link Programmes to this Disciplines', 'acronym' : 'Enter Acronym' } - def __init__(self, *args, **kwargs): - super(DisciplineForm, self).__init__(*args, **kwargs) - - # Get the current discipline instance - discipline = kwargs.get('instance', None) - - if discipline: - # Show programmes that are either unlinked or linked to the current discipline - self.fields['programmes'].queryset = Programme.objects.filter( - Q(discipline__isnull=True) | Q(discipline=discipline) - ) - else: - # Show only programmes that are unlinked (no discipline assigned) - self.fields['programmes'].queryset = Programme.objects.filter(discipline__isnull=True) class CurriculumForm(ModelForm): @@ -63,7 +44,7 @@ class Meta: widgets = { 'name' : forms.TextInput(attrs={'placeholder': 'Enter New Curriculum Name','max_length': 70,'class':'field'}), 'programme' : forms.Select(attrs={'class':'ui fluid search selection dropdown',}), - 'version' : forms.NumberInput(attrs={'placeholder': 'Enter the latest version',' class': 'field','min': '1.0'}, ), + 'version' : forms.NumberInput(attrs={'placeholder': 'Enter the latest version',' class': 'field'}, ), 'working_curriculum' : forms.CheckboxInput(attrs={'class': 'ui checkbox'}), 'no_of_semester' : forms.NumberInput(attrs={'placeholder': 'Enter the number of semesters',' class': 'field'}, ), 'min_credit' : forms.NumberInput(attrs={'placeholder': 'Minimum Number of Credits',' class': 'field'}, ), @@ -124,23 +105,16 @@ class CourseForm(ModelForm): def clean(self): cleaned_data = super().clean() - # Get percentage fields with default values of 0 if None or missing - percent_quiz_1 = cleaned_data.get("percent_quiz_1") or 0 - percent_midsem = cleaned_data.get("percent_midsem") or 0 - percent_quiz_2 = cleaned_data.get("percent_quiz_2") or 0 - percent_endsem = cleaned_data.get("percent_endsem") or 0 - percent_project = cleaned_data.get("percent_project") or 0 - percent_lab_evaluation = cleaned_data.get("percent_lab_evaluation") or 0 - percent_course_attendance = cleaned_data.get("percent_course_attendance") or 0 - percentages_sum = ( - percent_quiz_1 + percent_midsem + percent_quiz_2 + - percent_endsem + percent_project + percent_lab_evaluation + - percent_course_attendance + cleaned_data.get("percent_quiz_1") + + cleaned_data.get("percent_midsem") + + cleaned_data.get("percent_quiz_2") + + cleaned_data.get("percent_endsem") + + cleaned_data.get("percent_project") + + cleaned_data.get("percent_lab_evaluation") + + cleaned_data.get("percent_course_attendance") ) - - # credits = cleaned_data.get("credit") - + if percentages_sum != 100: msg = 'Percentages must add up to 100%, they currently add up to ' + str(percentages_sum) + '%' self.add_error('percent_quiz_1', msg) @@ -150,18 +124,15 @@ def clean(self): self.add_error('percent_project', msg) self.add_error('percent_lab_evaluation', msg) self.add_error('percent_course_attendance', msg) - - # if credits==0: - # msg2="Credits can't be zero" - # self.add_error('credits', msg2) + return cleaned_data + class Meta: model = Course fields = '__all__' widgets = { 'code' : forms.TextInput(attrs={'placeholder': 'Course Code','max_length': 10,}), 'name' : forms.TextInput(attrs={'placeholder': 'Course/Project Name','max_length': 100,}), - 'version' : forms.NumberInput(attrs={'placeholder': 'version_no'}, ), 'credit' : forms.NumberInput(attrs={'placeholder': 'Course Credits',}, ), 'lecture_hours' : forms.NumberInput(attrs={'placeholder': 'Lecture hours',}, ), 'tutorial_hours' : forms.NumberInput(attrs={'placeholder': 'Tutorial hours',}, ), @@ -181,12 +152,10 @@ class Meta: 'percent_project' : forms.NumberInput(attrs={'placeholder': '%'}, ), 'percent_lab_evaluation' : forms.NumberInput(attrs={'placeholder': '%'}, ), 'percent_course_attendance' : forms.NumberInput(attrs={'placeholder': '%'}, ), - 'max_seats' : forms.NumberInput(attrs={'placeholder': 'max_seats',}, ), } labels = { 'code' : 'Course Code', 'name' : 'Course Name', - 'version':'version', 'credit' : 'Credits', 'lecture_hours' : 'Academic Loads', 'tutorial_hours' : '', @@ -205,8 +174,7 @@ class Meta: 'percent_lab_evaluation' : 'percent_lab_evaluation', 'percent_course_attendance' : 'percent_course_attendance', 'working_course' : 'working_course', - 'disciplines' : 'disciplines', - 'max_seats' : 'max_seats' + 'disciplines' : 'disciplines' } @@ -226,14 +194,6 @@ class Meta: 'year' : 'Batch Year', 'curriculum' : 'Select Curriculum For Batch Students', } - def __init__(self, *args, **kwargs): - super(BatchForm, self).__init__(*args, **kwargs) - - available_curriculums = Curriculum.objects.filter(working_curriculum=True) - - - self.fields['curriculum'].queryset = available_curriculums - self.fields['curriculum'].empty_label = "Select Curriculum" # This adds a blank option with a label class CourseSlotForm(ModelForm): @@ -259,186 +219,4 @@ class Meta: 'duration' : "Course/Project Duration", 'min_registration_limit': 'Min Course Slot Registration Limit', 'max_registration_limit': 'Max Course Slot Registration Limit', - } - - - -#new -class NewCourseProposalFile(ModelForm): - - class Meta: - model = NewProposalFile - fields = '__all__' - widgets = { - 'code' : forms.TextInput(attrs={'placeholder': 'Course Code','max_length': 10,}), - 'name' : forms.TextInput(attrs={'placeholder': 'Course/Project Name','max_length': 100,}), - 'credit' : forms.NumberInput(attrs={'placeholder': 'Course Credits',}, ), - 'lecture_hours' : forms.NumberInput(attrs={'placeholder': 'Lecture hours',}, ), - 'tutorial_hours' : forms.NumberInput(attrs={'placeholder': 'Tutorial hours',}, ), - 'pratical_hours' : forms.NumberInput(attrs={'placeholder': 'Practical hours',}, ), - 'discussion_hours' : forms.NumberInput(attrs={'placeholder': 'Group Discussion hours',}, ), - 'project_hours' : forms.NumberInput(attrs={'placeholder': 'Project hours',}, ), - 'pre_requisits' : forms.Textarea(attrs={'placeholder': 'Text','class':'field'}), - 'pre_requisit_courses' : forms.SelectMultiple(attrs={'class':'ui fluid search selection dropdown',}), - 'syllabus' : forms.Textarea(attrs={'placeholder': 'Text','class':'field'}), - 'ref_books' : forms.Textarea(attrs={'placeholder': 'Text','class':'field'}), - 'percent_quiz_1' : forms.NumberInput(attrs={'placeholder': '%'}, ), - 'percent_midsem' : forms.NumberInput(attrs={'placeholder': '%'}, ), - 'percent_quiz_2' : forms.NumberInput(attrs={'placeholder': '%'}, ), - 'percent_endsem' : forms.NumberInput(attrs={'placeholder': '%'}, ), - 'percent_project' : forms.NumberInput(attrs={'placeholder': '%'}, ), - 'percent_lab_evaluation' : forms.NumberInput(attrs={'placeholder': '%'}, ), - 'percent_course_attendance' : forms.NumberInput(attrs={'placeholder': '%'}, ), - 'uploader' : forms.TextInput(attrs={'readonly':'readonly'},), - 'designation' : forms.TextInput(attrs={'readonly':'readonly'},), - 'subject' : forms.Textarea(attrs={'placeholder': 'Subject','class':'field',}), - 'description' : forms.Textarea(attrs={'placeholder': 'Description','class':'field',}), - 'upload_date' : forms.TextInput(), - 'max_seats' : forms.NumberInput(attrs={'placeholder': '',}, ), - - } - labels = { - 'code' : 'Course Code', - 'name' : 'Course Name', - 'credit' : 'Credits', - 'lecture_hours' : 'Academic Loads', - 'tutorial_hours' : '', - 'pratical_hours' : '', - 'discussion_hours' : '', - 'project_hours' : '', - 'pre_requisits' : 'Pre-requisits', - 'pre_requisit_courses' : 'Pre-requisit Courses', - 'syllabus' : 'Syllabus', - 'ref_books' : 'References & Books', - 'percent_quiz_1' : 'percent_quiz_1', - 'percent_midsem' : 'percent_midsem', - 'percent_quiz_2' : 'percent_quiz_2', - 'percent_endsem' : 'percent_endsem', - 'percent_project' : 'percent_project', - 'percent_lab_evaluation' : 'percent_lab_evaluation', - 'percent_course_attendance' : 'percent_course_attendance', - 'uploader' : 'Uploader name', - 'designation' : 'Uploader design', - 'subject' : 'title', - 'description' : 'Description', - 'upload_date' : '', - 'max_seats':'' - - } - - -class CourseProposalTrackingFile(ModelForm): - - class Meta: - model = Proposal_Tracking - fields = '__all__' - widgets = { - 'file_id' :forms.NumberInput(attrs={'placeholder': 'Course Proposal id','readonly':'readonly'}, ), - 'current_id' : forms.TextInput(attrs={'placeholder': 'Enter Uploader','class':'ui fluid search selection dropdown','readonly':'readonly'},), - 'current_design' : forms.TextInput(attrs={'class':'ui fluid search selection dropdown','readonly':'readonly'},), - 'receive_id' : forms.Select(attrs={'class':'ui fluid search selection dropdown',}), - 'receive_design' : forms.Select(attrs={'class':'ui fluid search selection dropdown',}), - 'disciplines' : forms.Select(attrs={'class':'ui fluid search selection dropdown',}), - 'remarks' : forms.Textarea(attrs={'placeholder': 'Remarks','class':'field',}), - } - labels = { - 'file_id' : 'file_id', - 'current_id' : 'Uploader name', - 'current_design' : 'Uploader design', - 'receive_id' : 'receiver name', - 'receive_design' : 'receiver design', - 'disciplines':'disciplines', - 'remarks' : 'remarks', - - } - - def clean(self): - - r_id = self.cleaned_data.get('receive_id') - r_des = self.cleaned_data.get('receive_design') - des=HoldsDesignation.objects.filter(user=r_id) - print(des) - data2='' - msg1='' - if des: - data2 = ', '.join(str(i) for i in des) - msg1 = f'{r_id} has only these working designations: {data2}' - - else: - msg1=f'{r_id} has no working designations' - data = HoldsDesignation.objects.select_related('designation').filter(user=r_id,designation=r_des) - - if not data: - msg = 'Invalid reciever id and reciever designation' - raise ValidationError({'receive_id': [msg, msg1]}) - - name="" - name = name+str(r_des) - if "hod" in name.lower() : - pass - elif "professor" in name.lower() : - pass - elif "dean academic" in name.lower(): - pass - else: - msg3 = f"You can't send Proposal Form to the user {r_id}-{r_des}" - raise ValidationError({'receive_id': [msg3]}) - - - - - return self.cleaned_data - -class CourseInstructorForm(forms.ModelForm): - # next_year = datetime.now().year +1 - max_year = Batch.objects.aggregate(max_year=Max('year'))['max_year'] - next_year = max_year + 1 if max_year else datetime.now().year + 1 - course_id = forms.ModelChoiceField( - queryset=Course.objects.all(), - label="Select Course", - empty_label="Choose a course", - widget=forms.Select(attrs={'class': 'ui fluid search selection dropdown'}) - ) - instructor_id = forms.ModelChoiceField( - # queryset=ExtraInfo.objects.filter(user_type='faculty'), - queryset = Faculty.objects.all(), - label="Select Instructor", - empty_label="Choose an instructor", - widget=forms.Select(attrs={'class': 'ui fluid search selection dropdown'}) - ) - - year = forms.ChoiceField( - choices=[('', 'Choose a year')] + [(year, year) for year in Batch.objects.values_list('year', flat=True).distinct()]+[(next_year, next_year)], - label="Select Year", - widget=forms.Select(attrs={'class': 'ui fluid search selection dropdown'}) - ) - semester_type = forms.ChoiceField( - choices=[('', 'Choose a semester type')] + list(CourseInstructor.SEMESTER_TYPE_CHOICES), - label="Select Semester Type", - widget=forms.Select(attrs={'class': 'ui fluid search selection dropdown'}) - ) - class Meta: - model = CourseInstructor - fields = ['course_id', 'instructor_id', 'year', 'semester_type'] - - # def __init__(self, *args, **kwargs): - # super().__init__(*args, **kwargs) - # # Query all unique years from the Batch table - # unique_years = Batch.objects.values_list('year', flat=True).distinct() - # # Set the choices for the 'year' field dynamically - # self.fields['year'].choices = [(year, year) for year in unique_years] - - # def sed(self): - # r_id = self.cleaned_data.get('receive_id') - # return r_id - # def __init__(self, *args, **kwargs): - # super().__init__(*args, **kwargs) - # des = HoldsDesignation.objects.select_related('working','designation').filter(user=5333) - # self.fields['receive_design'].queryset = Designation.objects.filter(id=list(des.designation)) - - - # def clean(self): - # cleaned_data = super().clean() - # user_id = cleaned_data.get('receive_id') - # if user_id: - # self.fields['receive_design'].queryset = HoldsDesignation.objects.select_related('designation').filter(user_id=user_id) + } \ No newline at end of file diff --git a/FusionIIIT/applications/programme_curriculum/management/__init__.py b/FusionIIIT/applications/programme_curriculum/management/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/FusionIIIT/applications/programme_curriculum/management/commands/__init__.py b/FusionIIIT/applications/programme_curriculum/management/commands/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/FusionIIIT/applications/programme_curriculum/migrations/0001_initial.py b/FusionIIIT/applications/programme_curriculum/migrations/0001_initial.py index 0654934c9..b01ec58a7 100644 --- a/FusionIIIT/applications/programme_curriculum/migrations/0001_initial.py +++ b/FusionIIIT/applications/programme_curriculum/migrations/0001_initial.py @@ -1,7 +1,5 @@ -# Generated by Django 3.1.5 on 2024-07-16 15:44 +# Generated by Django 3.1.5 on 2023-03-15 18:53 -from django.conf import settings -import django.core.validators from django.db import migrations, models import django.db.models.deletion @@ -12,7 +10,6 @@ class Migration(migrations.Migration): dependencies = [ ('globals', '0001_initial'), - migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] operations = [ @@ -21,11 +18,10 @@ class Migration(migrations.Migration): fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('name', models.CharField(max_length=100)), - ('version', models.DecimalField(decimal_places=1, default=1.0, max_digits=5, validators=[django.core.validators.MinValueValidator(1.0), django.core.validators.DecimalValidator(decimal_places=1, max_digits=5)])), + ('version', models.PositiveIntegerField(default=1)), ('working_curriculum', models.BooleanField(default=True)), ('no_of_semester', models.PositiveIntegerField(default=1)), ('min_credit', models.PositiveIntegerField(default=0)), - ('latest_version', models.BooleanField(default=True)), ], ), migrations.CreateModel( @@ -34,7 +30,7 @@ class Migration(migrations.Migration): ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('category', models.CharField(choices=[('UG', 'Undergraduate'), ('PG', 'Postgraduate'), ('PHD', 'Doctor of Philosophy')], max_length=3)), ('name', models.CharField(max_length=70, unique=True)), - ('programme_begin_year', models.PositiveIntegerField(default=2024)), + ('programme_begin_year', models.PositiveIntegerField(default=2023)), ], ), migrations.CreateModel( @@ -70,9 +66,8 @@ class Migration(migrations.Migration): name='Course', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('code', models.CharField(max_length=10)), - ('name', models.CharField(max_length=100)), - ('version', models.DecimalField(decimal_places=1, default=1.0, max_digits=5, validators=[django.core.validators.MinValueValidator(1.0), django.core.validators.DecimalValidator(decimal_places=1, max_digits=5)])), + ('code', models.CharField(max_length=10, unique=True)), + ('name', models.CharField(max_length=100, unique=True)), ('credit', models.PositiveIntegerField(default=0)), ('lecture_hours', models.PositiveIntegerField(null=True)), ('tutorial_hours', models.PositiveIntegerField(null=True)), @@ -90,12 +85,11 @@ class Migration(migrations.Migration): ('percent_course_attendance', models.PositiveIntegerField(default=5)), ('ref_books', models.TextField()), ('working_course', models.BooleanField(default=True)), - ('latest_version', models.BooleanField(default=True)), ('disciplines', models.ManyToManyField(blank=True, to='programme_curriculum.Discipline')), ('pre_requisit_courses', models.ManyToManyField(blank=True, related_name='_course_pre_requisit_courses_+', to='programme_curriculum.Course')), ], options={ - 'unique_together': {('code', 'version')}, + 'unique_together': {('code', 'name')}, }, ), migrations.CreateModel( @@ -103,7 +97,7 @@ class Migration(migrations.Migration): fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('name', models.CharField(choices=[('B.Tech', 'B.Tech'), ('M.Tech', 'M.Tech'), ('B.Des', 'B.Des'), ('M.Des', 'M.Des'), ('Phd', 'Phd')], max_length=50)), - ('year', models.PositiveIntegerField(default=2024)), + ('year', models.PositiveIntegerField(default=2023)), ('running_batch', models.BooleanField(default=True)), ('curriculum', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='programme_curriculum.curriculum')), ('discipline', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='programme_curriculum.discipline')), @@ -112,65 +106,6 @@ class Migration(migrations.Migration): 'unique_together': {('name', 'discipline', 'year')}, }, ), - migrations.CreateModel( - name='Proposal_Tracking', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('file_id', models.CharField(max_length=100)), - ('current_id', models.CharField(max_length=100)), - ('current_design', models.CharField(max_length=100)), - ('receive_date', models.DateTimeField(auto_now_add=True)), - ('forward_date', models.DateTimeField(auto_now_add=True)), - ('remarks', models.CharField(blank=True, max_length=250, null=True)), - ('is_added', models.BooleanField(default=False)), - ('is_submitted', models.BooleanField(default=False)), - ('is_rejected', models.BooleanField(default=False)), - ('sender_archive', models.BooleanField(default=False)), - ('receiver_archive', models.BooleanField(default=False)), - ('disciplines', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='programme_curriculum.discipline')), - ('receive_design', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='globals.designation')), - ('receive_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), - ], - options={ - 'unique_together': {('file_id', 'current_id', 'current_design', 'disciplines')}, - }, - ), - migrations.CreateModel( - name='NewProposalFile', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('uploader', models.CharField(max_length=100)), - ('designation', models.CharField(max_length=100)), - ('code', models.CharField(max_length=10)), - ('name', models.CharField(max_length=100)), - ('credit', models.PositiveIntegerField(default=3)), - ('lecture_hours', models.PositiveIntegerField(default=3, null=True)), - ('tutorial_hours', models.PositiveIntegerField(default=0, null=True)), - ('pratical_hours', models.PositiveIntegerField(default=0, null=True)), - ('discussion_hours', models.PositiveIntegerField(default=0, null=True)), - ('project_hours', models.PositiveIntegerField(default=0, null=True)), - ('pre_requisits', models.TextField(blank=True, null=True)), - ('syllabus', models.TextField()), - ('percent_quiz_1', models.PositiveIntegerField(default=10)), - ('percent_midsem', models.PositiveIntegerField(default=20)), - ('percent_quiz_2', models.PositiveIntegerField(default=10)), - ('percent_endsem', models.PositiveIntegerField(default=30)), - ('percent_project', models.PositiveIntegerField(default=15)), - ('percent_lab_evaluation', models.PositiveIntegerField(default=10)), - ('percent_course_attendance', models.PositiveIntegerField(default=5)), - ('ref_books', models.TextField()), - ('subject', models.CharField(max_length=100, null=True)), - ('description', models.CharField(max_length=400, null=True)), - ('upload_date', models.DateTimeField(auto_now_add=True)), - ('is_read', models.BooleanField(default=False)), - ('is_update', models.BooleanField(default=False)), - ('is_archive', models.BooleanField(default=False)), - ('pre_requisit_courses', models.ManyToManyField(blank=True, to='programme_curriculum.Course')), - ], - options={ - 'unique_together': {('code', 'uploader', 'name')}, - }, - ), migrations.AlterUniqueTogether( name='curriculum', unique_together={('name', 'version')}, @@ -180,7 +115,7 @@ class Migration(migrations.Migration): fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('name', models.CharField(max_length=100)), - ('type', models.CharField(choices=[('Professional Core', 'Professional Core'), ('Professional Elective', 'Professional Elective'), ('Professional Lab', 'Professional Lab'), ('Engineering Science', 'Engineering Science'), ('Natural Science', 'Natural Science'), ('Humanities', 'Humanities'), ('Design', 'Design'), ('Manufacturing', 'Manufacturing'), ('Management Science', 'Management Science'), ('Open Elective', 'Open Elective'), ('Swayam', 'Swayam'), ('Project', 'Project'), ('Optional', 'Optional'), ('Others', 'Others')], max_length=70)), + ('type', models.CharField(choices=[('Professional Core', 'Professional Core'), ('Professional Elective', 'Professional Elective'), ('Professional Lab', 'Professional Lab'), ('Engineering Science', 'Engineering Science'), ('Natural Science', 'Natural Science'), ('Humanities', 'Humanities'), ('Design', 'Design'), ('Manufacturing', 'Manufacturing'), ('Management Science', 'Management Science'), ('Optional Elective', 'Optional Elective'), ('Project', 'Project'), ('Optional', 'Optional'), ('Others', 'Others')], max_length=70)), ('course_slot_info', models.TextField(null=True)), ('duration', models.PositiveIntegerField(default=1)), ('min_registration_limit', models.PositiveIntegerField(default=0)), diff --git a/FusionIIIT/applications/programme_curriculum/migrations/0002_course_max_seats.py b/FusionIIIT/applications/programme_curriculum/migrations/0002_course_max_seats.py deleted file mode 100644 index 75700de21..000000000 --- a/FusionIIIT/applications/programme_curriculum/migrations/0002_course_max_seats.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.1.5 on 2024-10-23 00:20 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('programme_curriculum', '0001_initial'), - ] - - operations = [ - migrations.AddField( - model_name='course', - name='max_seats', - field=models.IntegerField(default=90), - ), - ] diff --git a/FusionIIIT/applications/programme_curriculum/migrations/0003_auto_20241115_1545.py b/FusionIIIT/applications/programme_curriculum/migrations/0003_auto_20241115_1545.py deleted file mode 100644 index a30ce9892..000000000 --- a/FusionIIIT/applications/programme_curriculum/migrations/0003_auto_20241115_1545.py +++ /dev/null @@ -1,39 +0,0 @@ -# Generated by Django 3.1.5 on 2024-11-15 15:45 - -import django.core.validators -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('globals', '0002_moduleaccess'), - ('programme_curriculum', '0002_course_max_seats'), - ] - - operations = [ - migrations.AddField( - model_name='courseinstructor', - name='semester_no', - field=models.IntegerField(default=1, validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(8)]), - ), - migrations.AddField( - model_name='courseinstructor', - name='year', - field=models.IntegerField(default=2024), - ), - migrations.AlterField( - model_name='courseinstructor', - name='instructor_id', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='globals.faculty'), - ), - migrations.AlterUniqueTogether( - name='courseinstructor', - unique_together={('course_id', 'instructor_id', 'year')}, - ), - migrations.RemoveField( - model_name='courseinstructor', - name='batch_id', - ), - ] diff --git a/FusionIIIT/applications/programme_curriculum/migrations/0004_auto_20241117_0137.py b/FusionIIIT/applications/programme_curriculum/migrations/0004_auto_20241117_0137.py deleted file mode 100644 index 8a8d84741..000000000 --- a/FusionIIIT/applications/programme_curriculum/migrations/0004_auto_20241117_0137.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.1.5 on 2024-11-17 01:37 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('programme_curriculum', '0003_auto_20241115_1545'), - ] - - operations = [ - migrations.AlterField( - model_name='courseslot', - name='type', - field=models.CharField(choices=[('Professional Core', 'Professional Core'), ('Professional Elective', 'Professional Elective'), ('Professional Lab', 'Professional Lab'), ('Engineering Science', 'Engineering Science'), ('Natural Science', 'Natural Science'), ('Humanities', 'Humanities'), ('Design', 'Design'), ('Manufacturing', 'Manufacturing'), ('Management Science', 'Management Science'), ('Open Elective', 'Open Elective'), ('Swayam', 'Swayam'), ('Project', 'Project'), ('Optional', 'Optional'), ('Backlog', 'Backlog'), ('Others', 'Others')], max_length=70), - ), - ] diff --git a/FusionIIIT/applications/programme_curriculum/migrations/0005_auto_20250201_2228.py b/FusionIIIT/applications/programme_curriculum/migrations/0005_auto_20250201_2228.py deleted file mode 100644 index 837b42045..000000000 --- a/FusionIIIT/applications/programme_curriculum/migrations/0005_auto_20250201_2228.py +++ /dev/null @@ -1,28 +0,0 @@ -# Generated by Django 3.1.5 on 2025-02-01 22:28 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('programme_curriculum', '0004_auto_20241117_0137'), - ] - - operations = [ - migrations.AlterField( - model_name='batch', - name='year', - field=models.PositiveIntegerField(default=2025), - ), - migrations.AlterField( - model_name='courseinstructor', - name='year', - field=models.IntegerField(default=2025), - ), - migrations.AlterField( - model_name='programme', - name='programme_begin_year', - field=models.PositiveIntegerField(default=2025), - ), - ] diff --git a/FusionIIIT/applications/programme_curriculum/migrations/0006_newproposalfile_max_seats.py b/FusionIIIT/applications/programme_curriculum/migrations/0006_newproposalfile_max_seats.py deleted file mode 100644 index cf96d1520..000000000 --- a/FusionIIIT/applications/programme_curriculum/migrations/0006_newproposalfile_max_seats.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.1.5 on 2025-03-03 16:15 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('programme_curriculum', '0005_auto_20250201_2228'), - ] - - operations = [ - migrations.AddField( - model_name='newproposalfile', - name='max_seats', - field=models.IntegerField(default=90), - ), - ] diff --git a/FusionIIIT/applications/programme_curriculum/migrations/0007_auto_20250421_0900.py b/FusionIIIT/applications/programme_curriculum/migrations/0007_auto_20250421_0900.py deleted file mode 100644 index e26d0f68f..000000000 --- a/FusionIIIT/applications/programme_curriculum/migrations/0007_auto_20250421_0900.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.1.5 on 2025-04-21 09:00 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('programme_curriculum', '0006_newproposalfile_max_seats'), - ] - - operations = [ - migrations.AddField( - model_name='courseinstructor', - name='semester_type', - field=models.CharField(choices=[('Odd Semester', 'Odd Semester'), ('Even Semester', 'Even Semester'), ('Summer Semester', 'Summer Semester')], max_length=20, null=True), - ), - ] diff --git a/FusionIIIT/applications/programme_curriculum/migrations/0008_populate_semester_type.py b/FusionIIIT/applications/programme_curriculum/migrations/0008_populate_semester_type.py deleted file mode 100644 index 6a0f35aa5..000000000 --- a/FusionIIIT/applications/programme_curriculum/migrations/0008_populate_semester_type.py +++ /dev/null @@ -1,30 +0,0 @@ -# Generated by Django 3.1.5 on 2025-04-21 09:01 - -from django.db import migrations - -def populate_academic_year_and_semester_type(apps, schema_editor): - import datetime - - CourseInstructor = apps.get_model('programme_curriculum', 'CourseInstructor') - - for ci in CourseInstructor.objects.all(): - if ci.semester_no % 2 == 1: - ci.semester_type = "Odd Semester" - else: - ci.semester_type = "Even Semester" - - ci.save() - -class Migration(migrations.Migration): - - dependencies = [ - ('programme_curriculum', '0007_auto_20250421_0900'), - ] - - operations = [ - migrations.RunPython(populate_academic_year_and_semester_type), - migrations.RemoveField( - model_name='courseinstructor', - name='semester_no', - ), - ] diff --git a/FusionIIIT/applications/programme_curriculum/migrations/0009_auto_20250503_1355.py b/FusionIIIT/applications/programme_curriculum/migrations/0009_auto_20250503_1355.py deleted file mode 100644 index 4c05524f3..000000000 --- a/FusionIIIT/applications/programme_curriculum/migrations/0009_auto_20250503_1355.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.1.5 on 2025-05-03 13:55 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('globals', '0004_extrainfo_last_selected_role'), - ('programme_curriculum', '0008_populate_semester_type'), - ] - - operations = [ - migrations.AlterUniqueTogether( - name='courseinstructor', - unique_together={('course_id', 'instructor_id', 'year', 'semester_type')}, - ), - ] diff --git a/FusionIIIT/applications/programme_curriculum/migrations/0010_auto_20250824_1342.py b/FusionIIIT/applications/programme_curriculum/migrations/0010_auto_20250824_1342.py deleted file mode 100644 index 56a45b8bd..000000000 --- a/FusionIIIT/applications/programme_curriculum/migrations/0010_auto_20250824_1342.py +++ /dev/null @@ -1,138 +0,0 @@ -# Generated by Django 3.1.5 on 2025-08-24 13:42 - -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('programme_curriculum', '0009_auto_20250503_1355'), - ] - - operations = [ - migrations.CreateModel( - name='StudentBatchUpload', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('jee_app_no', models.CharField(help_text='JEE Application Number', max_length=50, unique=True)), - ('roll_number', models.CharField(blank=True, help_text='Institute Roll Number', max_length=20, null=True, unique=True)), - ('institute_email', models.EmailField(blank=True, help_text='Institute Email ID', max_length=254, null=True)), - ('name', models.CharField(help_text='Full Name', max_length=200)), - ('father_name', models.CharField(help_text="Father's Name", max_length=200)), - ('mother_name', models.CharField(help_text="Mother's Name", max_length=200)), - ('gender', models.CharField(choices=[('Male', 'Male'), ('Female', 'Female'), ('Other', 'Other')], max_length=10)), - ('category', models.CharField(choices=[('GEN', 'General'), ('OBC', 'Other Backward Class'), ('SC', 'Scheduled Caste'), ('ST', 'Scheduled Tribe'), ('EWS', 'Economically Weaker Section')], max_length=10)), - ('pwd', models.CharField(choices=[('YES', 'Yes'), ('NO', 'No')], default='NO', help_text='Person with Disability', max_length=3)), - ('phone_number', models.CharField(blank=True, help_text='Mobile Number', max_length=15, null=True)), - ('personal_email', models.EmailField(blank=True, help_text='Personal Email ID', max_length=254, null=True)), - ('address', models.TextField(help_text='Full Address')), - ('state', models.CharField(blank=True, max_length=100, null=True)), - ('branch', models.CharField(help_text='Discipline/Branch', max_length=200)), - ('date_of_birth', models.DateField(blank=True, null=True)), - ('ai_rank', models.IntegerField(blank=True, help_text='JEE AI Rank', null=True)), - ('category_rank', models.IntegerField(blank=True, help_text='Category Rank', null=True)), - ('tenth_marks', models.FloatField(blank=True, help_text='10th Class Marks (%)', null=True)), - ('twelfth_marks', models.FloatField(blank=True, help_text='12th Class Marks (%)', null=True)), - ('father_occupation', models.CharField(blank=True, max_length=200, null=True)), - ('father_mobile', models.CharField(blank=True, max_length=15, null=True)), - ('mother_occupation', models.CharField(blank=True, max_length=200, null=True)), - ('mother_mobile', models.CharField(blank=True, max_length=15, null=True)), - ('aadhar_number', models.CharField(blank=True, max_length=12, null=True)), - ('allotted_category', models.CharField(blank=True, max_length=10, null=True)), - ('allotted_gender', models.CharField(blank=True, max_length=10, null=True)), - ('year', models.IntegerField(help_text='Academic Year')), - ('programme_type', models.CharField(choices=[('ug', 'Undergraduate'), ('pg', 'Postgraduate'), ('phd', 'PhD')], max_length=10)), - ('reported_status', models.CharField(choices=[('NOT_REPORTED', 'Not Reported'), ('REPORTED', 'Reported')], default='NOT_REPORTED', max_length=20)), - ('password', models.CharField(blank=True, help_text='Auto-generated password', max_length=100, null=True)), - ('created_at', models.DateTimeField(auto_now_add=True)), - ('updated_at', models.DateTimeField(auto_now=True)), - ('uploaded_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)), - ], - options={ - 'verbose_name': 'Student Batch Upload', - 'verbose_name_plural': 'Student Batch Uploads', - 'ordering': ['roll_number', 'name'], - }, - ), - migrations.CreateModel( - name='UploadHistory', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('upload_type', models.CharField(choices=[('excel', 'Excel Upload'), ('manual', 'Manual Entry'), ('bulk', 'Bulk Import')], max_length=20)), - ('programme_type', models.CharField(choices=[('ug', 'Undergraduate'), ('pg', 'Postgraduate'), ('phd', 'PhD')], max_length=10)), - ('total_records', models.IntegerField(default=0)), - ('successful_records', models.IntegerField(default=0)), - ('failed_records', models.IntegerField(default=0)), - ('file_name', models.CharField(blank=True, max_length=255, null=True)), - ('upload_notes', models.TextField(blank=True, null=True)), - ('created_at', models.DateTimeField(auto_now_add=True)), - ('uploaded_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)), - ], - options={ - 'verbose_name': 'Upload History', - 'verbose_name_plural': 'Upload History', - 'ordering': ['-created_at'], - }, - ), - migrations.CreateModel( - name='StudentStatusLog', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('old_reported_status', models.CharField(max_length=20)), - ('new_reported_status', models.CharField(max_length=20)), - ('change_reason', models.TextField(blank=True, null=True)), - ('ip_address', models.GenericIPAddressField(blank=True, null=True)), - ('created_at', models.DateTimeField(auto_now_add=True)), - ('changed_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)), - ('student', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='status_logs', to='programme_curriculum.studentbatchupload')), - ], - options={ - 'verbose_name': 'Student Status Log', - 'verbose_name_plural': 'Student Status Logs', - 'ordering': ['-created_at'], - }, - ), - migrations.CreateModel( - name='BatchConfiguration', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('programme', models.CharField(choices=[('B.Tech', 'Bachelor of Technology'), ('B.Des', 'Bachelor of Design'), ('M.Tech', 'Master of Technology'), ('M.Des', 'Master of Design'), ('PhD', 'Doctor of Philosophy')], max_length=50)), - ('discipline', models.CharField(choices=[('Computer Science and Engineering', 'Computer Science and Engineering'), ('Electronics and Communication Engineering', 'Electronics and Communication Engineering'), ('Mechanical Engineering', 'Mechanical Engineering'), ('Smart Manufacturing', 'Smart Manufacturing'), ('Design', 'Design')], max_length=100)), - ('year', models.IntegerField(help_text='Academic year (e.g., 2025 for 2025-26)')), - ('total_seats', models.IntegerField(default=60)), - ('filled_seats', models.IntegerField(default=0, editable=False)), - ('available_seats', models.IntegerField(default=0, editable=False)), - ('created_at', models.DateTimeField(auto_now_add=True)), - ('updated_at', models.DateTimeField(auto_now=True)), - ], - options={ - 'verbose_name': 'Batch Configuration', - 'verbose_name_plural': 'Batch Configurations', - 'ordering': ['programme', 'discipline', 'year'], - 'unique_together': {('programme', 'discipline', 'year')}, - }, - ), - migrations.AddIndex( - model_name='studentbatchupload', - index=models.Index(fields=['programme_type', 'year'], name='programme_c_program_a95017_idx'), - ), - migrations.AddIndex( - model_name='studentbatchupload', - index=models.Index(fields=['branch'], name='programme_c_branch_76eb51_idx'), - ), - migrations.AddIndex( - model_name='studentbatchupload', - index=models.Index(fields=['reported_status'], name='programme_c_reporte_681f29_idx'), - ), - migrations.AddIndex( - model_name='studentbatchupload', - index=models.Index(fields=['jee_app_no'], name='programme_c_jee_app_cec0ce_idx'), - ), - migrations.AddIndex( - model_name='studentbatchupload', - index=models.Index(fields=['roll_number'], name='programme_c_roll_nu_d6ad8b_idx'), - ), - ] diff --git a/FusionIIIT/applications/programme_curriculum/migrations/0011_auto_20250824_2024.py b/FusionIIIT/applications/programme_curriculum/migrations/0011_auto_20250824_2024.py deleted file mode 100644 index 4d5cc9b5b..000000000 --- a/FusionIIIT/applications/programme_curriculum/migrations/0011_auto_20250824_2024.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 3.1.5 on 2025-08-24 20:24 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('programme_curriculum', '0010_auto_20250824_1342'), - ] - - operations = [ - migrations.AlterField( - model_name='studentbatchupload', - name='allotted_category', - field=models.CharField(blank=True, max_length=20, null=True), - ), - migrations.AlterField( - model_name='studentbatchupload', - name='allotted_gender', - field=models.CharField(blank=True, max_length=20, null=True), - ), - ] diff --git a/FusionIIIT/applications/programme_curriculum/migrations/0012_add_password_email_models.py b/FusionIIIT/applications/programme_curriculum/migrations/0012_add_password_email_models.py deleted file mode 100644 index 4689b278b..000000000 --- a/FusionIIIT/applications/programme_curriculum/migrations/0012_add_password_email_models.py +++ /dev/null @@ -1,120 +0,0 @@ -# Generated by Django 3.1.5 on 2025-08-25 04:12 - -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('programme_curriculum', '0011_auto_20250824_2024'), - ] - - operations = [ - migrations.CreateModel( - name='StudentPasswordHistory', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('password_hash', models.CharField(max_length=128)), - ('created_at', models.DateTimeField(auto_now_add=True)), - ('is_initial_password', models.BooleanField(default=False)), - ('is_active', models.BooleanField(default=True)), - ('password_changed_at', models.DateTimeField(blank=True, null=True)), - ('change_reason', models.CharField(blank=True, max_length=100, null=True)), - ('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)), - ('student', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='password_history', to='programme_curriculum.studentbatchupload')), - ], - options={ - 'verbose_name': 'Student Password History', - 'verbose_name_plural': 'Student Password History', - 'db_table': 'student_password_history', - 'ordering': ['-created_at'], - }, - ), - migrations.CreateModel( - name='PasswordEmailLog', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('sent_to_email', models.EmailField(help_text='Email address where password was sent', max_length=254)), - ('email_status', models.CharField(choices=[('SENT', 'Successfully Sent'), ('FAILED', 'Failed to Send'), ('PENDING', 'Pending'), ('BOUNCED', 'Email Bounced')], default='PENDING', max_length=10)), - ('password_generated', models.CharField(blank=True, help_text='The password that was generated (for audit)', max_length=100, null=True)), - ('email_subject', models.CharField(blank=True, max_length=255, null=True)), - ('email_content_preview', models.TextField(blank=True, help_text='First 200 chars of email content', null=True)), - ('error_message', models.TextField(blank=True, null=True)), - ('attempts_count', models.IntegerField(default=1)), - ('ip_address', models.GenericIPAddressField(blank=True, null=True)), - ('user_agent', models.TextField(blank=True, null=True)), - ('sent_at', models.DateTimeField(auto_now_add=True)), - ('last_attempt_at', models.DateTimeField(auto_now=True)), - ('sent_by', models.ForeignKey(blank=True, help_text='Admin who triggered the password email', null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)), - ('student', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='password_emails', to='programme_curriculum.studentbatchupload')), - ], - options={ - 'verbose_name': 'Password Email Log', - 'verbose_name_plural': 'Password Email Logs', - 'db_table': 'password_email_log', - 'ordering': ['-sent_at'], - }, - ), - migrations.CreateModel( - name='EmailTemplate', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('template_type', models.CharField(choices=[('INITIAL_PASSWORD', 'Initial Password Email'), ('PASSWORD_RESET', 'Password Reset Email'), ('BULK_PASSWORD', 'Bulk Password Email'), ('REMINDER', 'Login Reminder Email')], max_length=20, unique=True)), - ('subject_template', models.CharField(max_length=255)), - ('html_template', models.TextField()), - ('text_template', models.TextField(blank=True, null=True)), - ('variables_info', models.JSONField(default=dict, help_text='JSON object describing available template variables')), - ('is_active', models.BooleanField(default=True)), - ('created_at', models.DateTimeField(auto_now_add=True)), - ('updated_at', models.DateTimeField(auto_now=True)), - ('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)), - ], - options={ - 'verbose_name': 'Email Template', - 'verbose_name_plural': 'Email Templates', - 'db_table': 'email_template', - }, - ), - migrations.CreateModel( - name='BulkPasswordEmailOperation', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('operation_id', models.CharField(max_length=100, unique=True)), - ('programme_type', models.CharField(blank=True, max_length=10, null=True)), - ('year', models.IntegerField(blank=True, null=True)), - ('branch_filter', models.CharField(blank=True, max_length=200, null=True)), - ('total_students', models.IntegerField(default=0)), - ('emails_sent', models.IntegerField(default=0)), - ('emails_failed', models.IntegerField(default=0)), - ('emails_pending', models.IntegerField(default=0)), - ('operation_status', models.CharField(choices=[('INITIATED', 'Initiated'), ('IN_PROGRESS', 'In Progress'), ('COMPLETED', 'Completed'), ('FAILED', 'Failed'), ('CANCELLED', 'Cancelled')], default='INITIATED', max_length=15)), - ('start_time', models.DateTimeField(auto_now_add=True)), - ('end_time', models.DateTimeField(blank=True, null=True)), - ('duration_seconds', models.IntegerField(blank=True, null=True)), - ('error_summary', models.TextField(blank=True, null=True)), - ('notes', models.TextField(blank=True, null=True)), - ('initiated_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)), - ], - options={ - 'verbose_name': 'Bulk Password Email Operation', - 'verbose_name_plural': 'Bulk Password Email Operations', - 'db_table': 'bulk_password_email_operation', - 'ordering': ['-start_time'], - }, - ), - migrations.AddIndex( - model_name='passwordemaillog', - index=models.Index(fields=['email_status'], name='password_em_email_s_0aff1d_idx'), - ), - migrations.AddIndex( - model_name='passwordemaillog', - index=models.Index(fields=['sent_at'], name='password_em_sent_at_a4c3ff_idx'), - ), - migrations.AddIndex( - model_name='passwordemaillog', - index=models.Index(fields=['student'], name='password_em_student_6e6088_idx'), - ), - ] diff --git a/FusionIIIT/applications/programme_curriculum/migrations/0013_remove_password_field_add_user_link.py b/FusionIIIT/applications/programme_curriculum/migrations/0013_remove_password_field_add_user_link.py deleted file mode 100644 index 240a3170a..000000000 --- a/FusionIIIT/applications/programme_curriculum/migrations/0013_remove_password_field_add_user_link.py +++ /dev/null @@ -1,30 +0,0 @@ -# Generated by Django 3.1.5 on 2025-08-25 04:30 - -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('programme_curriculum', '0012_add_password_email_models'), - ] - - operations = [ - migrations.RemoveField( - model_name='studentbatchupload', - name='password', - ), - migrations.AddField( - model_name='studentbatchupload', - name='user', - field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='student_profile', to=settings.AUTH_USER_MODEL), - ), - migrations.AlterField( - model_name='studentbatchupload', - name='uploaded_by', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='uploaded_students', to=settings.AUTH_USER_MODEL), - ), - ] diff --git a/FusionIIIT/applications/programme_curriculum/migrations/0014_auto_20250825_2048.py b/FusionIIIT/applications/programme_curriculum/migrations/0014_auto_20250825_2048.py deleted file mode 100644 index cf56a104d..000000000 --- a/FusionIIIT/applications/programme_curriculum/migrations/0014_auto_20250825_2048.py +++ /dev/null @@ -1,55 +0,0 @@ -# Generated by Django 3.1.5 on 2025-08-25 20:48 - -import applications.programme_curriculum.models_student_management -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('programme_curriculum', '0013_remove_password_field_add_user_link'), - ] - - operations = [ - migrations.RemoveIndex( - model_name='studentbatchupload', - name='programme_c_program_a95017_idx', - ), - migrations.AddField( - model_name='studentbatchupload', - name='academic_year', - field=models.CharField(blank=True, db_column='academic_year', help_text='Academic Year String (e.g., 2025-26)', max_length=20), - ), - migrations.AddField( - model_name='studentbatchupload', - name='allocation_status', - field=models.CharField(default='ALLOCATED', help_text='Allocation Status', max_length=50), - ), - migrations.AlterField( - model_name='studentbatchupload', - name='ai_rank', - field=models.IntegerField(blank=True, db_column='jee_rank', help_text='JEE AI Rank', null=True), - ), - migrations.AlterField( - model_name='studentbatchupload', - name='uploaded_by', - field=models.ForeignKey(blank=True, db_column='created_by_id', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='uploaded_students', to=settings.AUTH_USER_MODEL), - ), - migrations.AlterField( - model_name='studentbatchupload', - name='user', - field=models.ForeignKey(blank=True, db_column='user_account_id', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='student_profile', to=settings.AUTH_USER_MODEL), - ), - migrations.AlterField( - model_name='studentbatchupload', - name='year', - field=models.IntegerField(db_column='batch_year', default=applications.programme_curriculum.models_student_management.get_current_academic_year, help_text='Academic Year'), - ), - migrations.AddIndex( - model_name='studentbatchupload', - index=models.Index(fields=['programme_type', 'year'], name='programme_c_program_8a36dc_idx'), - ), - ] diff --git a/FusionIIIT/applications/programme_curriculum/migrations/0015_add_email_password_fields.py b/FusionIIIT/applications/programme_curriculum/migrations/0015_add_email_password_fields.py deleted file mode 100644 index fff17b055..000000000 --- a/FusionIIIT/applications/programme_curriculum/migrations/0015_add_email_password_fields.py +++ /dev/null @@ -1,28 +0,0 @@ -# Generated by Django 3.1.5 on 2025-08-25 22:54 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('programme_curriculum', '0014_auto_20250825_2048'), - ] - - operations = [ - migrations.AddField( - model_name='studentbatchupload', - name='email_password', - field=models.CharField(blank=True, help_text='Temporary storage for email notification', max_length=50, null=True), - ), - migrations.AddField( - model_name='studentbatchupload', - name='password_email_sent', - field=models.BooleanField(default=False, help_text='Whether password email has been sent'), - ), - migrations.AddField( - model_name='studentbatchupload', - name='password_generated_at', - field=models.DateTimeField(blank=True, help_text='When password was generated', null=True), - ), - ] diff --git a/FusionIIIT/applications/programme_curriculum/migrations/0016_batch_total_seats.py b/FusionIIIT/applications/programme_curriculum/migrations/0016_batch_total_seats.py deleted file mode 100644 index 803f6362d..000000000 --- a/FusionIIIT/applications/programme_curriculum/migrations/0016_batch_total_seats.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.1.5 on 2025-08-28 06:19 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('programme_curriculum', '0015_add_email_password_fields'), - ] - - operations = [ - migrations.AddField( - model_name='batch', - name='total_seats', - field=models.PositiveIntegerField(default=0), - ), - ] diff --git a/FusionIIIT/applications/programme_curriculum/migrations/0017_fix_curriculum_unique_constraint.py b/FusionIIIT/applications/programme_curriculum/migrations/0017_fix_curriculum_unique_constraint.py deleted file mode 100644 index 2f02595d9..000000000 --- a/FusionIIIT/applications/programme_curriculum/migrations/0017_fix_curriculum_unique_constraint.py +++ /dev/null @@ -1,17 +0,0 @@ -# Generated by Django 3.1.5 on 2025-08-28 22:04 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('programme_curriculum', '0016_batch_total_seats'), - ] - - operations = [ - migrations.AlterUniqueTogether( - name='curriculum', - unique_together={('name', 'programme', 'version')}, - ), - ] diff --git a/FusionIIIT/applications/programme_curriculum/migrations/0018_studentbatchupload_source.py b/FusionIIIT/applications/programme_curriculum/migrations/0018_studentbatchupload_source.py deleted file mode 100644 index 51dd58b1f..000000000 --- a/FusionIIIT/applications/programme_curriculum/migrations/0018_studentbatchupload_source.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.1.5 on 2025-08-29 19:31 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('programme_curriculum', '0017_fix_curriculum_unique_constraint'), - ] - - operations = [ - migrations.AddField( - model_name='studentbatchupload', - name='source', - field=models.CharField(default='admin_upload', help_text='Source of student data (admin_upload, excel_upload, manual_entry, etc.)', max_length=50), - ), - ] diff --git a/FusionIIIT/applications/programme_curriculum/migrations/0019_add_withdrawal_status.py b/FusionIIIT/applications/programme_curriculum/migrations/0019_add_withdrawal_status.py deleted file mode 100644 index e51495bc7..000000000 --- a/FusionIIIT/applications/programme_curriculum/migrations/0019_add_withdrawal_status.py +++ /dev/null @@ -1,22 +0,0 @@ -# Generated by Django 3.1.5 on 2025-08-29 23:35 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('programme_curriculum', '0018_studentbatchupload_source'), - ] - - operations = [ - migrations.RemoveField( - model_name='studentbatchupload', - name='source', - ), - migrations.AlterField( - model_name='studentbatchupload', - name='reported_status', - field=models.CharField(choices=[('NOT_REPORTED', 'Not Reported'), ('REPORTED', 'Reported'), ('WITHDRAWAL', 'Withdrawal')], default='NOT_REPORTED', max_length=20), - ), - ] diff --git a/FusionIIIT/applications/programme_curriculum/migrations/0020_auto_20250830_0100.py b/FusionIIIT/applications/programme_curriculum/migrations/0020_auto_20250830_0100.py deleted file mode 100644 index 38795134f..000000000 --- a/FusionIIIT/applications/programme_curriculum/migrations/0020_auto_20250830_0100.py +++ /dev/null @@ -1,26 +0,0 @@ -# Generated by Django 3.1.5 on 2025-08-30 01:00 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('programme_curriculum', '0019_add_withdrawal_status'), - ] - - operations = [ - migrations.RemoveField( - model_name='batch', - name='total_seats', - ), - migrations.AlterField( - model_name='studentbatchupload', - name='reported_status', - field=models.CharField(choices=[('NOT_REPORTED', 'Not Reported'), ('REPORTED', 'Reported')], default='NOT_REPORTED', max_length=20), - ), - migrations.AlterUniqueTogether( - name='curriculum', - unique_together={('name', 'version')}, - ), - ] diff --git a/FusionIIIT/applications/programme_curriculum/migrations/0021_auto_20250830_0126.py b/FusionIIIT/applications/programme_curriculum/migrations/0021_auto_20250830_0126.py deleted file mode 100644 index e75e9caf3..000000000 --- a/FusionIIIT/applications/programme_curriculum/migrations/0021_auto_20250830_0126.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 3.1.5 on 2025-08-30 01:26 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('programme_curriculum', '0020_auto_20250830_0100'), - ] - - operations = [ - migrations.AddField( - model_name='studentbatchupload', - name='source', - field=models.CharField(default='admin_upload', help_text='Source of student data (admin_upload, excel_upload, manual_entry, etc.)', max_length=50), - ), - migrations.AlterField( - model_name='studentbatchupload', - name='reported_status', - field=models.CharField(choices=[('NOT_REPORTED', 'Not Reported'), ('REPORTED', 'Reported'), ('WITHDRAWAL', 'Withdrawal')], default='NOT_REPORTED', max_length=20), - ), - ] diff --git a/FusionIIIT/applications/programme_curriculum/migrations/0022_batch_total_seats.py b/FusionIIIT/applications/programme_curriculum/migrations/0022_batch_total_seats.py deleted file mode 100644 index b25a01b8d..000000000 --- a/FusionIIIT/applications/programme_curriculum/migrations/0022_batch_total_seats.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.1.5 on 2025-08-30 02:43 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('programme_curriculum', '0021_auto_20250830_0126'), - ] - - operations = [ - migrations.AddField( - model_name='batch', - name='total_seats', - field=models.PositiveIntegerField(default=60), - ), - ] diff --git a/FusionIIIT/applications/programme_curriculum/migrations/0023_courseauditlog.py b/FusionIIIT/applications/programme_curriculum/migrations/0023_courseauditlog.py deleted file mode 100644 index 25657f4a1..000000000 --- a/FusionIIIT/applications/programme_curriculum/migrations/0023_courseauditlog.py +++ /dev/null @@ -1,37 +0,0 @@ -# Generated by Django 3.1.5 on 2025-08-30 18:42 - -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('programme_curriculum', '0022_batch_total_seats'), - ] - - operations = [ - migrations.CreateModel( - name='CourseAuditLog', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('timestamp', models.DateTimeField(auto_now_add=True)), - ('action', models.CharField(choices=[('CREATE', 'Create'), ('UPDATE', 'Update'), ('DELETE', 'Delete')], default='UPDATE', max_length=20)), - ('old_values', models.JSONField(blank=True, null=True)), - ('new_values', models.JSONField(blank=True, null=True)), - ('changed_fields', models.JSONField(default=list)), - ('version_bump_type', models.CharField(choices=[('NONE', 'No Version Bump'), ('PATCH', 'Patch (0.0.1)'), ('MINOR', 'Minor (0.1.0)'), ('MAJOR', 'Major (1.0.0)')], default='NONE', max_length=10)), - ('old_version', models.DecimalField(blank=True, decimal_places=1, max_digits=5, null=True)), - ('new_version', models.DecimalField(blank=True, decimal_places=1, max_digits=5, null=True)), - ('admin_override', models.BooleanField(default=False)), - ('reason', models.TextField(blank=True)), - ('course', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='audit_logs', to='programme_curriculum.course')), - ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), - ], - options={ - 'ordering': ['-timestamp'], - }, - ), - ] diff --git a/FusionIIIT/applications/programme_curriculum/migrations/0024_add_minority_field.py b/FusionIIIT/applications/programme_curriculum/migrations/0024_add_minority_field.py deleted file mode 100644 index f1b951126..000000000 --- a/FusionIIIT/applications/programme_curriculum/migrations/0024_add_minority_field.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.1.5 on 2025-09-02 13:27 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('programme_curriculum', '0023_courseauditlog'), - ] - - operations = [ - migrations.AddField( - model_name='studentbatchupload', - name='minority', - field=models.CharField(choices=[('YES', 'Yes'), ('NO', 'No')], default='NO', help_text='Minority Status', max_length=3), - ), - ] diff --git a/FusionIIIT/applications/programme_curriculum/migrations/0025_update_minority_values.py b/FusionIIIT/applications/programme_curriculum/migrations/0025_update_minority_values.py deleted file mode 100644 index 8e68b8b1b..000000000 --- a/FusionIIIT/applications/programme_curriculum/migrations/0025_update_minority_values.py +++ /dev/null @@ -1,51 +0,0 @@ -# Generated by Django 3.1.5 on 2025-09-02 16:53 - -from django.db import migrations, models - - -def update_minority_values(apps, schema_editor): - """Convert existing YES/NO values to Applicable/Not Applicable""" - StudentBatchUpload = apps.get_model('programme_curriculum', 'StudentBatchUpload') - - # Update YES to Applicable - StudentBatchUpload.objects.filter(minority='YES').update(minority='Applicable') - - # Update NO to Not Applicable - StudentBatchUpload.objects.filter(minority='NO').update(minority='Not Applicable') - - -def reverse_minority_values(apps, schema_editor): - """Reverse the migration: convert back to YES/NO""" - StudentBatchUpload = apps.get_model('programme_curriculum', 'StudentBatchUpload') - - # Update Applicable to YES - StudentBatchUpload.objects.filter(minority='Applicable').update(minority='YES') - - # Update Not Applicable to NO - StudentBatchUpload.objects.filter(minority='Not Applicable').update(minority='NO') - - -class Migration(migrations.Migration): - - dependencies = [ - ('programme_curriculum', '0024_add_minority_field'), - ] - - operations = [ - # First, increase field length to accommodate longer values - migrations.AlterField( - model_name='studentbatchupload', - name='minority', - field=models.CharField(choices=[('YES', 'Yes'), ('NO', 'No'), ('Applicable', 'Applicable'), ('Not Applicable', 'Not Applicable')], default='NO', help_text='Minority Status', max_length=20), - ), - - # Then update the data - migrations.RunPython(update_minority_values, reverse_minority_values), - - # Finally, update the field with the new choices and default - migrations.AlterField( - model_name='studentbatchupload', - name='minority', - field=models.CharField(choices=[('Applicable', 'Applicable'), ('Not Applicable', 'Not Applicable')], default='Not Applicable', help_text='Minority Status', max_length=20), - ), - ] diff --git a/FusionIIIT/applications/programme_curriculum/migrations/0026_add_database_indexes.py b/FusionIIIT/applications/programme_curriculum/migrations/0026_add_database_indexes.py deleted file mode 100644 index 2afda5843..000000000 --- a/FusionIIIT/applications/programme_curriculum/migrations/0026_add_database_indexes.py +++ /dev/null @@ -1,49 +0,0 @@ -# Database Optimization Migration for Student List Generation - -from django.db import migrations - -class Migration(migrations.Migration): - """ - Add database indexes to optimize student list generation queries - """ - - dependencies = [ - ('programme_curriculum', '0025_update_minority_values'), - ] - - operations = [ - # Add database indexes for optimized query performance - migrations.RunSQL( - sql=[ - # Main composite index for course registration queries - """ - CREATE INDEX IF NOT EXISTS idx_course_reg_main_query - ON course_registration(session, semester_type, course_id_id, registration_type, student_id_id); - """, - - # Individual indexes for course registration - """ - CREATE INDEX IF NOT EXISTS idx_course_reg_session_semester_course - ON course_registration(session, semester_type, course_id_id); - """, - - """ - CREATE INDEX IF NOT EXISTS idx_course_reg_student - ON course_registration(student_id_id); - """, - - """ - CREATE INDEX IF NOT EXISTS idx_course_reg_type - ON course_registration(registration_type); - """ - ], - - # Reverse migration to drop indexes - reverse_sql=[ - "DROP INDEX IF EXISTS idx_course_reg_main_query;", - "DROP INDEX IF EXISTS idx_course_reg_session_semester_course;", - "DROP INDEX IF EXISTS idx_course_reg_student;", - "DROP INDEX IF EXISTS idx_course_reg_type;" - ] - ) - ] diff --git a/FusionIIIT/applications/programme_curriculum/migrations/0027_auto_20250918_1616.py b/FusionIIIT/applications/programme_curriculum/migrations/0027_auto_20250918_1616.py deleted file mode 100644 index f94783a25..000000000 --- a/FusionIIIT/applications/programme_curriculum/migrations/0027_auto_20250918_1616.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.1.5 on 2025-09-18 16:16 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('programme_curriculum', '0026_add_database_indexes'), - ] - - operations = [ - migrations.AlterField( - model_name='studentbatchupload', - name='minority', - field=models.TextField(blank=True, help_text='Minority Status (e.g., Muslim, Christian, Sikh, Buddhist, Jain, Parsi, etc.)', null=True), - ), - ] diff --git a/FusionIIIT/applications/programme_curriculum/migrations/0028_add_new_student_fields.py b/FusionIIIT/applications/programme_curriculum/migrations/0028_add_new_student_fields.py deleted file mode 100644 index 52b12300d..000000000 --- a/FusionIIIT/applications/programme_curriculum/migrations/0028_add_new_student_fields.py +++ /dev/null @@ -1,68 +0,0 @@ -# Generated by Django 3.1.5 on 2025-11-05 02:53 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('programme_curriculum', '0027_auto_20250918_1616'), - ] - - operations = [ - migrations.AddField( - model_name='studentbatchupload', - name='admission_mode', - field=models.CharField(blank=True, choices=[('JEE Main', 'JEE Main'), ('JEE Advanced', 'JEE Advanced'), ('GATE', 'GATE'), ('DASA', 'DASA'), ('Foreign National', 'Foreign National'), ('Sponsored', 'Sponsored'), ('Any other (remarks)', 'Any other (remarks)')], max_length=50, null=True), - ), - migrations.AddField( - model_name='studentbatchupload', - name='admission_mode_remarks', - field=models.TextField(blank=True, help_text="Required when admission mode is 'Any other (remarks)'", null=True), - ), - migrations.AddField( - model_name='studentbatchupload', - name='blood_group', - field=models.CharField(blank=True, choices=[('A+', 'A+'), ('A-', 'A-'), ('B+', 'B+'), ('B-', 'B-'), ('AB+', 'AB+'), ('AB-', 'AB-'), ('O+', 'O+'), ('O-', 'O-'), ('Other', 'Other')], max_length=10, null=True), - ), - migrations.AddField( - model_name='studentbatchupload', - name='blood_group_remarks', - field=models.TextField(blank=True, help_text="Blood group remarks when 'Other' is selected", null=True), - ), - migrations.AddField( - model_name='studentbatchupload', - name='country', - field=models.CharField(blank=True, default='India', max_length=100, null=True), - ), - migrations.AddField( - model_name='studentbatchupload', - name='income', - field=models.DecimalField(blank=True, decimal_places=2, help_text='Annual family income', max_digits=10, null=True), - ), - migrations.AddField( - model_name='studentbatchupload', - name='income_group', - field=models.CharField(blank=True, choices=[('Below 1 Lakh', 'Below 1 Lakh'), ('1-2.5 Lakhs', '1-2.5 Lakhs'), ('2.5-5 Lakhs', '2.5-5 Lakhs'), ('5-8 Lakhs', '5-8 Lakhs'), ('Above 8 Lakhs', 'Above 8 Lakhs')], max_length=30, null=True), - ), - migrations.AddField( - model_name='studentbatchupload', - name='nationality', - field=models.CharField(blank=True, default='Indian', max_length=100, null=True), - ), - migrations.AddField( - model_name='studentbatchupload', - name='parent_email', - field=models.EmailField(blank=True, help_text='Parent Email ID', max_length=254, null=True), - ), - migrations.AddField( - model_name='studentbatchupload', - name='pwd_category', - field=models.CharField(blank=True, choices=[('Locomotor Disability', 'Locomotor Disability'), ('Visual Impairment', 'Visual Impairment'), ('Hearing Impairment', 'Hearing Impairment'), ('Speech and Language Disability', 'Speech and Language Disability'), ('Intellectual Disability', 'Intellectual Disability'), ('Autism Spectrum Disorder', 'Autism Spectrum Disorder'), ('Multiple Disabilities', 'Multiple Disabilities'), ('Any other (remarks)', 'Any other (remarks)')], help_text='Required when PWD is YES', max_length=100, null=True), - ), - migrations.AddField( - model_name='studentbatchupload', - name='pwd_category_remarks', - field=models.TextField(blank=True, help_text="Required when PWD category is 'Any other (remarks)'", null=True), - ), - ] diff --git a/FusionIIIT/applications/programme_curriculum/migrations/0029_increase_allotted_field_lengths.py b/FusionIIIT/applications/programme_curriculum/migrations/0029_increase_allotted_field_lengths.py deleted file mode 100644 index 35a0a30a3..000000000 --- a/FusionIIIT/applications/programme_curriculum/migrations/0029_increase_allotted_field_lengths.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 3.1.5 on 2025-11-05 14:25 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('programme_curriculum', '0028_add_new_student_fields'), - ] - - operations = [ - migrations.AlterField( - model_name='studentbatchupload', - name='allotted_category', - field=models.CharField(blank=True, max_length=50, null=True), - ), - migrations.AlterField( - model_name='studentbatchupload', - name='allotted_gender', - field=models.CharField(blank=True, max_length=50, null=True), - ), - ] diff --git a/FusionIIIT/applications/programme_curriculum/migrations/0030_add_specialization_field.py b/FusionIIIT/applications/programme_curriculum/migrations/0030_add_specialization_field.py deleted file mode 100644 index 8e9a4803c..000000000 --- a/FusionIIIT/applications/programme_curriculum/migrations/0030_add_specialization_field.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 3.1.5 on 2025-11-11 10:32 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('programme_curriculum', '0029_increase_allotted_field_lengths'), - ] - - operations = [ - migrations.AddField( - model_name='studentbatchupload', - name='specialization', - field=models.CharField(blank=True, help_text='Specialization', max_length=200, null=True), - ), - migrations.AlterField( - model_name='studentbatchupload', - name='jee_app_no', - field=models.CharField(help_text='JEE App. No./CCMT Roll. No.', max_length=50, unique=True), - ), - ] diff --git a/FusionIIIT/applications/programme_curriculum/migrations/0031_add_curriculum_options_to_batch.py b/FusionIIIT/applications/programme_curriculum/migrations/0031_add_curriculum_options_to_batch.py deleted file mode 100644 index 1a44180c1..000000000 --- a/FusionIIIT/applications/programme_curriculum/migrations/0031_add_curriculum_options_to_batch.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.1.5 on 2025-11-12 23:14 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('programme_curriculum', '0030_add_specialization_field'), - ] - - operations = [ - migrations.AddField( - model_name='batch', - name='curriculum_options', - field=models.JSONField(blank=True, null=True), - ), - ] diff --git a/FusionIIIT/applications/programme_curriculum/models.py b/FusionIIIT/applications/programme_curriculum/models.py index 54276ba6e..733217fff 100644 --- a/FusionIIIT/applications/programme_curriculum/models.py +++ b/FusionIIIT/applications/programme_curriculum/models.py @@ -1,17 +1,15 @@ from django.db import models from django import forms import datetime -import json from django.utils import timezone from django.db.models.fields import IntegerField, PositiveIntegerField from django.db.models import CheckConstraint, Q, F from django.utils.translation import gettext_lazy as _ from django.core.exceptions import ValidationError -from applications.globals.models import ExtraInfo,Faculty +from applications.globals.models import ExtraInfo from django.core.validators import MinValueValidator, MaxValueValidator, DecimalValidator -from applications.globals.models import (DepartmentInfo, Designation, - ExtraInfo, Faculty, HoldsDesignation) -from django.contrib.auth.models import User + +# Create your models here. PROGRAMME_CATEGORY_CHOICES = [ ('UG', 'Undergraduate'), @@ -30,75 +28,41 @@ ('Manufacturing', 'Manufacturing'), ('Management Science', 'Management Science'), ('Open Elective', 'Open Elective'), - ('Swayam', 'Swayam'), + ('Swayam','Swayam'), ('Project', 'Project'), ('Optional', 'Optional'), - ('Backlog', 'Backlog'), ('Others', 'Others') ] BATCH_NAMES = [ ('B.Tech', 'B.Tech'), ('M.Tech', 'M.Tech'), - ('M.Tech AI & ML', 'M.Tech AI & ML'), - ('M.Tech Data Science', 'M.Tech Data Science'), - ('M.Tech Communication and Signal Processing', 'M.Tech Communication and Signal Processing'), - ('M.Tech Nanoelectronics and VLSI Design', 'M.Tech Nanoelectronics and VLSI Design'), - ('M.Tech Power & Control', 'M.Tech Power & Control'), - ('M.Tech Design', 'M.Tech Design'), - ('M.Tech CAD/CAM', 'M.Tech CAD/CAM'), - ('M.Tech Manufacturing and Automation', 'M.Tech Manufacturing and Automation'), - ('B.Des', 'B.Des'), - ('M.Des', 'M.Des'), + ('B.Des','B.Des'), + ('M.Des','M.Des'), ('Phd', 'Phd') ] -VERSION_BUMP_CHOICES = [ - ('NONE', 'No Version Bump'), - ('PATCH', 'Patch (0.0.1)'), - ('MINOR', 'Minor (0.1.0)'), - ('MAJOR', 'Major (1.0.0)'), -] +class Programme(models.Model): + ''' + Current Purpose : To store the details regardina a programme + + -class CourseAuditLog(models.Model): - """Store audit trail for all course changes""" - - course = models.ForeignKey('Course', on_delete=models.CASCADE, related_name='audit_logs') - user = models.ForeignKey(User, on_delete=models.CASCADE) - timestamp = models.DateTimeField(auto_now_add=True) - action = models.CharField(max_length=20, choices=[ - ('CREATE', 'Create'), - ('UPDATE', 'Update'), - ('DELETE', 'Delete') - ], default='UPDATE') - old_values = models.JSONField(null=True, blank=True) # Store old field values - new_values = models.JSONField(null=True, blank=True) # Store new field values - changed_fields = models.JSONField(default=list) # List of field names that changed - version_bump_type = models.CharField(max_length=10, choices=VERSION_BUMP_CHOICES, default='NONE') - old_version = models.DecimalField(max_digits=5, decimal_places=1, null=True, blank=True) - new_version = models.DecimalField(max_digits=5, decimal_places=1, null=True, blank=True) - admin_override = models.BooleanField(default=False) # True if admin manually decided version bump - reason = models.TextField(blank=True) # Optional reason for changes - - class Meta: - ordering = ['-timestamp'] - - def __str__(self): - return f"{self.course.code} - {self.action} by {self.user.username} at {self.timestamp}" + ATTRIBUTES : + category(char) - to store the type of program(eg UG/PG) + name(char) - name of the program(eg 'Btech CSE' ) + program_begin_year(+ve Integer) - to store since when the programme is being offered -class Programme(models.Model): - """Store programme details""" + ! - the name attribute has ambiguous entries + ''' - category = models.CharField( - max_length=3, choices=PROGRAMME_CATEGORY_CHOICES, null=False, blank=False) - name = models.CharField(max_length=70, null=False, - unique=True, blank=False) - programme_begin_year = models.PositiveIntegerField( - default=datetime.date.today().year, null=False) + category = models.CharField(max_length=3, choices=PROGRAMME_CATEGORY_CHOICES, null=False, blank=False) + name = models.CharField(max_length=70, null=False, unique=True, blank=False) + programme_begin_year = models.PositiveIntegerField(default=datetime.date.today().year, null=False) - def __str__(self): - return str(self.category + " - " + self.name) + def _str_(self): + return str(self.category + " - "+ self.name) @property def curriculums(self): @@ -110,58 +74,95 @@ def get_discipline_objects(self): class Discipline(models.Model): - """Store discipline details""" + ''' + Current Purpose : To store the details regarding a discipline + + - name = models.CharField(max_length=100, null=False, - unique=True, blank=False) - acronym = models.CharField( - max_length=10, null=False, default="", blank=False) - programmes = models.ManyToManyField(Programme, blank=True) + ATTRIBUTES : - def __str__(self): + name(char) - to store the name of discipline(eg design, Computer science and engineering) + acronym(char) - the short form of the discipline(eg : CSE) + programmes(programme_curriculum.Programme) - to link a programme with the discipline(CSE in Btech, CSE in Mtech) + + ''' + + + name = models.CharField(max_length=100, null=False, unique=True, blank=False) + acronym = models.CharField(max_length=10, null=False, default="", blank=False) + programmes = models.ManyToManyField(Programme, blank=True) + + def _str_(self): return str(self.name) + " " + str(self.acronym) @property def batches(self): return Batch.objects.filter(discipline=self.id).order_by('year') - + class Curriculum(models.Model): - """Store curriculum details""" - - programme = models.ForeignKey( - Programme, on_delete=models.CASCADE, null=False) + ''' + Current Purpose : To store the details regarding a curriculum + Curriculum definition : a set of all courses offered by the institute within a programme + + + + + ATTRIBUTES : + + programmes(program_curriculum.Programme) - to link a program to a curriculum + name(char) - to store the name of the curriculum + version(positive Integer) - to store the version of a curriculum(used in cases of updating an existing curriculum) + working_curriculum(Boolean) - to check whether the curriculum is currently in execution or not + no_of_semester(Integer) - the number of semesters defined for the curriculum + min_credit(Integer) - the minimum credits required for the curriculum + ''' + programme = models.ForeignKey(Programme, on_delete=models.CASCADE, null=False) name = models.CharField(max_length=100, null=False, blank=False) version = models.DecimalField( - max_digits=5, - decimal_places=1, + max_digits=2, + decimal_places=1, default=1.0, - validators=[MinValueValidator(1.0), DecimalValidator(max_digits=5, decimal_places=1)]) + validators=[MinValueValidator(1.0), DecimalValidator(max_digits=2, decimal_places=1)]) working_curriculum = models.BooleanField(default=True, null=False) no_of_semester = models.PositiveIntegerField(default=1, null=False) min_credit = models.PositiveIntegerField(default=0, null=False) - latest_version = models.BooleanField(default=True) class Meta: unique_together = ('name', 'version',) - - def __str__(self): + + def _str_(self): return str(self.name + " v" + str(self.version)) @property def batches(self): return Batch.objects.filter(curriculum=self.id).order_by('year') + @property def semesters(self): return Semester.objects.filter(curriculum=self.id).order_by('semester_no') + class Semester(models.Model): - """Store semester details""" - - curriculum = models.ForeignKey( - Curriculum, null=False, on_delete=models.CASCADE) + ''' + Current Purpose : To store the details regarding a semester + + + + + + ATTRIBUTES : + + curriculum(programme_curriculum.Curriculum) - to store the link for the associated curriculum details for the semester + semester_no(int) - to store the semester number(format unclear) + instigate_semester(boolean) - might be used to check whether the semester is currently in action + start_semester(DateTime) - to store the start date of the semester + end_semester(DateTime) - to store the end date of the semester + + ''' + curriculum = models.ForeignKey(Curriculum, null=False, on_delete=models.CASCADE) semester_no = models.PositiveIntegerField(null=False) instigate_semester = models.BooleanField(default=False, null=True) start_semester = models.DateField(blank=True, null=True) @@ -170,25 +171,55 @@ class Semester(models.Model): class Meta: unique_together = ('curriculum', 'semester_no',) - - def __str__(self): - return str(Curriculum.__str__(self.curriculum) + ", sem-" + str(self.semester_no)) + + def _str_(self): + return str(Curriculum._str_(self.curriculum) + ", sem-" + str(self.semester_no)) @property def courseslots(self): return CourseSlot.objects.filter(semester=self.id).order_by("id") - class Course(models.Model): - """Store course details""" - + ''' + Current Purpose : To store the details regarding a course + + + + + + ATTRIBUTES : + + code(char) - the course code (eg CS3005) + name(char) - the name of the course(eg Machine Learning) + credit(Integer) - the credits defined for the course + lecture_hours(integer) - lecture hours defined for the course + tutorial_hours(Integer) - tutorial hours defined for the course + practical_hours(Integer) - practical hours defined for the course + discussion_hours(Integer) - discussion hours + project_hours(Integer) - project hours + pre_requisits(Boolean) - denote whether this course has prerequisites(courses that one should take before opting this ) + pre_requisit_courses(programme_curriculum.Course) - link to set of prerequisite courses + syllabus(text) - syllabus described for the course + percent_quiz_1(+ve int) - defined weightage in marking + percent_midsem(+ve int) - defined weightage in marking + percent_quiz_2(+ve int) - defined weightage in marking + percent_endsem (+ve int) - defined weightage in marking + percent_project(+ve int) - defined weightage in marking + percent_lab_evaluation (+ve int) - defined weightage in marking + percent_course_attendance (+ve int) - defined weightage in marking + ref_books(text) - reference books suggested for the course + working_course(boolean) - to denote whether the course is currently in execution or not + disciplines(programme_curriculum.Discipline) - to store which discipline is offering the course + + + ''' code = models.CharField(max_length=10, null=False, blank=False) name = models.CharField(max_length=100, null=False, blank=False) version = models.DecimalField( - max_digits=5, - decimal_places=1, - default=1.0, - validators=[MinValueValidator(1.0), DecimalValidator(max_digits=5, decimal_places=1)]) + max_digits=2, + decimal_places=1, + default=1.0, + validators=[MinValueValidator(1.0), DecimalValidator(max_digits=2, decimal_places=1)]) credit = models.PositiveIntegerField(default=0, null=False, blank=False) lecture_hours = PositiveIntegerField(null=True, ) tutorial_hours = PositiveIntegerField(null=True) @@ -198,74 +229,91 @@ class Course(models.Model): pre_requisits = models.TextField(null=True, blank=True) pre_requisit_courses = models.ManyToManyField('self', blank=True) syllabus = models.TextField() - percent_quiz_1 = models.PositiveIntegerField( - default=10, null=False, blank=False) - percent_midsem = models.PositiveIntegerField( - default=20, null=False, blank=False) - percent_quiz_2 = models.PositiveIntegerField( - default=10, null=False, blank=False) - percent_endsem = models.PositiveIntegerField( - default=30, null=False, blank=False) - percent_project = models.PositiveIntegerField( - default=15, null=False, blank=False) - percent_lab_evaluation = models.PositiveIntegerField( - default=10, null=False, blank=False) - percent_course_attendance = models.PositiveIntegerField( - default=5, null=False, blank=False) + percent_quiz_1 = models.PositiveIntegerField(default=10, null=False, blank=False) + percent_midsem = models.PositiveIntegerField(default=20, null=False, blank=False) + percent_quiz_2 = models.PositiveIntegerField(default=10, null=False, blank=False) + percent_endsem = models.PositiveIntegerField(default=30, null=False, blank=False) + percent_project = models.PositiveIntegerField(default=15, null=False, blank=False) + percent_lab_evaluation = models.PositiveIntegerField(default=10, null=False, blank=False) + percent_course_attendance = models.PositiveIntegerField(default=5, null=False, blank=False) ref_books = models.TextField() working_course = models.BooleanField(default=True) disciplines = models.ManyToManyField(Discipline, blank=True) latest_version = models.BooleanField(default=True) - max_seats = models.IntegerField(default=90) + class Meta: - unique_together = ('code', 'version') + unique_together = ('code','version') + + def _str_(self): + return str(self.code + " - " +self.name+"- v"+str(self.version)) - def __str__(self): - return str(self.code + " - " + self.name+"- v"+str(self.version)) @property def courseslots(self): return CourseSlot.objects.filter(courses=self.id) - class Batch(models.Model): - """Store batch details""" - - name = models.CharField(choices=BATCH_NAMES, - max_length=50, null=False, blank=False) - discipline = models.ForeignKey( - Discipline, null=False, on_delete=models.CASCADE) - year = models.PositiveIntegerField( - default=datetime.date.today().year, null=False) - curriculum = models.ForeignKey( - Curriculum, null=True, blank=True, on_delete=models.SET_NULL) - curriculum_options = models.JSONField(null=True, blank=True) + + + + ''' + Current Purpose : To store the details regarding a batch(eg details of curriculum assigned for batch) + + + + + + ATTRIBUTES : + + name(char) - to store the type of batch(eg Btech/Mtech/Phd, not nullable) + discipline(programme_curriculum.Discipline) - to link the discipline for the batch[not nullable] + year(+ve Integer) - to store the year of the batch(eg:2019 batch) + curriculum(programme_curriculum.Curriculum) - reference to the curriculum for the batch(can be null) + running_batch(Boolean) - to denote whether the batch is currently active or has graduated + + ''' + name = models.CharField(choices=BATCH_NAMES, max_length=50, null=False, blank=False) + discipline = models.ForeignKey(Discipline, null=False, on_delete=models.CASCADE) + year = models.PositiveIntegerField(default=datetime.date.today().year, null=False) + curriculum = models.ForeignKey(Curriculum, null=True, blank=True, on_delete=models.SET_NULL) running_batch = models.BooleanField(default=True) - total_seats = models.PositiveIntegerField(default=60, null=False, blank=False) class Meta: unique_together = ('name', 'discipline', 'year',) - def __str__(self): + def _str_(self): return str(self.name) + " " + str(self.discipline.acronym) + " " + str(self.year) - -class CourseSlot(models.Model): - """Store course slot details""" - semester = models.ForeignKey( - Semester, null=False, on_delete=models.CASCADE) +class CourseSlot(models.Model): + ''' + Current Purpose : To store the details regarding a course slot + Course slot : is defined as per the curriculum for a programme to have specific type of courses + for a given semester + ATTRIBUTES : + + semester(programme_curriculum.Semester) - [not nullable] to denote link to the semester details for which the courseslot is made + name(char) - [not nullable] the domain of the course[Professional Elective 1, IT Lab etc ] + type(char) - [not nullable] the type of course(elective/ core/NS/) + course slot_info - [can be null] stores textual info regarding course + courses(programme_curriculum.Course) - the list of courses being floated for the slot(can be null) + duration(integer) - might be to denote for how many semester will the course run for(eg PR's are run across 2 semesters) + minimum_registration_limit(integer) - minimum students required for a course + maximum_registration_limit(integer) - maximum students required for a course + + ''' + semester = models.ForeignKey(Semester, null=False, on_delete=models.CASCADE) name = models.CharField(max_length=100, null=False, blank=False) - type = models.CharField( - max_length=70, choices=COURSESLOT_TYPE_CHOICES, null=False) + type = models.CharField(max_length=70, choices=COURSESLOT_TYPE_CHOICES, null=False) course_slot_info = models.TextField(null=True) courses = models.ManyToManyField(Course, blank=True) duration = models.PositiveIntegerField(default=1) - min_registration_limit = models.PositiveIntegerField(default=0) - max_registration_limit = models.PositiveIntegerField(default=1000) + min_registration_limit = models.PositiveIntegerField(default = 0) + max_registration_limit = models.PositiveIntegerField(default = 1000) + - def __str__(self): - return str(Semester.__str__(self.semester) + ", " + self.name) + def _str_(self): + return str(Semester._str_(self.semester) + ", " + self.name) class Meta: unique_together = ('semester', 'name', 'type') @@ -274,101 +322,15 @@ class Meta: def for_batches(self): return ((Semester.objects.get(id=self.semester.id)).curriculum).batches - class CourseInstructor(models.Model): - course_id = models.ForeignKey(Course, on_delete=models.CASCADE) - instructor_id = models.ForeignKey(Faculty, on_delete=models.CASCADE) - year = models.IntegerField(default=datetime.date.today().year, null=False) - SEMESTER_TYPE_CHOICES = [ - ("Odd Semester", "Odd Semester"), - ("Even Semester", "Even Semester"), - ("Summer Semester", "Summer Semester"), - ] - semester_type = models.CharField( - max_length=20, - choices=SEMESTER_TYPE_CHOICES, - null=True - ) - - class Meta: - unique_together = ('course_id', 'instructor_id', 'year', 'semester_type') + course_id = models.ForeignKey(Course, on_delete = models.CASCADE) + instructor_id = models.ForeignKey(ExtraInfo, on_delete = models.CASCADE) + batch_id = models.ForeignKey(Batch, on_delete=models.CASCADE, default=1) + #change extra info to faculty(globals) - @property - def academic_year(self): - """Returns academic year in format 'YYYY-YY'""" - if self.semester_type == "Even Semester": - start_year = self.year - 1 - end_year = self.year - else: - start_year = self.year - end_year = self.year + 1 - - return f"{start_year}-{str(end_year)[-2:]}" - - -class NewProposalFile(models.Model): - - uploader = models.CharField(max_length=100, null=False, blank=False) - designation = models.CharField(max_length=100, null=False, blank=False) - code = models.CharField(max_length=10, null=False, blank=False) - name = models.CharField(max_length=100, null=False, blank=False) - credit = models.PositiveIntegerField(default=3, null=False, blank=False) - lecture_hours = PositiveIntegerField(default=3, null=True, ) - tutorial_hours = PositiveIntegerField(default=0, null=True) - pratical_hours = PositiveIntegerField(default=0, null=True) - discussion_hours = PositiveIntegerField(default=0, null=True) - project_hours = PositiveIntegerField(default=0, null=True) - pre_requisits = models.TextField(null=True, blank=True) - pre_requisit_courses = models.ManyToManyField(Course, blank=True) - syllabus = models.TextField() - max_seats = models.IntegerField(default=90) - percent_quiz_1 = models.PositiveIntegerField( - default=10, null=False, blank=False) - percent_midsem = models.PositiveIntegerField( - default=20, null=False, blank=False) - percent_quiz_2 = models.PositiveIntegerField( - default=10, null=False, blank=False) - percent_endsem = models.PositiveIntegerField( - default=30, null=False, blank=False) - percent_project = models.PositiveIntegerField( - default=15, null=False, blank=False) - percent_lab_evaluation = models.PositiveIntegerField( - default=10, null=False, blank=False) - percent_course_attendance = models.PositiveIntegerField( - default=5, null=False, blank=False) - ref_books = models.TextField() - subject = models.CharField(max_length=100, null=True, blank=False) - description = models.CharField(max_length=400, null=True, blank=False) - upload_date = models.DateTimeField(auto_now_add=True) - is_read = models.BooleanField(default=False) - is_update = models.BooleanField(default=False) - is_archive = models.BooleanField(default=False) - - class Meta: - unique_together = ('code', 'uploader', 'name') - - def __str__(self): - return str(self.uploader + " - " + self.designation + " - " + self.code + " - " + self.name) - - -class Proposal_Tracking(models.Model): - - file_id = models.CharField(max_length=100, null=False, blank=False) - current_id = models.CharField(max_length=100, null=False, blank=False) - current_design = models.CharField(max_length=100, null=False, blank=False) - receive_id = models.ForeignKey(User, blank=False, on_delete=models.CASCADE) - receive_design = models.ForeignKey(Designation, blank=False, on_delete=models.CASCADE) - disciplines = models.ForeignKey(Discipline, blank=False, on_delete=models.CASCADE) - receive_date = models.DateTimeField(auto_now_add=True) - forward_date = models.DateTimeField(auto_now_add=True) - remarks = models.CharField(max_length=250, null=True, blank=True) - is_added = models.BooleanField(default=False) - is_submitted = models.BooleanField(default=False) - is_rejected = models.BooleanField(default=False) - sender_archive = models.BooleanField(default=False) - receiver_archive = models.BooleanField(default=False) - - class Meta: - unique_together = ('file_id', 'current_id', 'current_design', 'disciplines') + class Meta: + unique_together = ('course_id', 'instructor_id', 'batch_id') + - \ No newline at end of file + def _self_(self): + return '{} - {}'.format(self.course_id, self.instructor_id) \ No newline at end of file diff --git a/FusionIIIT/applications/programme_curriculum/models_password_email.py b/FusionIIIT/applications/programme_curriculum/models_password_email.py deleted file mode 100644 index 62a3caa03..000000000 --- a/FusionIIIT/applications/programme_curriculum/models_password_email.py +++ /dev/null @@ -1,240 +0,0 @@ -# Password Email Management Models for Student Management - -from django.db import models -from django.contrib.auth.models import User -from django.utils import timezone -from .models_student_management import StudentBatchUpload - - -class PasswordEmailLog(models.Model): - """ - Model to log password emails sent to students for audit purposes - """ - EMAIL_STATUS_CHOICES = [ - ('SENT', 'Successfully Sent'), - ('FAILED', 'Failed to Send'), - ('PENDING', 'Pending'), - ('BOUNCED', 'Email Bounced'), - ] - - student = models.ForeignKey( - StudentBatchUpload, - on_delete=models.CASCADE, - related_name='password_emails' - ) - sent_to_email = models.EmailField(help_text="Email address where password was sent") - sent_by = models.ForeignKey( - User, - on_delete=models.SET_NULL, - null=True, - blank=True, - help_text="Admin who triggered the password email" - ) - email_status = models.CharField( - max_length=10, - choices=EMAIL_STATUS_CHOICES, - default='PENDING' - ) - password_generated = models.CharField( - max_length=100, - help_text="The password that was generated (for audit)", - blank=True, - null=True - ) - email_subject = models.CharField(max_length=255, blank=True, null=True) - email_content_preview = models.TextField( - blank=True, - null=True, - help_text="First 200 chars of email content" - ) - error_message = models.TextField(blank=True, null=True) - attempts_count = models.IntegerField(default=1) - ip_address = models.GenericIPAddressField(blank=True, null=True) - user_agent = models.TextField(blank=True, null=True) - sent_at = models.DateTimeField(auto_now_add=True) - last_attempt_at = models.DateTimeField(auto_now=True) - - class Meta: - db_table = 'password_email_log' - verbose_name = 'Password Email Log' - verbose_name_plural = 'Password Email Logs' - ordering = ['-sent_at'] - indexes = [ - models.Index(fields=['email_status']), - models.Index(fields=['sent_at']), - models.Index(fields=['student']), - ] - - def __str__(self): - return f"{self.student.name} - {self.sent_to_email} ({self.email_status}) at {self.sent_at}" - - def mark_as_sent(self, password=None): - """Mark email as successfully sent""" - self.email_status = 'SENT' - if password: - self.password_generated = password - self.save(update_fields=['email_status', 'password_generated', 'last_attempt_at']) - - def mark_as_failed(self, error_message=None): - """Mark email as failed""" - self.email_status = 'FAILED' - if error_message: - self.error_message = error_message - self.attempts_count += 1 - self.save(update_fields=['email_status', 'error_message', 'attempts_count', 'last_attempt_at']) - - -class BulkPasswordEmailOperation(models.Model): - """ - Model to track bulk password email operations - """ - OPERATION_STATUS_CHOICES = [ - ('INITIATED', 'Initiated'), - ('IN_PROGRESS', 'In Progress'), - ('COMPLETED', 'Completed'), - ('FAILED', 'Failed'), - ('CANCELLED', 'Cancelled'), - ] - - operation_id = models.CharField(max_length=100, unique=True) - initiated_by = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True) - programme_type = models.CharField(max_length=10, blank=True, null=True) - year = models.IntegerField(blank=True, null=True) - branch_filter = models.CharField(max_length=200, blank=True, null=True) - - total_students = models.IntegerField(default=0) - emails_sent = models.IntegerField(default=0) - emails_failed = models.IntegerField(default=0) - emails_pending = models.IntegerField(default=0) - - operation_status = models.CharField( - max_length=15, - choices=OPERATION_STATUS_CHOICES, - default='INITIATED' - ) - start_time = models.DateTimeField(auto_now_add=True) - end_time = models.DateTimeField(blank=True, null=True) - duration_seconds = models.IntegerField(blank=True, null=True) - error_summary = models.TextField(blank=True, null=True) - notes = models.TextField(blank=True, null=True) - - class Meta: - db_table = 'bulk_password_email_operation' - verbose_name = 'Bulk Password Email Operation' - verbose_name_plural = 'Bulk Password Email Operations' - ordering = ['-start_time'] - - def __str__(self): - return f"Bulk Email {self.operation_id} - {self.operation_status} ({self.emails_sent}/{self.total_students})" - - def calculate_completion_rate(self): - """Calculate completion percentage""" - if self.total_students == 0: - return 0 - completed = self.emails_sent + self.emails_failed - return round((completed / self.total_students) * 100, 2) - - def mark_completed(self): - """Mark operation as completed""" - self.operation_status = 'COMPLETED' - self.end_time = timezone.now() - if self.start_time and self.end_time: - self.duration_seconds = int((self.end_time - self.start_time).total_seconds()) - self.save(update_fields=['operation_status', 'end_time', 'duration_seconds']) - - def update_stats(self): - """Update email statistics from related PasswordEmailLog entries""" - logs = PasswordEmailLog.objects.filter( - student__in=self.get_target_students() - ).filter(sent_at__gte=self.start_time) - - self.emails_sent = logs.filter(email_status='SENT').count() - self.emails_failed = logs.filter(email_status='FAILED').count() - self.emails_pending = logs.filter(email_status='PENDING').count() - - self.save(update_fields=['emails_sent', 'emails_failed', 'emails_pending']) - - def get_target_students(self): - """Get the students targeted by this bulk operation""" - queryset = StudentBatchUpload.objects.all() - - if self.programme_type: - queryset = queryset.filter(programme_type=self.programme_type) - if self.year: - queryset = queryset.filter(year=self.year) - if self.branch_filter: - queryset = queryset.filter(branch__icontains=self.branch_filter) - - return queryset - - -class StudentPasswordHistory(models.Model): - """ - Model to track password changes for students - """ - student = models.ForeignKey( - StudentBatchUpload, - on_delete=models.CASCADE, - related_name='password_history' - ) - password_hash = models.CharField(max_length=128) # Store hashed passwords - created_by = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True) - created_at = models.DateTimeField(auto_now_add=True) - is_initial_password = models.BooleanField(default=False) - is_active = models.BooleanField(default=True) - password_changed_at = models.DateTimeField(blank=True, null=True) - change_reason = models.CharField(max_length=100, blank=True, null=True) - - class Meta: - db_table = 'student_password_history' - verbose_name = 'Student Password History' - verbose_name_plural = 'Student Password History' - ordering = ['-created_at'] - - def __str__(self): - return f"{self.student.name} - Password created at {self.created_at}" - - -class EmailTemplate(models.Model): - """ - Model to store customizable email templates for password emails - """ - TEMPLATE_TYPE_CHOICES = [ - ('INITIAL_PASSWORD', 'Initial Password Email'), - ('PASSWORD_RESET', 'Password Reset Email'), - ('BULK_PASSWORD', 'Bulk Password Email'), - ('REMINDER', 'Login Reminder Email'), - ] - - template_type = models.CharField(max_length=20, choices=TEMPLATE_TYPE_CHOICES, unique=True) - subject_template = models.CharField(max_length=255) - html_template = models.TextField() - text_template = models.TextField(blank=True, null=True) - variables_info = models.JSONField( - default=dict, - help_text="JSON object describing available template variables" - ) - is_active = models.BooleanField(default=True) - created_by = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True) - created_at = models.DateTimeField(auto_now_add=True) - updated_at = models.DateTimeField(auto_now=True) - - class Meta: - db_table = 'email_template' - verbose_name = 'Email Template' - verbose_name_plural = 'Email Templates' - - def __str__(self): - return f"{self.get_template_type_display()} Template" - - def render_subject(self, context): - """Render subject with provided context""" - from django.template import Context, Template - template = Template(self.subject_template) - return template.render(Context(context)) - - def render_html(self, context): - """Render HTML content with provided context""" - from django.template import Context, Template - template = Template(self.html_template) - return template.render(Context(context)) diff --git a/FusionIIIT/applications/programme_curriculum/models_student_management.py b/FusionIIIT/applications/programme_curriculum/models_student_management.py deleted file mode 100644 index ba95d853c..000000000 --- a/FusionIIIT/applications/programme_curriculum/models_student_management.py +++ /dev/null @@ -1,691 +0,0 @@ -# Student Management Models for Programme Curriculum - -import secrets -import string -from datetime import datetime -from django.db import models -from django.contrib.auth.models import User -from django.core.validators import RegexValidator -from django.utils import timezone - - -def get_current_academic_year(): - """ - Calculate current academic year based on date - Logic: July 2025 - June 2026 = Academic Year 2025 - """ - current_date = datetime.now() - if current_date.month >= 7: # July onwards = current year - return current_date.year - else: # January-June = previous year - return current_date.year - 1 - - -class BatchConfiguration(models.Model): - """ - Model to store batch configuration and seat management - """ - PROGRAMME_CHOICES = [ - ('B.Tech', 'Bachelor of Technology'), - ('B.Des', 'Bachelor of Design'), - ('M.Tech', 'Master of Technology'), - ('M.Des', 'Master of Design'), - ('PhD', 'Doctor of Philosophy'), - ] - - DISCIPLINE_CHOICES = [ - ('Computer Science and Engineering', 'Computer Science and Engineering'), - ('Electronics and Communication Engineering', 'Electronics and Communication Engineering'), - ('Mechanical Engineering', 'Mechanical Engineering'), - ('Smart Manufacturing', 'Smart Manufacturing'), - ('Design', 'Design'), - ] - - programme = models.CharField(max_length=50, choices=PROGRAMME_CHOICES) - discipline = models.CharField(max_length=100, choices=DISCIPLINE_CHOICES) - year = models.IntegerField(help_text="Academic year (e.g., 2025 for 2025-26)") - total_seats = models.IntegerField(default=60) - filled_seats = models.IntegerField(default=0, editable=False) - available_seats = models.IntegerField(default=0, editable=False) - created_at = models.DateTimeField(auto_now_add=True) - updated_at = models.DateTimeField(auto_now=True) - - class Meta: - unique_together = ['programme', 'discipline', 'year'] - verbose_name = 'Batch Configuration' - verbose_name_plural = 'Batch Configurations' - ordering = ['programme', 'discipline', 'year'] - - def __str__(self): - return f"{self.programme} - {self.discipline} ({self.year})" - - def calculate_seats(self): - """Calculate filled and available seats based on curriculum assignment""" - try: - from applications.programme_curriculum.models import Batch - batch = Batch.objects.filter( - name=self.programme, - discipline__name=self.discipline, - year=self.year, - running_batch=True - ).first() - - if self.filled_seats != student_count: - self.filled_seats = student_count - self.available_seats = max(0, self.total_seats - self.filled_seats) - self.save(update_fields=['filled_seats', 'available_seats']) - - except Exception as e: - self.filled_seats = 0 - self.available_seats = self.total_seats - - -class StudentBatchUpload(models.Model): - """ - Model to store student data uploaded via Excel or manual entry - """ - GENDER_CHOICES = [ - ('Male', 'Male'), - ('Female', 'Female'), - ('Other', 'Other'), - ] - - CATEGORY_CHOICES = [ - ('GEN', 'General'), - ('OBC', 'Other Backward Class'), - ('SC', 'Scheduled Caste'), - ('ST', 'Scheduled Tribe'), - ('EWS', 'Economically Weaker Section'), - ] - - PWD_CHOICES = [ - ('YES', 'Yes'), - ('NO', 'No'), - ] - - PWD_CATEGORY_CHOICES = [ - ('Locomotor Disability', 'Locomotor Disability'), - ('Visual Impairment', 'Visual Impairment'), - ('Hearing Impairment', 'Hearing Impairment'), - ('Speech and Language Disability', 'Speech and Language Disability'), - ('Intellectual Disability', 'Intellectual Disability'), - ('Autism Spectrum Disorder', 'Autism Spectrum Disorder'), - ('Multiple Disabilities', 'Multiple Disabilities'), - ('Any other (remarks)', 'Any other (remarks)'), - ] - - BLOOD_GROUP_CHOICES = [ - ('A+', 'A+'), - ('A-', 'A-'), - ('B+', 'B+'), - ('B-', 'B-'), - ('AB+', 'AB+'), - ('AB-', 'AB-'), - ('O+', 'O+'), - ('O-', 'O-'), - ('Other', 'Other'), - ] - - ADMISSION_MODE_CHOICES = [ - ('JEE Main', 'JEE Main'), - ('JEE Advanced', 'JEE Advanced'), - ('GATE', 'GATE'), - ('DASA', 'DASA'), - ('Foreign National', 'Foreign National'), - ('Sponsored', 'Sponsored'), - ('Any other (remarks)', 'Any other (remarks)'), - ] - - INCOME_GROUP_CHOICES = [ - ('Below 1 Lakh', 'Below 1 Lakh'), - ('1-2.5 Lakhs', '1-2.5 Lakhs'), - ('2.5-5 Lakhs', '2.5-5 Lakhs'), - ('5-8 Lakhs', '5-8 Lakhs'), - ('Above 8 Lakhs', 'Above 8 Lakhs'), - ] - - REPORTED_STATUS_CHOICES = [ - ('NOT_REPORTED', 'Not Reported'), - ('REPORTED', 'Reported'), - ('WITHDRAWAL', 'Withdrawal'), - ] - - PROGRAMME_TYPE_CHOICES = [ - ('ug', 'Undergraduate'), - ('pg', 'Postgraduate'), - ('phd', 'PhD'), - ] - - # Core identification fields - jee_app_no = models.CharField(max_length=50, unique=True, blank=True, null=True, help_text="JEE App. No./CCMT Roll. No.") - roll_number = models.CharField(max_length=20, unique=True, blank=True, null=True, help_text="Institute Roll Number") - institute_email = models.EmailField(blank=True, null=True, help_text="Institute Email ID") - - # Personal information - name = models.CharField(max_length=200, help_text="Full Name") - father_name = models.CharField(max_length=200, help_text="Father's Name") - mother_name = models.CharField(max_length=200, help_text="Mother's Name") - gender = models.CharField(max_length=10, choices=GENDER_CHOICES) - category = models.CharField(max_length=10, choices=CATEGORY_CHOICES) - pwd = models.CharField(max_length=3, choices=PWD_CHOICES, default='NO', help_text="Person with Disability") - minority = models.TextField(blank=True, null=True, help_text="Minority Status (e.g., Muslim, Christian, Sikh, Buddhist, Jain, Parsi, etc.)") - blood_group = models.CharField(max_length=10, choices=BLOOD_GROUP_CHOICES, blank=True, null=True) - blood_group_remarks = models.TextField(blank=True, null=True, help_text="Blood group remarks when 'Other' is selected") - pwd_category = models.CharField(max_length=100, choices=PWD_CATEGORY_CHOICES, blank=True, null=True, help_text="Required when PWD is YES") - pwd_category_remarks = models.TextField(blank=True, null=True, help_text="Required when PWD category is 'Any other (remarks)'") - - # Contact and address - phone_number = models.CharField(max_length=15, blank=True, null=True, help_text="Mobile Number") - personal_email = models.EmailField(blank=True, null=True, help_text="Personal Email ID") - address = models.TextField(help_text="Full Address") - state = models.CharField(max_length=100, blank=True, null=True) - country = models.CharField(max_length=100, blank=True, null=True, default='India') - nationality = models.CharField(max_length=100, blank=True, null=True, default='Indian') - - # Academic information - branch = models.CharField(max_length=200, help_text="Discipline/Branch") - specialization = models.CharField(max_length=200, blank=True, null=True, help_text="Specialization") - date_of_birth = models.DateField(blank=True, null=True) - ai_rank = models.IntegerField(blank=True, null=True, help_text="JEE AI Rank", db_column='jee_rank') - category_rank = models.IntegerField(blank=True, null=True, help_text="Category Rank") - income_group = models.CharField(max_length=30, choices=INCOME_GROUP_CHOICES, blank=True, null=True) - income = models.DecimalField(max_digits=10, decimal_places=2, blank=True, null=True, help_text="Annual family income") - - # Additional academic details - tenth_marks = models.FloatField(blank=True, null=True, help_text="10th Class Marks (%)") - twelfth_marks = models.FloatField(blank=True, null=True, help_text="12th Class Marks (%)") - - # Family information - father_occupation = models.CharField(max_length=200, blank=True, null=True) - father_mobile = models.CharField(max_length=15, blank=True, null=True) - mother_occupation = models.CharField(max_length=200, blank=True, null=True) - mother_mobile = models.CharField(max_length=15, blank=True, null=True) - parent_email = models.EmailField(blank=True, null=True, help_text="Parent Email ID") - aadhar_number = models.CharField(max_length=12, blank=True, null=True) - - # Allotment details - allotted_category = models.CharField(max_length=50, blank=True, null=True) - allotted_gender = models.CharField(max_length=50, blank=True, null=True) - admission_mode = models.CharField(max_length=50, choices=ADMISSION_MODE_CHOICES, blank=True, null=True) - admission_mode_remarks = models.TextField(blank=True, null=True, help_text="Required when admission mode is 'Any other (remarks)'") - - # System fields - year = models.IntegerField(help_text="Academic Year", db_column='batch_year', default=get_current_academic_year) - academic_year = models.CharField(max_length=20, help_text="Academic Year String (e.g., 2025-26)", db_column='academic_year', blank=True) - programme_type = models.CharField(max_length=10, choices=PROGRAMME_TYPE_CHOICES) - allocation_status = models.CharField(max_length=50, default='ALLOCATED', help_text="Allocation Status") - reported_status = models.CharField(max_length=20, choices=REPORTED_STATUS_CHOICES, default='NOT_REPORTED') - - # Source tracking - Track where the student data came from - source = models.CharField( - max_length=50, - default='admin_upload', - help_text='Source of student data (admin_upload, excel_upload, manual_entry, etc.)' - ) - - # Authentication - Map to existing user_account_id column instead of creating OneToOneField - user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True, related_name='student_profile', db_column='user_account_id') - - # Email password field - Store plain password temporarily for email notifications - # This field is cleared after email is sent for security - email_password = models.CharField(max_length=50, blank=True, null=True, help_text="Temporary storage for email notification") - password_email_sent = models.BooleanField(default=False, help_text="Whether password email has been sent") - password_generated_at = models.DateTimeField(blank=True, null=True, help_text="When password was generated") - - # Metadata - Map to existing columns - created_at = models.DateTimeField(auto_now_add=True) - updated_at = models.DateTimeField(auto_now=True) - uploaded_by = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True, related_name='uploaded_students', db_column='created_by_id') - - class Meta: - verbose_name = 'Student Batch Upload' - verbose_name_plural = 'Student Batch Uploads' - ordering = ['roll_number', 'name'] - indexes = [ - models.Index(fields=['programme_type', 'year']), - models.Index(fields=['branch']), - models.Index(fields=['reported_status']), - models.Index(fields=['jee_app_no']), - models.Index(fields=['roll_number']), - ] - - def __str__(self): - return f"{self.name} ({self.roll_number or self.jee_app_no})" - - def get_programme_name(self): - """Get the full programme name based on programme_type and branch""" - if self.programme_type == 'ug': - if 'design' in self.branch.lower(): - return 'B.Des' - else: - return 'B.Tech' - elif self.programme_type == 'pg': - if 'design' in self.branch.lower(): - return 'M.Des' - else: - return 'M.Tech' - elif self.programme_type == 'phd': - return 'PhD' - return 'Unknown' - - def get_display_branch(self): - """Get short display name for branch""" - branch_lower = self.branch.lower() - if 'computer' in branch_lower or 'cse' in branch_lower: - return 'CSE' - elif 'electronics' in branch_lower or 'ece' in branch_lower: - return 'ECE' - elif 'mechanical' in branch_lower or 'me' in branch_lower: - return 'ME' - elif 'smart' in branch_lower or 'manufacturing' in branch_lower: - return 'SM' - elif 'design' in branch_lower: - return 'DES' - return self.branch - - @property - def first_name(self): - """Get first name from full name""" - if self.name: - return self.name.split()[0] - return '' - - @property - def last_name(self): - """Get last name from full name""" - if self.name: - name_parts = self.name.split() - if len(name_parts) > 1: - return ' '.join(name_parts[1:]) - return '' - - def clean_branch_name(self, branch_text): - """Clean branch name by removing parenthetical content""" - import re - if not branch_text: - return branch_text - # Remove content in parentheses like "(4 Years, Bachelor of Technology)" - cleaned = re.sub(r'\s*\([^)]*\)', '', branch_text) - return cleaned.strip() - - def clean(self): - """Validate conditional field requirements""" - from django.core.exceptions import ValidationError - - # PWD Category validation - if self.pwd == 'YES' and not self.pwd_category: - raise ValidationError('PWD Category is required when PWD is YES') - - # PWD Category remarks validation - if self.pwd_category == 'Any other (remarks)' and not self.pwd_category_remarks: - raise ValidationError('PWD Category remarks are required when selecting "Any other (remarks)"') - - # Admission mode remarks validation - if self.admission_mode == 'Any other (remarks)' and not self.admission_mode_remarks: - raise ValidationError('Admission mode remarks are required when selecting "Any other (remarks)"') - - # Blood group remarks validation - if self.blood_group == 'Other' and not self.blood_group_remarks: - raise ValidationError('Blood group remarks are required when selecting "Other"') - - def save(self, *args, **kwargs): - """Override save to automatically set academic_year string and normalize emails""" - if not self.academic_year: - year = self.year or get_current_academic_year() - next_year = (year + 1) % 100 - self.academic_year = f"{year}-{next_year:02d}" - - if self.roll_number and not self.institute_email: - self.institute_email = f"{self.roll_number.lower()}@iiitdmj.ac.in" - - # Normalize email addresses to lowercase - if self.institute_email: - self.institute_email = self.institute_email.lower() - if self.personal_email: - self.personal_email = self.personal_email.lower() - if self.parent_email: - self.parent_email = self.parent_email.lower() - - # Clean branch name - if self.branch: - self.branch = self.clean_branch_name(self.branch) - - super().save(*args, **kwargs) - - def create_user_account(self, password=None): - """Create Django User account for this student""" - from django.contrib.auth.models import User - from django.db import IntegrityError - import secrets - import string - - if self.user: - return self.user, None - - if not password: - password = self.generate_secure_password() - - username = self.roll_number or self.jee_app_no - email = (self.institute_email or self.personal_email or '').lower() - - try: - existing_user = User.objects.filter(username=username).first() - if existing_user: - self.user = existing_user - self.save() - return existing_user, None - - user = User.objects.create_user( - username=username, - email=email, - password=password, - first_name=self.name.split()[0] if self.name else '', - last_name=' '.join(self.name.split()[1:]) if len(self.name.split()) > 1 else '', - is_active=True - ) - - self.user = user - - from django.utils import timezone - self.email_password = password - self.password_generated_at = timezone.now() - self.password_email_sent = False - self.save() - - return user, password - - except IntegrityError as e: - if 'username' in str(e): - existing_user = User.objects.filter(username=username).first() - if existing_user: - self.user = existing_user - self.save() - return existing_user, None - raise e - - def get_user_account(self): - """Get the Django User account for this student""" - return self.user - - def has_user_account(self): - """Check if student has a Django User account""" - return self.user is not None - - def update_user_password(self, new_password): - """Update user password""" - if self.user: - self.user.set_password(new_password) - self.user.save() - return True - return False - - @staticmethod - def generate_secure_password(length=12): - """Generate cryptographically secure password""" - lowercase = string.ascii_lowercase - uppercase = string.ascii_uppercase - digits = string.digits - special = "!@#$%" - - password = [ - secrets.choice(lowercase), - secrets.choice(uppercase), - secrets.choice(digits), - secrets.choice(special) - ] - - all_chars = lowercase + uppercase + digits + special - for _ in range(length - 4): - password.append(secrets.choice(all_chars)) - - secrets.SystemRandom().shuffle(password) - return ''.join(password) - - def create_complete_student_profile(self): - """Manually trigger complete student profile creation""" - if self.reported_status != 'REPORTED': - raise ValueError("Student must be in REPORTED status to create complete profile") - - if not self.roll_number: - raise ValueError("Student must have a roll number to create complete profile") - - from .signals import create_django_user, create_extra_info, create_academic_student - from django.db import transaction - - try: - with transaction.atomic(): - user, password = create_django_user(self) - extra_info = create_extra_info(self, user) - academic_student = create_academic_student(self, extra_info) - - self.user = user - self.save(update_fields=['user']) - - return { - 'success': True, - 'user': user, - 'extra_info': extra_info, - 'academic_student': academic_student, - 'password': password - } - - except Exception as e: - return { - 'success': False, - 'error': str(e) - } - - def has_complete_profile(self): - """ - Check if student has complete profile (User + ExtraInfo + Academic Student) - """ - if not self.user: - return False - - try: - # Check if ExtraInfo exists - extra_info = self.user.extrainfo - - # Check if Academic Student exists - from applications.academic_information.models import Student - academic_student = Student.objects.get(id=extra_info) - - return True - except: - return False - - def get_profile_status(self): - """ - Get detailed status of student profile creation - """ - status = { - 'has_user': bool(self.user), - 'has_extra_info': False, - 'has_academic_student': False, - 'is_complete': False - } - - if self.user: - try: - extra_info = self.user.extrainfo - status['has_extra_info'] = True - - # Check academic student - from applications.academic_information.models import Student - academic_student = Student.objects.get(id=extra_info) - status['has_academic_student'] = True - - status['is_complete'] = True - except: - pass - - return status - - -class StudentStatusLog(models.Model): - """ - Model to log changes in student status for audit purposes - """ - student = models.ForeignKey(StudentBatchUpload, on_delete=models.CASCADE, related_name='status_logs') - changed_by = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True) - old_reported_status = models.CharField(max_length=20) - new_reported_status = models.CharField(max_length=20) - change_reason = models.TextField(blank=True, null=True) - ip_address = models.GenericIPAddressField(blank=True, null=True) - created_at = models.DateTimeField(auto_now_add=True) - - class Meta: - verbose_name = 'Student Status Log' - verbose_name_plural = 'Student Status Logs' - ordering = ['-created_at'] - - def __str__(self): - return f"{self.student.name} - {self.old_reported_status} → {self.new_reported_status} at {self.created_at}" - - -class UploadHistory(models.Model): - """ - Model to track upload history and statistics - """ - UPLOAD_TYPE_CHOICES = [ - ('excel', 'Excel Upload'), - ('manual', 'Manual Entry'), - ('bulk', 'Bulk Import'), - ] - - upload_type = models.CharField(max_length=20, choices=UPLOAD_TYPE_CHOICES) - programme_type = models.CharField(max_length=10, choices=StudentBatchUpload.PROGRAMME_TYPE_CHOICES) - total_records = models.IntegerField(default=0) - successful_records = models.IntegerField(default=0) - failed_records = models.IntegerField(default=0) - file_name = models.CharField(max_length=255, blank=True, null=True) - uploaded_by = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True) - upload_notes = models.TextField(blank=True, null=True) - created_at = models.DateTimeField(auto_now_add=True) - - class Meta: - verbose_name = 'Upload History' - verbose_name_plural = 'Upload History' - ordering = ['-created_at'] - - def __str__(self): - return f"{self.upload_type} - {self.programme_type} ({self.created_at.strftime('%Y-%m-%d %H:%M')})" - - @property - def success_rate(self): - """Calculate success rate percentage""" - if self.total_records == 0: - return 0 - return round((self.successful_records / self.total_records) * 100, 2) - - -# Django Signal for Automatic Student Profile Creation -from django.db.models.signals import post_save -from django.dispatch import receiver - -def create_student_profiles_automatically(students_list): - """Create student profiles automatically""" - from django.utils import timezone - from applications.globals.models import ExtraInfo, DepartmentInfo - from applications.programme_curriculum.models import Batch, Discipline, Curriculum, Programme - from applications.academic_information.models import Student as AcademicStudent - from applications.globals.models import Designation, HoldsDesignation - import json - - results = [] - - for student in students_list: - try: - if student.user: - results.append({'success': True, 'student': student.name, 'skipped': True}) - continue - - user, password = student.create_user_account() - if user: - pass - - if student.roll_number and student.user: - branch_field = student.branch or '' - branch_upper = branch_field.upper() - - if 'COMPUTER SCIENCE' in branch_upper or 'CSE' in branch_upper: - dept_name = 'CSE' - elif 'ELECTRONICS' in branch_upper or 'ECE' in branch_upper: - dept_name = 'ECE' - elif 'DESIGN' in branch_upper: - dept_name = 'Design' - else: - dept_name = 'CSE' - - department = DepartmentInfo.objects.get_or_create(name=dept_name)[0] - - extra_info, created = ExtraInfo.objects.get_or_create( - id=student.roll_number, - defaults={ - 'user': student.user, - 'title': 'Mr.' if student.gender == 'Male' else 'Ms.', - 'sex': 'M' if student.gender == 'Male' else 'F', - 'user_type': 'student', - 'department': department - } - ) - - if student.user and student.roll_number: - from applications.academic_information.models import Student as AcademicStudent - - try: - extra_info = ExtraInfo.objects.get(id=student.roll_number) - - academic_student, created = AcademicStudent.objects.get_or_create( - id=extra_info, - defaults={ - 'programme': student.get_programme_name(), - 'batch': student.year, - 'cpi': 0.0, - 'category': student.category or 'General', - 'father_name': student.father_name or '', - 'mother_name': student.mother_name or '', - 'hall_no': 0, - 'room_no': '', - 'specialization': '' - } - ) - - except ExtraInfo.DoesNotExist: - pass - except Exception as e: - pass - - if student.user: - student_designation = Designation.objects.filter(name='student').first() - if student_designation: - holds_designation, created = HoldsDesignation.objects.get_or_create( - user=student.user, - designation=student_designation, - defaults={'working': student.user} - ) - - results.append({'success': True, 'student': student.name}) - - except Exception as e: - results.append({'success': False, 'student': student.name, 'error': str(e)}) - - return results - -@receiver(post_save, sender=StudentBatchUpload) -def auto_create_student_profile(sender, instance, created, **kwargs): - """Automatically create student profile when status changes to REPORTED""" - if instance.reported_status == 'REPORTED' and not instance.user: - try: - post_save.disconnect(auto_create_student_profile, sender=StudentBatchUpload) - - try: - automation_result = create_student_profiles_automatically([instance]) - finally: - post_save.connect(auto_create_student_profile, sender=StudentBatchUpload) - - except Exception as e: - try: - post_save.connect(auto_create_student_profile, sender=StudentBatchUpload) - except: - pass diff --git a/FusionIIIT/applications/programme_curriculum/signals.py b/FusionIIIT/applications/programme_curriculum/signals.py deleted file mode 100644 index 79411e6b7..000000000 --- a/FusionIIIT/applications/programme_curriculum/signals.py +++ /dev/null @@ -1,241 +0,0 @@ -"""Django Signals for Student Management""" - -from django.db.models.signals import post_save, pre_save -from django.dispatch import receiver -from django.contrib.auth.models import User -from django.db import transaction, IntegrityError -from django.utils import timezone -import logging - -from .models_student_management import StudentBatchUpload, StudentStatusLog -from applications.globals.models import ExtraInfo, DepartmentInfo -from applications.academic_information.models import Student -from applications.programme_curriculum.models import Batch - -logger = logging.getLogger(__name__) - -@receiver(pre_save, sender=StudentBatchUpload) -def log_status_change(sender, instance, **kwargs): - """Log status changes for audit purposes""" - if instance.pk: - try: - old_instance = StudentBatchUpload.objects.get(pk=instance.pk) - if old_instance.reported_status != instance.reported_status: - StudentStatusLog.objects.create( - student=instance, - old_reported_status=old_instance.reported_status, - new_reported_status=instance.reported_status, - change_reason=f"Status changed from {old_instance.reported_status} to {instance.reported_status}", - created_at=timezone.now() - ) - except StudentBatchUpload.DoesNotExist: - pass - - - -@receiver(post_save, sender=StudentBatchUpload) -def handle_withdrawal_status(sender, instance, created, **kwargs): - """Handle when student status is changed to WITHDRAWAL""" - if instance.reported_status == 'WITHDRAWAL': - if instance.user: - instance.user.is_active = False - instance.user.save() - - -def create_django_user(student_upload): - """Create Django User account for the student""" - username = student_upload.roll_number - existing_user = User.objects.filter(username=username).first() - - if existing_user: - return existing_user, None - - password = StudentBatchUpload.generate_secure_password() - - user = User.objects.create_user( - username=username, - email=student_upload.institute_email or student_upload.personal_email, - password=password, - first_name=student_upload.first_name, - last_name=student_upload.last_name, - is_active=True - ) - - return user, password - - -def create_extra_info(student_upload, user): - """Create ExtraInfo record for the student""" - existing_extra_info = ExtraInfo.objects.filter(id=student_upload.roll_number).first() - if existing_extra_info: - return existing_extra_info - - department = get_department_for_branch(student_upload.branch) - sex = 'M' if student_upload.gender == 'Male' else 'F' if student_upload.gender == 'Female' else 'O' - - extra_info = ExtraInfo.objects.create( - id=student_upload.roll_number, - user=user, - title='Mr.' if student_upload.gender == 'Male' else 'Ms.', - sex=sex, - date_of_birth=student_upload.date_of_birth or timezone.now().date(), - user_status='PRESENT', - address=student_upload.address or '', - phone_no=int(student_upload.phone_number) if student_upload.phone_number and student_upload.phone_number.isdigit() else 9999999999, - user_type='student', - department=department, - about_me=f"Student from {student_upload.branch} batch {student_upload.year}", - date_modified=timezone.now() - ) - - return extra_info - - -def create_academic_student(student_upload, extra_info): - """Create Academic Student record""" - existing_student = Student.objects.filter(id=extra_info).first() - if existing_student: - return existing_student - - batch = get_batch_for_student(student_upload) - - category_mapping = { - 'GEN': 'GEN', - 'General': 'GEN', - 'OBC': 'OBC', - 'SC': 'SC', - 'ST': 'ST', - 'EWS': 'GEN', - } - category = category_mapping.get(student_upload.category, 'GEN') - - academic_student = Student.objects.create( - id=extra_info, - programme=student_upload.get_programme_name(), - batch=student_upload.year, - batch_id=batch, - cpi=0.0, - category=category, - father_name=student_upload.father_name or '', - mother_name=student_upload.mother_name or '', - hall_no=0, - room_no='', - specialization='None', - curr_semester_no=1 - ) - - return academic_student - - -def get_department_for_branch(branch): - """Get department based on branch name""" - branch_lower = branch.lower() - - department_mapping = { - 'computer': 'Computer Science and Engineering', - 'cse': 'Computer Science and Engineering', - 'electronics': 'Electronics and Communication Engineering', - 'ece': 'Electronics and Communication Engineering', - 'mechanical': 'Mechanical Engineering', - 'me': 'Mechanical Engineering', - 'smart': 'Smart Manufacturing', - 'manufacturing': 'Smart Manufacturing', - 'design': 'Design', - } - - for key, dept_name in department_mapping.items(): - if key in branch_lower: - dept = DepartmentInfo.objects.filter(name=dept_name).first() - if dept: - return dept - - return DepartmentInfo.objects.first() - - -def get_batch_for_student(student_upload): - """Get or create Batch for the student""" - discipline_mapping = { - 'Computer Science and Engineering': 'Computer Science and Engineering', - 'Electronics and Communication Engineering': 'Electronics and Communication Engineering', - 'Mechanical Engineering': 'Mechanical Engineering', - 'Smart Manufacturing': 'Smart Manufacturing', - 'Design': 'Design', - } - - branch_lower = student_upload.branch.lower() - discipline_name = None - - for key, value in discipline_mapping.items(): - if key.lower() in branch_lower: - discipline_name = value - break - - if not discipline_name: - if 'computer' in branch_lower or 'cse' in branch_lower: - discipline_name = 'Computer Science and Engineering' - elif 'electronics' in branch_lower or 'ece' in branch_lower: - discipline_name = 'Electronics and Communication Engineering' - elif 'mechanical' in branch_lower or 'me' in branch_lower: - discipline_name = 'Mechanical Engineering' - elif 'smart' in branch_lower or 'manufacturing' in branch_lower: - discipline_name = 'Smart Manufacturing' - elif 'design' in branch_lower: - discipline_name = 'Design' - - from applications.programme_curriculum.models import Discipline - - try: - discipline = Discipline.objects.filter(name=discipline_name).first() - if discipline: - batch = Batch.objects.filter( - discipline=discipline, - year=student_upload.year - ).first() - if batch: - return batch - except Exception as e: - pass - - return None - - -def send_welcome_email(student_upload, password): - """Send welcome email with login credentials""" - try: - from django.core.mail import send_mail - from django.conf import settings - - subject = f"Welcome to IIITDM Jabalpur - Login Credentials" - message = f""" -Dear {student_upload.name}, - -Welcome to IIITDM Jabalpur! Your account has been successfully created. - -Login Credentials: -Username: {student_upload.roll_number} -Password: {password} -Email: {student_upload.institute_email} - -Please login to the student portal and change your password immediately. - -Best regards, -Academic Section -IIITDM Jabalpur -""" - - email_to = student_upload.institute_email or student_upload.personal_email - if email_to: - send_mail( - subject, - message, - settings.DEFAULT_FROM_EMAIL, - [email_to], - fail_silently=False, - ) - - student_upload.password_email_sent = True - student_upload.email_password = '' - student_upload.save(update_fields=['password_email_sent', 'email_password']) - - except Exception as e: - pass diff --git a/FusionIIIT/applications/programme_curriculum/static/samples/sample_course_instructor.xls b/FusionIIIT/applications/programme_curriculum/static/samples/sample_course_instructor.xls deleted file mode 100644 index 5d7c6eaa7..000000000 Binary files a/FusionIIIT/applications/programme_curriculum/static/samples/sample_course_instructor.xls and /dev/null differ diff --git a/FusionIIIT/applications/programme_curriculum/urls.py b/FusionIIIT/applications/programme_curriculum/urls.py index 99e2a9cf7..85355b0ca 100644 --- a/FusionIIIT/applications/programme_curriculum/urls.py +++ b/FusionIIIT/applications/programme_curriculum/urls.py @@ -2,7 +2,7 @@ from django.urls import path, include from . import views from django.contrib import admin -from applications.programme_curriculum.api import views as v2 + app_name = 'programme_curriculum' urlpatterns = [ @@ -19,7 +19,6 @@ path('course//', views.view_a_course, name='view_a_course'), path('disciplines/', views.view_all_discplines, name='view_all_discplines'), path('batches/', views.view_all_batches, name='view_all_batches'), - path('admin_programmes/', views.admin_view_all_programmes, name='admin_view_all_programmes'), path('admin_working_curriculums/', views.admin_view_all_working_curriculums, name='admin_view_all_working_curriculums'), @@ -49,33 +48,4 @@ path('admin_edit_discipline//', views.edit_discipline_form, name='edit_discipline_form'), path('admin_instigate_semester//', views.instigate_semester, name='instigate_semester'), path('admin_replicate_curriculum//', views.replicate_curriculum, name='replicate_curriculum'), - - - - #new - path('view_course_proposal_forms/',views.view_course_proposal_forms,name='view_course_proposal_forms'), - path('faculty_view_all_courses/', views.faculty_view_all_courses, name='faculty_view_all_courses'), - path('faculty_view_a_course//',views.faculty_view_a_course,name="faculty_view_a_course"), - path('reject_form//', views.reject_form, name='reject_form'), - path('new_course_proposal_file/',views.new_course_proposal_file,name='new_course_proposal_file'), - path('update_course_proposal_file//',views.update_course_proposal_file,name='update_course_proposal_file'), - - path('view_a_course_proposal_form//',views.view_a_course_proposal_form,name='view_a_course_proposal_form'), - path('filetracking//',views.filetracking,name='filetracking'), - path('inward_files/',views.inward_files,name='inward_files'), - path('forward_course_forms//',views.forward_course_forms,name='forward_course_forms'), - path('view_inward_files//',views.view_inward_files,name='view_inward_files'), - path('outward_files/',views.outward_files,name='outward_files'), - path('tracking_archive//',views.tracking_archive,name='tracking_archive'), - path('tracking_unarchive//',views.tracking_unarchive,name='tracking_unarchive'), - path('file_archive//',views.file_archive,name='file_archive'), - path('file_unarchive//',views.file_unarchive,name='file_unarchive'), - - path('admin_instructor/',views.admin_view_all_course_instructor,name='admin_view_all_course_instructor'), - path('admin_add_course_instructor/', views.add_course_instructor, name='add_course_instructor'), - path('admin_update_course_instructor//', views.update_course_instructor_form, name='update_course_instructor_form'), - - - # urls for api view - path('api/',include('applications.programme_curriculum.api.urls')) -] +] \ No newline at end of file diff --git a/FusionIIIT/applications/programme_curriculum/views.py b/FusionIIIT/applications/programme_curriculum/views.py index 0903acf41..018e096cd 100644 --- a/FusionIIIT/applications/programme_curriculum/views.py +++ b/FusionIIIT/applications/programme_curriculum/views.py @@ -1,25 +1,20 @@ from django.db.models.query_utils import Q from django.http import request -from django.shortcuts import get_object_or_404, render, HttpResponse,redirect -from django.http import HttpResponse, HttpResponseRedirect, JsonResponse -# import itertools +from django.shortcuts import get_object_or_404, render, HttpResponse +from django.http import HttpResponse, HttpResponseRedirect +import itertools from django.contrib import messages from django.contrib.auth.decorators import login_required from django.contrib.auth.models import User -from .models import Programme, Discipline, Curriculum, Semester, Course, Batch, CourseSlot,NewProposalFile,Proposal_Tracking,CourseInstructor -from .forms import ProgrammeForm, DisciplineForm, CurriculumForm, SemesterForm, CourseForm, BatchForm, CourseSlotForm, ReplicateCurriculumForm,NewCourseProposalFile,CourseProposalTrackingFile, CourseInstructorForm -from .filters import CourseFilter, BatchFilter, CurriculumFilter,CourseInstructorFilter -from django.db import IntegrityError, transaction -from django.utils import timezone -import xlrd - -from notification.views import prog_and_curr_notif -# from applications.academic_information.models import Student -from applications.globals.models import (DepartmentInfo, Designation,ExtraInfo, Faculty, HoldsDesignation) -# ------------module-functions---------------# - +from .models import Programme, Discipline, Curriculum, Semester, Course, Batch, CourseSlot +from .forms import ProgrammeForm, DisciplineForm, CurriculumForm, SemesterForm, CourseForm, BatchForm, CourseSlotForm, ReplicateCurriculumForm +from .filters import CourseFilter, BatchFilter, CurriculumFilter +# from applications.academic_information.models import Student +from applications.globals.models import (DepartmentInfo, Designation, + ExtraInfo, Faculty, HoldsDesignation) +# ------------module-functions---------------# @login_required(login_url='/accounts/login') def programme_curriculum(request): @@ -33,15 +28,11 @@ def programme_curriculum(request): user_details - Gets the information about the logged in user. des - Gets the designation about the looged in user. """ - user=request.user - user_details = ExtraInfo.objects.get(user = request.user) des = HoldsDesignation.objects.all().filter(user = request.user).first() - if request.session['currentDesignationSelected']== "student" or request.session['currentDesignationSelected']== "Associate Professor" or request.session['currentDesignationSelected']== "Professor" or request.session['currentDesignationSelected']== "Assistant Professor" or request.session['currentDesignationSelected']== "Dean Academic": + if str(des.designation) == "student" or str(des.designation) == "Associate Professor" or str(des.designation) == "Professor" or str(des.designation) == "Assistant Professor" : return HttpResponseRedirect('/programme_curriculum/programmes/') - elif 'hod' in request.session['currentDesignationSelected'].lower() : - return HttpResponseRedirect('/programme_curriculum/programmes/') - elif request.session['currentDesignationSelected'] in ["acadadmin", "studentacadadmin"]: + elif str(request.user) == "acadadmin" : return HttpResponseRedirect('/programme_curriculum/admin_programmes') return HttpResponseRedirect('/programme_curriculum/programmes/') @@ -60,25 +51,12 @@ def view_all_programmes(request): pg - PG programmes phd - PHD programmes """ - url='programme_curriculum/' - user_details = ExtraInfo.objects.get(user = request.user) - des = HoldsDesignation.objects.all().filter(user = request.user).first() - if request.session['currentDesignationSelected'] in ["acadadmin", "studentacadadmin"]: - return render(request, 'programme_curriculum/admin_programmes/') - - elif request.session['currentDesignationSelected']== "Associate Professor" or request.session['currentDesignationSelected']== "Professor" or request.session['currentDesignationSelected']== "Assistant Professor" or request.session['currentDesignationSelected']== "Dean Academic" : - url+='faculty/' - - elif 'hod' in request.session['currentDesignationSelected'].lower(): - url+='faculty/' - notifs = request.user.notifications.all() - ug = Programme.objects.filter(category='UG') pg = Programme.objects.filter(category='PG') phd = Programme.objects.filter(category='PHD') - url+='view_all_programmes.html' - return render(request, url, {'ug': ug, 'pg': pg, 'phd': phd,'notifications': notifs,}) + + return render(request, 'programme_curriculum/view_all_programmes.html', {'ug': ug, 'pg': pg, 'phd': phd}) def view_curriculums_of_a_programme(request, programme_id): @@ -94,18 +72,6 @@ def view_curriculums_of_a_programme(request, programme_id): working_curriculum - Curriculums that are affective past_curriculum - Curriculums thet are obsolete """ - url='programme_curriculum/' - user_details = ExtraInfo.objects.get(user = request.user) - des = HoldsDesignation.objects.all().filter(user = request.user).first() - if request.session['currentDesignationSelected'] in ["acadadmin", "studentacadadmin"]: - return render(request, 'programme_curriculum/admin_programmes/') - - elif request.session['currentDesignationSelected']== "Associate Professor" or request.session['currentDesignationSelected']== "Professor" or request.session['currentDesignationSelected']== "Assistant Professor" or request.session['currentDesignationSelected']== "Dean Academic" : - url+='faculty/' - - elif 'hod' in request.session['currentDesignationSelected'].lower(): - url+='faculty/' - notifs = request.user.notifications.all() program = get_object_or_404(Programme, Q(id=programme_id)) curriculums = program.curriculums @@ -115,31 +81,21 @@ def view_curriculums_of_a_programme(request, programme_id): working_curriculums = curriculums.filter(working_curriculum=1) past_curriculums = curriculums.filter(working_curriculum=0) - url+='view_curriculums_of_a_programme.html' - return render(request,url, {'program': program, 'past_curriculums': past_curriculums, 'working_curriculums': working_curriculums, 'curriculumfilter': curriculumfilter,'notifications': notifs,}) + + return render(request,'programme_curriculum/view_curriculums_of_a_programme.html', {'program': program, 'past_curriculums': past_curriculums, 'working_curriculums': working_curriculums, 'curriculumfilter': curriculumfilter}) def view_all_working_curriculums(request): - """ views all the working curriculums offered by the institute """ - url='programme_curriculum/' - user_details = ExtraInfo.objects.get(user = request.user) - des = HoldsDesignation.objects.all().filter(user = request.user).first() - if request.session['currentDesignationSelected'] in ["acadadmin", "studentacadadmin"]: - return render(request, 'programme_curriculum/admin_programmes/') - - elif request.session['currentDesignationSelected']== "Associate Professor" or request.session['currentDesignationSelected']== "Professor" or request.session['currentDesignationSelected']== "Assistant Professor" or request.session['currentDesignationSelected']== "Dean Academic" : - - url+='faculty/' - elif 'hod' in request.session['currentDesignationSelected'].lower(): - url+='faculty/' curriculums = Curriculum.objects.filter(working_curriculum=1) - notifs = request.user.notifications.all() + curriculumfilter = CurriculumFilter(request.GET, queryset=curriculums) curriculums = curriculumfilter.qs - return render(request,url+'view_all_working_curriculums.html',{'curriculums':curriculums, 'curriculumfilter': curriculumfilter,'notifications': notifs,}) + return render(request,'programme_curriculum/view_all_working_curriculums.html',{'curriculums':curriculums, 'curriculumfilter': curriculumfilter}) + + def view_semesters_of_a_curriculum(request, curriculum_id): """ @@ -152,20 +108,7 @@ def view_semesters_of_a_curriculum(request, curriculum_id): transpose_semester_slots - semester_slots 2D list is transpose for viewing in HTML . semester_credits - Total Credits for each semester. """ - url='programme_curriculum/' - user_details = ExtraInfo.objects.get(user = request.user) - des = HoldsDesignation.objects.all().filter(user = request.user).first() - if request.session['currentDesignationSelected'] in ["acadadmin", "studentacadadmin"]: - return render(request, 'programme_curriculum/admin_programmes/') - - - elif request.session['currentDesignationSelected']== "Associate Professor" or request.session['currentDesignationSelected']== "Professor" or request.session['currentDesignationSelected']== "Assistant Professor" or request.session['currentDesignationSelected']== "Dean Academic" : - url+='faculty/' - - elif 'hod' in request.session['currentDesignationSelected'].lower(): - url+='faculty/' curriculum = get_object_or_404(Curriculum, Q(id=curriculum_id)) - notifs = request.user.notifications.all() semesters = curriculum.semesters semester_slots = [] for sem in semesters: @@ -190,118 +133,54 @@ def view_semesters_of_a_curriculum(request, curriculum_id): max_credit = max(max_credit, course.credit) credits_sum = credits_sum + max_credit semester_credits.append(credits_sum) + + print (semester_credits) transpose_semester_slots = list(zip(*semester_slots)) - return render(request, url+'view_semesters_of_a_curriculum.html', {'curriculum': curriculum, 'semesters': semesters, 'semester_slots': transpose_semester_slots, 'semester_credits': semester_credits,'notifications': notifs,}) + return render(request, 'programme_curriculum/view_semesters_of_a_curriculum.html', {'curriculum': curriculum, 'semesters': semesters, 'semester_slots': transpose_semester_slots, 'semester_credits': semester_credits}) def view_a_semester_of_a_curriculum(request, semester_id): """ views a specfic semester of a specfic curriculum """ - url='programme_curriculum/' - user_details = ExtraInfo.objects.get(user = request.user) - des = HoldsDesignation.objects.all().filter(user = request.user).first() - if request.session['currentDesignationSelected'] in ["acadadmin", "studentacadadmin"]: - return render(request, 'programme_curriculum/admin_programmes/') - elif request.session['currentDesignationSelected']== "Associate Professor" or request.session['currentDesignationSelected']== "Professor" or request.session['currentDesignationSelected']== "Assistant Professor" or request.session['currentDesignationSelected']== "Dean Academic" : - url+='faculty/' - - elif 'hod' in request.session['currentDesignationSelected'].lower(): - url+='faculty/' semester = get_object_or_404(Semester, Q(id=semester_id)) course_slots = semester.courseslots - notifs = request.user.notifications.all() - return render(request, url+'view_a_semester_of_a_curriculum.html', {'semester': semester, 'course_slots': course_slots,'notifications': notifs,}) + return render(request, 'programme_curriculum/view_a_semester_of_a_curriculum.html', {'semester': semester, 'course_slots': course_slots}) def view_a_courseslot(request, courseslot_id): """ view a course slot """ - url='programme_curriculum/' - user_details = ExtraInfo.objects.get(user = request.user) - des = HoldsDesignation.objects.all().filter(user = request.user).first() - if request.session['currentDesignationSelected'] in ["acadadmin", "studentacadadmin"]: - return render(request, 'programme_curriculum/admin_programmes/') - elif request.session['currentDesignationSelected']== "Associate Professor" or request.session['currentDesignationSelected']== "Professor" or request.session['currentDesignationSelected']== "Assistant Professor" or request.session['currentDesignationSelected']== "Dean Academic" : - url+='faculty/' - - elif 'hod' in request.session['currentDesignationSelected'].lower(): - url+='faculty/' course_slot = get_object_or_404(CourseSlot, Q(id=courseslot_id)) - notifs = request.user.notifications.all() - return render(request, url+'view_a_courseslot.html', {'course_slot': course_slot,'notifications': notifs,}) + return render(request, 'programme_curriculum/view_a_courseslot.html', {'course_slot': course_slot}) def view_all_courses(request): """ views all the course slots of a specfic semester """ - url='programme_curriculum/' - user_details = ExtraInfo.objects.get(user = request.user) - des = HoldsDesignation.objects.all().filter(user = request.user).first() - if request.session['currentDesignationSelected'] in ["acadadmin", "studentacadadmin"]: - return render(request, 'programme_curriculum/admin_programmes/') - elif request.session['currentDesignationSelected']== "Associate Professor" or request.session['currentDesignationSelected']== "Professor" or request.session['currentDesignationSelected']== "Assistant Professor" or request.session['currentDesignationSelected']== "Dean Academic" : - - url+='faculty/' - - elif 'hod' in request.session['currentDesignationSelected'].lower(): - url+='faculty/' courses = Course.objects.all() - notifs = request.user.notifications.all() coursefilter = CourseFilter(request.GET, queryset=courses) courses = coursefilter.qs - return render(request, url+'view_all_courses.html', {'courses': courses, 'coursefilter': coursefilter,'notifications': notifs,}) + return render(request, 'programme_curriculum/view_all_courses.html', {'courses': courses, 'coursefilter': coursefilter}) def view_a_course(request, course_id): """ views the details of a Course """ - url='programme_curriculum/' - user_details = ExtraInfo.objects.get(user = request.user) - des = HoldsDesignation.objects.all().filter(user = request.user).first() - if request.session['currentDesignationSelected'] in ["acadadmin", "studentacadadmin"]: - return render(request, 'programme_curriculum/admin_programmes/') - elif request.session['currentDesignationSelected']== "Associate Professor" or request.session['currentDesignationSelected']== "Professor" or request.session['currentDesignationSelected']== "Assistant Professor" or request.session['currentDesignationSelected']== "Dean Academic" : - - url+='faculty/' - elif 'hod' in request.session['currentDesignationSelected'].lower(): - url+='faculty/' course = get_object_or_404(Course, Q(id=course_id)) - notifs = request.user.notifications.all() - return render(request, url+'view_a_course.html', {'course': course,'notifications': notifs,}) + return render(request, 'programme_curriculum/view_a_course.html', {'course': course}) def view_all_discplines(request): """ views the details of a Course """ - url='programme_curriculum/' - user_details = ExtraInfo.objects.get(user = request.user) - des = HoldsDesignation.objects.all().filter(user = request.user).first() - if request.session['currentDesignationSelected'] in ["acadadmin", "studentacadadmin"]: - return render(request, 'programme_curriculum/admin_programmes/') - elif request.session['currentDesignationSelected']== "Associate Professor" or request.session['currentDesignationSelected']== "Professor" or request.session['currentDesignationSelected']== "Assistant Professor" or request.session['currentDesignationSelected']== "Dean Academic" : - url+='faculty/' - - elif 'hod' in request.session['currentDesignationSelected'].lower(): - url+='faculty/' disciplines = Discipline.objects.all() - notifs = request.user.notifications.all() - return render(request, url+'view_all_disciplines.html', {'disciplines': disciplines,'notifications': notifs,}) + return render(request, 'programme_curriculum/view_all_disciplines.html', {'disciplines': disciplines}) def view_all_batches(request): """ views the details of a Course """ - url='programme_curriculum/' - user_details = ExtraInfo.objects.get(user = request.user) - des = HoldsDesignation.objects.all().filter(user = request.user).first() - if request.session['currentDesignationSelected'] in ["acadadmin", "studentacadadmin"]: - return render(request, 'programme_curriculum/admin_programmes/') - elif request.session['currentDesignationSelected']== "Associate Professor" or request.session['currentDesignationSelected']== "Professor" or request.session['currentDesignationSelected']== "Assistant Professor" or request.session['currentDesignationSelected']== "Dean Academic" : - - url+='faculty/' - elif 'hod' in request.session['currentDesignationSelected'].lower(): - url+='faculty/' batches = Batch.objects.all().order_by('year') @@ -312,9 +191,8 @@ def view_all_batches(request): finished_batches = batches.filter(running_batch=False) batches = batches.filter(running_batch=True) - notifs = request.user.notifications.all() - return render(request, url+'view_all_batches.html', {'batches': batches, 'finished_batches': finished_batches, 'batchfilter': batchfilter,'notifications': notifs,}) + return render(request, 'programme_curriculum/view_all_batches.html', {'batches': batches, 'finished_batches': finished_batches, 'batchfilter': batchfilter}) @@ -333,12 +211,10 @@ def admin_view_all_programmes(request): """ user_details = ExtraInfo.objects.get(user = request.user) des = HoldsDesignation.objects.all().filter(user = request.user).first() - if request.session['currentDesignationSelected']== "student" or request.session['currentDesignationSelected']== "Associate Professor" or request.session['currentDesignationSelected']== "Professor" or request.session['currentDesignationSelected']== "Assistant Professor" : + if str(des.designation) == "student" or str(des.designation) == "Associate Professor" or str(des.designation) == "Professor" or str(des.designation) == "Assistant Professor" : return HttpResponseRedirect('/programme_curriculum/programmes/') - elif str(request.user) in ["acadadmin", "studentacadadmin"]: + elif str(request.user) == "acadadmin" : pass - elif 'hod' in request.session['currentDesignationSelected'].lower(): - return HttpResponseRedirect('/programme_curriculum/programmes/') ug = Programme.objects.filter(category='UG') pg = Programme.objects.filter(category='PG') @@ -347,6 +223,7 @@ def admin_view_all_programmes(request): return render(request, 'programme_curriculum/acad_admin/admin_view_all_programmes.html', {'ug': ug, 'pg': pg, "phd": phd}) +@login_required(login_url='/accounts/login') def admin_view_curriculums_of_a_programme(request, programme_id): """ This function is used to Display Curriculum of a specific Programmes. @@ -362,12 +239,10 @@ def admin_view_curriculums_of_a_programme(request, programme_id): """ user_details = ExtraInfo.objects.get(user = request.user) des = HoldsDesignation.objects.all().filter(user = request.user).first() - if request.session['currentDesignationSelected']== "student" or request.session['currentDesignationSelected']== "Associate Professor" or request.session['currentDesignationSelected']== "Professor" or request.session['currentDesignationSelected']== "Assistant Professor" : + if str(des.designation) == "student" or str(des.designation) == "Associate Professor" or str(des.designation) == "Professor" or str(des.designation) == "Assistant Professor" : return HttpResponseRedirect('/programme_curriculum/programmes/') - elif str(request.user) in ["acadadmin", "studentacadadmin"]: + elif str(request.user) == "acadadmin" : pass - elif 'hod' in request.session['currentDesignationSelected'].lower(): - return HttpResponseRedirect('/programme_curriculum/programmes/') program = get_object_or_404(Programme, Q(id=programme_id)) curriculums = program.curriculums @@ -382,17 +257,16 @@ def admin_view_curriculums_of_a_programme(request, programme_id): return render(request,'programme_curriculum/acad_admin/admin_view_curriculums_of_a_programme.html', {'program': program, 'past_curriculums': past_curriculums, 'working_curriculums': working_curriculums, 'curriculumfilter': curriculumfilter}) +@login_required(login_url='/accounts/login') def admin_view_all_working_curriculums(request): """ views all the working curriculums offered by the institute """ user_details = ExtraInfo.objects.get(user = request.user) des = HoldsDesignation.objects.all().filter(user = request.user).first() - if request.session['currentDesignationSelected']== "student" or request.session['currentDesignationSelected']== "Associate Professor" or request.session['currentDesignationSelected']== "Professor" or request.session['currentDesignationSelected']== "Assistant Professor" : + if str(des.designation) == "student" or str(des.designation) == "Associate Professor" or str(des.designation) == "Professor" or str(des.designation) == "Assistant Professor" : return HttpResponseRedirect('/programme_curriculum/programmes/') - elif str(request.user) in ["acadadmin", "studentacadadmin"]: + elif str(request.user) == "acadadmin" : pass - elif 'hod' in request.session['currentDesignationSelected'].lower(): - return HttpResponseRedirect('/programme_curriculum/programmes/') curriculums = Curriculum.objects.filter(working_curriculum=1) @@ -403,17 +277,16 @@ def admin_view_all_working_curriculums(request): return render(request,'programme_curriculum/acad_admin/admin_view_all_working_curriculums.html',{'curriculums':curriculums, 'curriculumfilter': curriculumfilter}) +@login_required(login_url='/accounts/login') def admin_view_semesters_of_a_curriculum(request, curriculum_id): """ gets all the semesters of a specfic curriculum """ user_details = ExtraInfo.objects.get(user = request.user) des = HoldsDesignation.objects.all().filter(user = request.user).first() - if request.session['currentDesignationSelected']== "student" or request.session['currentDesignationSelected']== "Associate Professor" or request.session['currentDesignationSelected']== "Professor" or request.session['currentDesignationSelected']== "Assistant Professor" : + if str(des.designation) == "student" or str(des.designation) == "Associate Professor" or str(des.designation) == "Professor" or str(des.designation) == "Assistant Professor" : return HttpResponseRedirect('/programme_curriculum/programmes/') - elif str(request.user) in ["acadadmin", "studentacadadmin"]: + elif str(request.user) == "acadadmin" : pass - elif 'hod' in request.session['currentDesignationSelected'].lower(): - return HttpResponseRedirect('/programme_curriculum/programmes/') curriculum = get_object_or_404(Curriculum, Q(id=curriculum_id)) semesters = curriculum.semesters @@ -440,14 +313,17 @@ def admin_view_semesters_of_a_curriculum(request, curriculum_id): max_credit = max(max_credit, course.credit) credits_sum = credits_sum + max_credit semester_credits.append(credits_sum) + + print (semester_credits) transpose_semester_slots = list(zip(*semester_slots)) - all_batches = Batch.objects.filter(running_batch=True, curriculum__isnull=True).order_by('year') + all_batches = Batch.objects.filter(running_batch=True).exclude(curriculum=curriculum_id).order_by('year') return render(request, 'programme_curriculum/acad_admin/admin_view_semesters_of_a_curriculum.html', {'curriculum': curriculum, 'semesters': semesters, 'semester_slots': transpose_semester_slots, 'semester_credits': semester_credits, 'all_batches':all_batches}) +@login_required(login_url='/accounts/login') def admin_view_a_semester_of_a_curriculum(request, semester_id): """ This function is used to Display all Semester of a Curriculum. @@ -461,12 +337,10 @@ def admin_view_a_semester_of_a_curriculum(request, semester_id): """ user_details = ExtraInfo.objects.get(user = request.user) des = HoldsDesignation.objects.all().filter(user = request.user).first() - if request.session['currentDesignationSelected']== "student" or request.session['currentDesignationSelected']== "Associate Professor" or request.session['currentDesignationSelected']== "Professor" or request.session['currentDesignationSelected']== "Assistant Professor" : + if str(des.designation) == "student" or str(des.designation) == "Associate Professor" or str(des.designation) == "Professor" or str(des.designation) == "Assistant Professor" : return HttpResponseRedirect('/programme_curriculum/programmes/') - elif str(request.user) in ["acadadmin", "studentacadadmin"]: + elif str(request.user) == "acadadmin" : pass - elif 'hod' in request.session['currentDesignationSelected'].lower(): - return HttpResponseRedirect('/programme_curriculum/programmes/') semester = get_object_or_404(Semester, Q(id=semester_id)) course_slots = semester.courseslots @@ -474,17 +348,17 @@ def admin_view_a_semester_of_a_curriculum(request, semester_id): return render(request, 'programme_curriculum/acad_admin/admin_view_a_semester_of_a_curriculum.html', {'semester': semester, 'course_slots': course_slots}) + +@login_required(login_url='/accounts/login') def admin_view_a_courseslot(request, courseslot_id): """ view a course slot """ user_details = ExtraInfo.objects.get(user = request.user) des = HoldsDesignation.objects.all().filter(user = request.user).first() - if request.session['currentDesignationSelected']== "student" or request.session['currentDesignationSelected']== "Associate Professor" or request.session['currentDesignationSelected']== "Professor" or request.session['currentDesignationSelected']== "Assistant Professor" : + if str(des.designation) == "student" or str(des.designation) == "Associate Professor" or str(des.designation) == "Professor" or str(des.designation) == "Assistant Professor" : return HttpResponseRedirect('/programme_curriculum/programmes/') - elif str(request.user) in ["acadadmin", "studentacadadmin"]: + elif str(request.user) == "acadadmin" : pass - elif 'hod' in request.session['currentDesignationSelected'].lower(): - return HttpResponseRedirect('/programme_curriculum/programmes/') edit = request.POST.get('edit', -1) course_slot = get_object_or_404(CourseSlot, Q(id=courseslot_id)) @@ -494,17 +368,16 @@ def admin_view_a_courseslot(request, courseslot_id): return render(request, 'programme_curriculum/acad_admin/admin_view_a_courseslot.html', {'course_slot': course_slot}) +@login_required(login_url='/accounts/login') def admin_view_all_courses(request): """ views all the course slots of a specfic semester """ user_details = ExtraInfo.objects.get(user = request.user) des = HoldsDesignation.objects.all().filter(user = request.user).first() - if request.session['currentDesignationSelected']== "student" or request.session['currentDesignationSelected']== "Associate Professor" or request.session['currentDesignationSelected']== "Professor" or request.session['currentDesignationSelected']== "Assistant Professor" : + if str(des.designation) == "student" or str(des.designation) == "Associate Professor" or str(des.designation) == "Professor" or str(des.designation) == "Assistant Professor" : return HttpResponseRedirect('/programme_curriculum/programmes/') - elif str(request.user) in ["acadadmin", "studentacadadmin"]: + elif str(request.user) == "acadadmin" : pass - elif 'hod' in request.session['currentDesignationSelected'].lower(): - return HttpResponseRedirect('/programme_curriculum/programmes/') courses = Course.objects.all() @@ -516,49 +389,46 @@ def admin_view_all_courses(request): return render(request, 'programme_curriculum/acad_admin/admin_view_all_courses.html', {'courses': courses, 'coursefilter': coursefilter}) +@login_required(login_url='/accounts/login') def admin_view_a_course(request, course_id): """ views the details of a Course """ user_details = ExtraInfo.objects.get(user = request.user) des = HoldsDesignation.objects.all().filter(user = request.user).first() - if request.session['currentDesignationSelected']== "student" or request.session['currentDesignationSelected']== "Associate Professor" or request.session['currentDesignationSelected']== "Professor" or request.session['currentDesignationSelected']== "Assistant Professor" : + if str(des.designation) == "student" or str(des.designation) == "Associate Professor" or str(des.designation) == "Professor" or str(des.designation) == "Assistant Professor" : return HttpResponseRedirect('/programme_curriculum/programmes/') - elif str(request.user) in ["acadadmin", "studentacadadmin"]: + elif str(request.user) == "acadadmin" : pass - elif 'hod' in request.session['currentDesignationSelected'].lower(): - return HttpResponseRedirect('/programme_curriculum/programmes/') course = get_object_or_404(Course, Q(id=course_id)) return render(request, 'programme_curriculum/acad_admin/admin_view_a_course.html', {'course': course}) +@login_required(login_url='/accounts/login') def admin_view_all_discplines(request): """ views the details of a Course """ user_details = ExtraInfo.objects.get(user = request.user) des = HoldsDesignation.objects.all().filter(user = request.user).first() - if request.session['currentDesignationSelected']== "student" or request.session['currentDesignationSelected']== "Associate Professor" or request.session['currentDesignationSelected']== "Professor" or request.session['currentDesignationSelected']== "Assistant Professor" : + if str(des.designation) == "student" or str(des.designation) == "Associate Professor" or str(des.designation) == "Professor" or str(des.designation) == "Assistant Professor" : return HttpResponseRedirect('/programme_curriculum/programmes/') - elif str(request.user) in ["acadadmin", "studentacadadmin"]: + elif str(request.user) == "acadadmin" : pass - elif 'hod' in request.session['currentDesignationSelected'].lower(): - return HttpResponseRedirect('/programme_curriculum/programmes/') disciplines = Discipline.objects.all() return render(request, 'programme_curriculum/acad_admin/admin_view_all_disciplines.html', {'disciplines': disciplines}) +@login_required(login_url='/accounts/login') def admin_view_all_batches(request): """ views the details of a Course """ user_details = ExtraInfo.objects.get(user = request.user) des = HoldsDesignation.objects.all().filter(user = request.user).first() - if request.session['currentDesignationSelected']== "student" or request.session['currentDesignationSelected']== "Associate Professor" or request.session['currentDesignationSelected']== "Professor" or request.session['currentDesignationSelected']== "Assistant Professor" : + if str(des.designation) == "student" or str(des.designation) == "Associate Professor" or str(des.designation) == "Professor" or str(des.designation) == "Assistant Professor" : return HttpResponseRedirect('/programme_curriculum/programmes/') - elif str(request.user) in ["acadadmin", "studentacadadmin"]: + elif str(request.user) == "acadadmin" : pass - elif 'hod' in request.session['currentDesignationSelected'].lower(): - return HttpResponseRedirect('/programme_curriculum/programmes/') batches = Batch.objects.all().order_by('year') @@ -570,22 +440,19 @@ def admin_view_all_batches(request): batches = batches.filter(running_batch=True) - from applications.programme_curriculum.api.views_student_management import get_batch_curriculum_display return render(request, 'programme_curriculum/acad_admin/admin_view_all_batches.html', {'batches': batches, 'finished_batches': finished_batches, 'batchfilter': batchfilter}) - +@login_required(login_url='/accounts/login') def add_discipline_form(request): user_details = ExtraInfo.objects.get(user = request.user) des = HoldsDesignation.objects.all().filter(user = request.user).first() - if request.session['currentDesignationSelected']== "student" or request.session['currentDesignationSelected']== "Associate Professor" or request.session['currentDesignationSelected']== "Professor" or request.session['currentDesignationSelected']== "Assistant Professor" : + if str(des.designation) == "student" or str(des.designation) == "Associate Professor" or str(des.designation) == "Professor" or str(des.designation) == "Assistant Professor" : return HttpResponseRedirect('/programme_curriculum/programmes/') - elif str(request.user) in ["acadadmin", "studentacadadmin"]: + elif str(request.user) == "acadadmin" : pass - elif 'hod' in request.session['currentDesignationSelected'].lower(): - return HttpResponseRedirect('/programme_curriculum/programmes/') form = DisciplineForm() submitbutton= request.POST.get('Submit') @@ -599,16 +466,15 @@ def add_discipline_form(request): return render(request, 'programme_curriculum/acad_admin/add_discipline_form.html',{'form':form}) +@login_required(login_url='/accounts/login') def edit_discipline_form(request, discipline_id): user_details = ExtraInfo.objects.get(user = request.user) des = HoldsDesignation.objects.all().filter(user = request.user).first() - if request.session['currentDesignationSelected']== "student" or request.session['currentDesignationSelected']== "Associate Professor" or request.session['currentDesignationSelected']== "Professor" or request.session['currentDesignationSelected']== "Assistant Professor" : + if str(des.designation) == "student" or str(des.designation) == "Associate Professor" or str(des.designation) == "Professor" or str(des.designation) == "Assistant Professor" : return HttpResponseRedirect('/programme_curriculum/programmes/') - elif str(request.user) in ["acadadmin", "studentacadadmin"]: + elif str(request.user) == "acadadmin" : pass - elif 'hod' in request.session['currentDesignationSelected'].lower(): - return HttpResponseRedirect('/programme_curriculum/programmes/') discipline = get_object_or_404(Discipline, Q(id=discipline_id)) form = DisciplineForm(instance=discipline) @@ -623,17 +489,15 @@ def edit_discipline_form(request, discipline_id): return render(request, 'programme_curriculum/acad_admin/add_discipline_form.html',{'form':form}) - +@login_required(login_url='/accounts/login') def add_programme_form(request): user_details = ExtraInfo.objects.get(user = request.user) des = HoldsDesignation.objects.all().filter(user = request.user).first() - if request.session['currentDesignationSelected']== "student" or request.session['currentDesignationSelected']== "Associate Professor" or request.session['currentDesignationSelected']== "Professor" or request.session['currentDesignationSelected']== "Assistant Professor" : + if str(des.designation) == "student" or str(des.designation) == "Associate Professor" or str(des.designation) == "Professor" or str(des.designation) == "Assistant Professor" : return HttpResponseRedirect('/programme_curriculum/programmes/') - elif str(request.user) in ["acadadmin", "studentacadadmin"]: + elif str(request.user) == "acadadmin" : pass - elif 'hod' in request.session['currentDesignationSelected'].lower(): - return HttpResponseRedirect('/programme_curriculum/programmes/') form = ProgrammeForm() submitbutton= request.POST.get('Submit') @@ -649,17 +513,15 @@ def add_programme_form(request): return render(request,'programme_curriculum/acad_admin/add_programme_form.html',{'form':form, 'submitbutton': submitbutton}) - +@login_required(login_url='/accounts/login') def edit_programme_form(request, programme_id): user_details = ExtraInfo.objects.get(user = request.user) des = HoldsDesignation.objects.all().filter(user = request.user).first() - if request.session['currentDesignationSelected']== "student" or request.session['currentDesignationSelected']== "Associate Professor" or request.session['currentDesignationSelected']== "Professor" or request.session['currentDesignationSelected']== "Assistant Professor" : + if str(des.designation) == "student" or str(des.designation) == "Associate Professor" or str(des.designation) == "Professor" or str(des.designation) == "Assistant Professor" : return HttpResponseRedirect('/programme_curriculum/programmes/') - elif str(request.user) in ["acadadmin", "studentacadadmin"]: + elif str(request.user) == "acadadmin" : pass - elif 'hod' in request.session['currentDesignationSelected'].lower(): - return HttpResponseRedirect('/programme_curriculum/programmes/') programme = get_object_or_404(Programme, Q(id=programme_id)) form = ProgrammeForm(instance=programme) @@ -674,6 +536,7 @@ def edit_programme_form(request, programme_id): return render(request, 'programme_curriculum/acad_admin/add_programme_form.html',{'form':form, 'submitbutton': submitbutton}) +@login_required(login_url='/accounts/login') def add_curriculum_form(request): """ This function is used to add Curriculum and Semester into Curriculum and Semester table. @@ -684,12 +547,10 @@ def add_curriculum_form(request): """ user_details = ExtraInfo.objects.get(user = request.user) des = HoldsDesignation.objects.all().filter(user = request.user).first() - if request.session['currentDesignationSelected']== "student" or request.session['currentDesignationSelected']== "Associate Professor" or request.session['currentDesignationSelected']== "Professor" or request.session['currentDesignationSelected']== "Assistant Professor" : + if str(des.designation) == "student" or str(des.designation) == "Associate Professor" or str(des.designation) == "Professor" or str(des.designation) == "Assistant Professor" : return HttpResponseRedirect('/programme_curriculum/programmes/') - elif str(request.user) in ["acadadmin", "studentacadadmin"]: + elif str(request.user) == "acadadmin" : pass - elif 'hod' in request.session['currentDesignationSelected'].lower(): - return HttpResponseRedirect('/programme_curriculum/programmes/') programme_id = request.GET.get('programme_id', -1) @@ -727,12 +588,10 @@ def edit_curriculum_form(request, curriculum_id): """ user_details = ExtraInfo.objects.get(user = request.user) des = HoldsDesignation.objects.all().filter(user = request.user).first() - if request.session['currentDesignationSelected']== "student" or request.session['currentDesignationSelected']== "Associate Professor" or request.session['currentDesignationSelected']== "Professor" or request.session['currentDesignationSelected']== "Assistant Professor" : + if str(des.designation) == "student" or str(des.designation) == "Associate Professor" or str(des.designation) == "Professor" or str(des.designation) == "Assistant Professor" : return HttpResponseRedirect('/programme_curriculum/programmes/') - elif str(request.user) in ["acadadmin", "studentacadadmin"]: + elif str(request.user) == "acadadmin" : pass - elif 'hod' in request.session['currentDesignationSelected'].lower(): - return HttpResponseRedirect('/programme_curriculum/programmes/') curriculum = get_object_or_404(Curriculum, Q(id=curriculum_id)) @@ -770,16 +629,15 @@ def edit_curriculum_form(request, curriculum_id): return render(request, 'programme_curriculum/acad_admin/add_curriculum_form.html',{'form':form, 'submitbutton': submitbutton}) +@login_required(login_url='/accounts/login') def add_course_form(request): user_details = ExtraInfo.objects.get(user = request.user) des = HoldsDesignation.objects.all().filter(user = request.user).first() - if request.session['currentDesignationSelected']== "student" or request.session['currentDesignationSelected']== "Associate Professor" or request.session['currentDesignationSelected']== "Professor" or request.session['currentDesignationSelected']== "Assistant Professor" : + if str(des.designation) == "student" or str(des.designation) == "Associate Professor" or str(des.designation) == "Professor" or str(des.designation) == "Assistant Professor" : return HttpResponseRedirect('/programme_curriculum/programmes/') - elif str(request.user) in ["acadadmin", "studentacadadmin"]: + elif str(request.user) == "acadadmin" : pass - elif 'hod' in request.session['currentDesignationSelected'].lower(): - return HttpResponseRedirect('/programme_curriculum/programmes/') form = CourseForm() submitbutton= request.POST.get('Submit') @@ -787,83 +645,47 @@ def add_course_form(request): if request.method == 'POST': form = CourseForm(request.POST) if form.is_valid(): - new_course = form.save(commit=False) - # new_course.version=1.0 - new_course.save() + form.save() course = Course.objects.last() messages.success(request, "Added successful") return HttpResponseRedirect("/programme_curriculum/admin_course/" + str(course.id) + "/") - return render(request,'programme_curriculum/acad_admin/course_form.html',{'form':form,'submitbutton': submitbutton}) + return render(request,'programme_curriculum/acad_admin/add_course_form.html',{'form':form}) + +@login_required(login_url='/accounts/login') def update_course_form(request, course_id): + + #user_details = ExtraInfo.objects.get(user = request.user) des = HoldsDesignation.objects.all().filter(user = request.user).first() - if request.session['currentDesignationSelected']== "student": + if str(des.designation) == "student": # or str(des.designation) == "Associate Professor" or str(des.designation) == "Professor" or str(des.designation) == "Assistant Professor" return HttpResponseRedirect('/programme_curriculum/programmes/') - elif str(request.user) in ["acadadmin", "studentacadadmin"]: + elif str(request.user) == "acadadmin" : pass - elif 'hod' in request.session['currentDesignationSelected'].lower(): - return HttpResponseRedirect('/programme_curriculum/programmes/') course = get_object_or_404(Course, Q(id=course_id)) - previous = Course.objects.all().filter(code=course.code).order_by('version').last() - course.version=previous.version - version_error='' form = CourseForm(instance=course) submitbutton= request.POST.get('Submit') if submitbutton: if request.method == 'POST': - form = CourseForm(request.POST) - if form.is_valid() : - previous.latest_version=False - previous.save() - form.latest_version=True - new_course = form.save(commit=False) - add=True - ver=0 - if(new_course.version>previous.version): - # Check if a course with the same values (except version, latest_version, disciplines, and pre_requisit_courses) already exists - old_course=Course.objects.filter(code=new_course.code, name=new_course.name, credit=new_course.credit, lecture_hours=new_course.lecture_hours, tutorial_hours=new_course.tutorial_hours, pratical_hours=new_course.pratical_hours, discussion_hours=new_course.discussion_hours, project_hours=new_course.project_hours, pre_requisits=new_course.pre_requisits, syllabus=new_course.syllabus, percent_quiz_1=new_course.percent_quiz_1, percent_midsem=new_course.percent_midsem, percent_quiz_2=new_course.percent_quiz_2, percent_endsem=new_course.percent_endsem, percent_project=new_course.percent_project, percent_lab_evaluation=new_course.percent_lab_evaluation, percent_course_attendance=new_course.percent_course_attendance, ref_books=new_course.ref_books,max_seats=new_course.max_seats) - if old_course: - # Check if disciplines or pre_requisit_courses have been changed - for i in old_course: - if set(form.cleaned_data['disciplines']) != set(i.disciplines.all()) or set(form.cleaned_data['pre_requisit_courses']) != set(i.pre_requisit_courses.all()): - add=True - else: - add=False - ver=i.version - break - if add: - new_course.save() # Save the new course first before adding many-to-many fields - new_course.disciplines.set(form.cleaned_data['disciplines']) - new_course.pre_requisit_courses.set(form.cleaned_data['pre_requisit_courses']) - course = Course.objects.last() - messages.success(request, "Added successful") - return HttpResponseRedirect("/programme_curriculum/admin_course/" + str(course.id) + "/") - else: - form.add_error(None, 'A Course with the same values already exists at "Version Number '+' ' +str(ver)+'"') - else: - new_course.save() # Save the new course first before adding many-to-many fields - new_course.disciplines.set(form.cleaned_data['disciplines']) - new_course.pre_requisit_courses.set(form.cleaned_data['pre_requisit_courses']) - course = Course.objects.last() - messages.success(request, "Added successful") - return HttpResponseRedirect("/programme_curriculum/admin_course/" + str(course.id) + "/") - else: - version_error+=f'The version should be greater than {previous.version}' - - return render(request,'programme_curriculum/acad_admin/course_form.html',{'course':course, 'form':form, 'submitbutton': submitbutton,'version_error':version_error}) + form = CourseForm(request.POST, instance=course) + if form.is_valid(): + form.save() + messages.success(request, "Updated "+ course.name +" successful") + return HttpResponseRedirect("/programme_curriculum/admin_course/" + str(course_id) + "/") + return render(request,'programme_curriculum/acad_admin/add_course_form.html',{'course':course, 'form':form, 'submitbutton': submitbutton}) + + +@login_required(login_url='/accounts/login') def add_courseslot_form(request): user_details = ExtraInfo.objects.get(user = request.user) des = HoldsDesignation.objects.all().filter(user = request.user).first() - if request.session['currentDesignationSelected']== "student" or request.session['currentDesignationSelected']== "Associate Professor" or request.session['currentDesignationSelected']== "Professor" or request.session['currentDesignationSelected']== "Assistant Professor" : + if str(des.designation) == "student" or str(des.designation) == "Associate Professor" or str(des.designation) == "Professor" or str(des.designation) == "Assistant Professor" : return HttpResponseRedirect('/programme_curriculum/programmes/') - elif str(request.user) in ["acadadmin", "studentacadadmin"]: + elif str(request.user) == "acadadmin" : pass - elif 'hod' in request.session['currentDesignationSelected'].lower(): - return HttpResponseRedirect('/programme_curriculum/programmes/') curriculum_id = request.GET.get('curriculum_id', -1) submitbutton= request.POST.get('Submit') @@ -881,16 +703,15 @@ def add_courseslot_form(request): return render(request, 'programme_curriculum/acad_admin/add_courseslot_form.html',{'form':form, 'submitbutton': submitbutton, 'curriculum_id': curriculum_id}) +@login_required(login_url='/accounts/login') def edit_courseslot_form(request, courseslot_id): user_details = ExtraInfo.objects.get(user = request.user) des = HoldsDesignation.objects.all().filter(user = request.user).first() - if request.session['currentDesignationSelected']== "student" or request.session['currentDesignationSelected']== "Associate Professor" or request.session['currentDesignationSelected']== "Professor" or request.session['currentDesignationSelected']== "Assistant Professor" : + if str(des.designation) == "student" or str(des.designation) == "Associate Professor" or str(des.designation) == "Professor" or str(des.designation) == "Assistant Professor" : return HttpResponseRedirect('/programme_curriculum/programmes/') - elif str(request.user) in ["acadadmin", "studentacadadmin"]: + elif str(request.user) == "acadadmin" : pass - elif 'hod' in request.session['currentDesignationSelected'].lower(): - return HttpResponseRedirect('/programme_curriculum/programmes/') courseslot = get_object_or_404(CourseSlot, Q(id=courseslot_id)) curriculum_id = courseslot.semester.curriculum.id @@ -910,12 +731,10 @@ def delete_courseslot(request, courseslot_id): user_details = ExtraInfo.objects.get(user = request.user) des = HoldsDesignation.objects.all().filter(user = request.user).first() - if request.session['currentDesignationSelected']== "student" or request.session['currentDesignationSelected']== "Associate Professor" or request.session['currentDesignationSelected']== "Professor" or request.session['currentDesignationSelected']== "Assistant Professor" : + if str(des.designation) == "student" or str(des.designation) == "Associate Professor" or str(des.designation) == "Professor" or str(des.designation) == "Assistant Professor" : return HttpResponseRedirect('/programme_curriculum/programmes/') - elif str(request.user) in ["acadadmin", "studentacadadmin"]: + elif str(request.user) == "acadadmin" : pass - elif 'hod' in request.session['currentDesignationSelected'].lower(): - return HttpResponseRedirect('/programme_curriculum/programmes/') courseslot = get_object_or_404(CourseSlot, Q(id=courseslot_id)) submitbutton= request.POST.get('Submit') @@ -930,43 +749,38 @@ def delete_courseslot(request, courseslot_id): return render(request, 'programme_curriculum/view_a_courseslot.html', {'course_slot': courseslot}) -# views.py +@login_required(login_url='/accounts/login') def add_batch_form(request): - user_details = ExtraInfo.objects.get(user=request.user) - des = HoldsDesignation.objects.all().filter(user=request.user).first() - - if request.session['currentDesignationSelected'] in ["student", "Associate Professor", "Professor", "Assistant Professor"]: + + user_details = ExtraInfo.objects.get(user = request.user) + des = HoldsDesignation.objects.all().filter(user = request.user).first() + if str(des.designation) == "student" or str(des.designation) == "Associate Professor" or str(des.designation) == "Professor" or str(des.designation) == "Assistant Professor" : return HttpResponseRedirect('/programme_curriculum/programmes/') - elif str(request.user) in ["acadadmin", "studentacadadmin"]: + elif str(request.user) == "acadadmin" : pass - elif 'hod' in request.session['currentDesignationSelected'].lower(): - return HttpResponseRedirect('/programme_curriculum/programmes/') - - # Explicitly setting curriculum to None or '' to prevent any default value - form = BatchForm(initial={'curriculum': None}) - - submitbutton = request.POST.get('Submit') + + curriculum_id = request.GET.get('curriculum_id', -1) + form = BatchForm(initial={'curriculum': curriculum_id}) + submitbutton= request.POST.get('Submit') if submitbutton: if request.method == 'POST': form = BatchForm(request.POST) if form.is_valid(): form.save() - messages.success(request, "Added Batch successfully") + messages.success(request, "Added Batch successful") return HttpResponseRedirect('/programme_curriculum/admin_batches/') + return render(request, 'programme_curriculum/acad_admin/add_batch_form.html',{'form':form, 'submitbutton': submitbutton}) + - return render(request, 'programme_curriculum/acad_admin/add_batch_form.html', {'form': form, 'submitbutton': submitbutton}) - - +@login_required(login_url='/accounts/login') def edit_batch_form(request, batch_id): user_details = ExtraInfo.objects.get(user = request.user) des = HoldsDesignation.objects.all().filter(user = request.user).first() - if request.session['currentDesignationSelected']== "student" or request.session['currentDesignationSelected']== "Associate Professor" or request.session['currentDesignationSelected']== "Professor" or request.session['currentDesignationSelected']== "Assistant Professor" : + if str(des.designation) == "student" or str(des.designation) == "Associate Professor" or str(des.designation) == "Professor" or str(des.designation) == "Assistant Professor" : return HttpResponseRedirect('/programme_curriculum/programmes/') - elif str(request.user) in ["acadadmin", "studentacadadmin"]: + elif str(request.user) == "acadadmin" : pass - elif 'hod' in request.session['currentDesignationSelected'].lower(): - return HttpResponseRedirect('/programme_curriculum/programmes/') curriculum_id = request.GET.get('curriculum_id', -1) batch = get_object_or_404(Batch, Q(id=batch_id)) @@ -985,6 +799,7 @@ def edit_batch_form(request, batch_id): return render(request,'programme_curriculum/acad_admin/add_batch_form.html',{'batch':batch, 'form':form, 'submitbutton':submitbutton}) +@login_required(login_url='/accounts/login') def instigate_semester(request, semester_id): """ This function is used to add the semester information. @@ -996,12 +811,10 @@ def instigate_semester(request, semester_id): """ user_details = ExtraInfo.objects.get(user = request.user) des = HoldsDesignation.objects.all().filter(user = request.user).first() - if request.session['currentDesignationSelected']== "student" or request.session['currentDesignationSelected']== "Associate Professor" or request.session['currentDesignationSelected']== "Professor" or request.session['currentDesignationSelected']== "Assistant Professor" : + if str(des.designation) == "student" or str(des.designation) == "Associate Professor" or str(des.designation) == "Professor" or str(des.designation) == "Assistant Professor" : return HttpResponseRedirect('/programme_curriculum/programmes/') - elif str(request.user) in ["acadadmin", "studentacadadmin"]: + elif str(request.user) == "acadadmin" : pass - elif 'hod' in request.session['currentDesignationSelected'].lower(): - return HttpResponseRedirect('/programme_curriculum/programmes/') semester = get_object_or_404(Semester, Q(id=semester_id)) @@ -1027,6 +840,7 @@ def instigate_semester(request, semester_id): return render(request,'programme_curriculum/acad_admin/instigate_semester_form.html',{'semester':semester, 'form':form, 'submitbutton':submitbutton, 'curriculum_id':curriculum_id}) +@login_required(login_url='/accounts/login') def replicate_curriculum(request, curriculum_id): """ This function is used to replicate the previous curriculum into a new curriculum. @@ -1037,12 +851,10 @@ def replicate_curriculum(request, curriculum_id): """ user_details = ExtraInfo.objects.get(user = request.user) des = HoldsDesignation.objects.all().filter(user = request.user).first() - if request.session['currentDesignationSelected']== "student" or request.session['currentDesignationSelected']== "Associate Professor" or request.session['currentDesignationSelected']== "Professor" or request.session['currentDesignationSelected']== "Assistant Professor" : + if str(des.designation) == "student" or str(des.designation) == "Associate Professor" or str(des.designation) == "Professor" or str(des.designation) == "Assistant Professor" : return HttpResponseRedirect('/programme_curriculum/programmes/') - elif str(request.user) in ["acadadmin", "studentacadadmin"]: + elif str(request.user) == "acadadmin" : pass - elif 'hod' in request.session['currentDesignationSelected'].lower(): - return HttpResponseRedirect('/programme_curriculum/programmes/') old_curriculum = get_object_or_404(Curriculum, Q(id=curriculum_id)) programme = old_curriculum.programme @@ -1084,645 +896,4 @@ def replicate_curriculum(request, curriculum_id): messages.success(request, "Added successful") return HttpResponseRedirect('/programme_curriculum/admin_curriculum_semesters/' + str(curriculum.id) + '/') - return render(request, 'programme_curriculum/acad_admin/add_curriculum_form.html',{'form':form, 'submitbutton': submitbutton}) - -#new - -@login_required(login_url='/accounts/login') -def view_course_proposal_forms(request): - data="" - user_details = ExtraInfo.objects.get(user = request.user) - des = HoldsDesignation.objects.all().filter(user = request.user).last() - - if request.session['currentDesignationSelected'] == "Associate Professor" or request.session['currentDesignationSelected'] == "Professor" or request.session['currentDesignationSelected'] == "Assistant Professor" : - pass - elif request.session['currentDesignationSelected'] == "student" : - return HttpResponseRedirect('/programme_curriculum/programmes/') - elif request.session['currentDesignationSelected'] in ["acadadmin", "studentacadadmin"]: - return render(request, 'programme_curriculum/admin_programmes/') - else: - data='Files Cannot be sent with current Designation Switch to "Professor or Assistant Professor or Associate Professor"' - notifs = request.user.notifications.all() - courseProposal = NewProposalFile.objects.filter(uploader=des.user,designation=request.session['currentDesignationSelected'],is_update=False) - updatecourseProposal = NewProposalFile.objects.filter(uploader=des.user,designation=request.session['currentDesignationSelected'],is_update=True) - - return render(request, 'programme_curriculum/faculty/view_course_proposal_forms.html',{'courseProposals': courseProposal,'updateProposals':updatecourseProposal,'data':data,'notifications': notifs,}) - -@login_required(login_url='/accounts/login') -def faculty_view_all_courses(request): - """ views all the course slots of a specfic semester """ - - user_details = ExtraInfo.objects.get(user = request.user) - des = HoldsDesignation.objects.all().filter(user = request.user).first() - if request.session['currentDesignationSelected'] == "student" : - return HttpResponseRedirect('/programme_curriculum/programmes/') - elif request.session['currentDesignationSelected'] in ["acadadmin", "studentacadadmin"]: - return HttpResponseRedirect('/programme_curriculum/admin_programmes') - elif request.session['currentDesignationSelected'] == "Associate Professor" or request.session['currentDesignationSelected'] == "Professor" or request.session['currentDesignationSelected'] == "Assistant Professor": - pass - elif 'hod' in request.session['currentDesignationSelected'].lower(): - pass - - courses = Course.objects.all() - notifs = request.user.notifications.all() - coursefilter = CourseFilter(request.GET, queryset=courses) - - courses = coursefilter.qs - - return render(request, 'programme_curriculum/faculty/faculty_view_all_courses.html', {'courses': courses, 'coursefilter': coursefilter,'notifications': notifs,}) - - -def faculty_view_a_course(request, course_id): - """ views the details of a Course """ - - user_details = ExtraInfo.objects.get(user = request.user) - des = HoldsDesignation.objects.all().filter(user = request.user).first() - if request.session['currentDesignationSelected'] == "student" : - return HttpResponseRedirect('/programme_curriculum/programmes/') - elif request.session['currentDesignationSelected'] in ["acadadmin", "studentacadadmin"]: - return HttpResponseRedirect('/programme_curriculum/admin_programmes') - elif request.session['currentDesignationSelected'] == "Associate Professor" or request.session['currentDesignationSelected'] == "Professor" or request.session['currentDesignationSelected'] == "Assistant Professor" : - pass - elif 'hod' in request.session['currentDesignationSelected'].lower(): - pass - course = get_object_or_404(Course, Q(id=course_id)) - notifs = request.user.notifications.all() - return render(request, 'programme_curriculum/faculty/faculty_view_a_course.html', {'course': course,'notifications': notifs,}) - - -def view_a_course_proposal_form(request,CourseProposal_id): - user_details = ExtraInfo.objects.get(user = request.user) - des = HoldsDesignation.objects.all().filter(user = request.user).first() - - if request.session['currentDesignationSelected'] == "Associate Professor" or request.session['currentDesignationSelected'] == "Professor" or request.session['currentDesignationSelected'] == "Assistant Professor": - pass - elif 'hod' in request.session['currentDesignationSelected'].lower(): - pass - elif request.session['currentDesignationSelected'] in ["acadadmin", "studentacadadmin"]: - return HttpResponseRedirect('/programme_curriculum/admin_programmes') - else: - return HttpResponseRedirect('/programme_curriculum/programmes') - - user_details = ExtraInfo.objects.get(user = request.user) - des = HoldsDesignation.objects.all().filter(user = request.user).first() - proposalform = get_object_or_404(NewProposalFile, Q(id=CourseProposal_id)) - notifs = request.user.notifications.all() - return render(request, 'programme_curriculum/faculty/view_a_course_proposal.html', {'proposal': proposalform,'notifications': notifs,}) - - -def new_course_proposal_file(request): - des = HoldsDesignation.objects.all().filter(user = request.user).first() - if request.session['currentDesignationSelected'] == "Associate Professor" or request.session['currentDesignationSelected'] == "Professor" or request.session['currentDesignationSelected'] == "Assistant Professor": - pass - elif request.session['currentDesignationSelected'] in ["acadadmin", "studentacadadmin"]: - return HttpResponseRedirect('/programme_curriculum/admin_programmes') - else: - return HttpResponseRedirect('/programme_curriculum/programmes') - - uploader = request.user.extrainfo - design=request.session['currentDesignationSelected'] - form=NewCourseProposalFile(initial={'uploader':des.user,'designation':design}) - submitbutton= request.POST.get('Submit') - - if submitbutton: - if request.method == 'POST': - form = NewCourseProposalFile(request.POST) - if form.is_valid(): - new_course=form.save(commit=False) - new_course.is_read=False - new_course.save() - messages.success(request, "Added successful") - - return HttpResponseRedirect('/programme_curriculum/view_course_proposal_forms/') - - return render(request,'programme_curriculum/faculty/course_proposal_form.html',{'form':form,'submitbutton': submitbutton}) - - - -def filetracking(request,proposal_id): - - - des = HoldsDesignation.objects.all().filter(user = request.user).first() - if request.session['currentDesignationSelected'] == "Associate Professor" or request.session['currentDesignationSelected'] == "Professor" or request.session['currentDesignationSelected'] == "Assistant Professor" or request.session['currentDesignationSelected'] == "Dean Academic": - pass - elif 'hod' in request.session['currentDesignationSelected'].lower(): - pass - elif request.session['currentDesignationSelected'] in ["acadadmin", "studentacadadmin"]: - return HttpResponseRedirect('/programme_curriculum/admin_programmes') - uploader = request.user.extrainfo - design=request.session['currentDesignationSelected'] - file = get_object_or_404(NewProposalFile, Q(id=proposal_id)) - file_data=file.name+' '+file.code - form=CourseProposalTrackingFile(initial={'current_id':file.uploader,'current_design':file.designation,'file_id':int(proposal_id)}) - - submitbutton= request.POST.get('Submit') - - if submitbutton: - if request.method == 'POST': - form = CourseProposalTrackingFile(request.POST) - if form.is_valid(): - try: - form.is_read=False - form.save() - receiver=request.POST.get('receive_id') - receiver_id = User.objects.get(id=receiver) - receiver_design=request.POST.get('receive_design') - receiver_des= Designation.objects.get(id=receiver_design) - uploader=request.POST.get('current_id') - uploader_design=request.POST.get('current_design') - - data='Received as '+ str(receiver_id) +'-'+str(receiver_des) +' Course Proposal Form "'+file_data +'" By '+str(uploader)+' - '+str(uploader_design) - # data=file.subject - messages.success(request, "Submitted successful") - prog_and_curr_notif(request.user,receiver_id,data) - return HttpResponseRedirect('/programme_curriculum/outward_files/') - except IntegrityError as e: - # Handle the IntegrityError here, for example: - form.add_error(None, 'Proposal_ tracking with this File id, Current id, Current design and Disciplines already exists.') - - - - return render(request,'programme_curriculum/faculty/filetracking.html',{'form':form,'submitbutton': submitbutton,'file_info':file_data,}) - - - -def inward_files(request): - user_details = ExtraInfo.objects.get(user = request.user) - des = HoldsDesignation.objects.all().filter(user = request.user).last() - data='' - - if request.session['currentDesignationSelected'] == "Associate Professor" or request.session['currentDesignationSelected'] == "Professor" or request.session['currentDesignationSelected'] == "Assistant Professor" : - data=f'As a "{request.session["currentDesignationSelected"]}" you cannot receive any proposal requests' - pass - elif 'hod' in request.session['currentDesignationSelected'].lower(): - pass - elif request.session['currentDesignationSelected'] == "Dean Academic": - pass - elif request.session['currentDesignationSelected'] in ["acadadmin", "studentacadadmin"]: - return HttpResponseRedirect('/programme_curriculum/admin_programmes/') - else: - return HttpResponseRedirect('/programme_curriculum/programmes/') - - id=request.user - user_designation=HoldsDesignation.objects.select_related('user','working','designation').filter(user=request.user) - notifs = request.user.notifications.all() - designation = Designation.objects.get(name=request.session['currentDesignationSelected']) - des_id = designation.id - - courseProposal = Proposal_Tracking.objects.filter(receive_design = des_id,receive_id= id) - - - return render(request, 'programme_curriculum/faculty/inward_course_forms.html',{'courseProposals': courseProposal,'design':request.session['currentDesignationSelected'],'data':data,'notifications': notifs,}) - - - -def outward_files(request): - user_details = ExtraInfo.objects.get(user = request.user) - des = HoldsDesignation.objects.all().filter(user = request.user).last() - data='' - if request.session['currentDesignationSelected'] == "Dean Academic" : - data=f'As a "{request.session["currentDesignationSelected"]}" you cannot have any out going files' - pass - elif request.session['currentDesignationSelected'] == "Associate Professor" or request.session['currentDesignationSelected'] == "Professor" or request.session['currentDesignationSelected'] == "Assistant Professor" : - pass - elif 'hod' in request.session['currentDesignationSelected'].lower(): - pass - elif request.session['currentDesignationSelected'] == "student" : - return HttpResponseRedirect('/programme_curriculum/programmes/') - elif request.session['currentDesignationSelected'] in ["acadadmin", "studentacadadmin"]: - return render(request, 'programme_curriculum/admin_programmes/') - - id=request.user - notifs = request.user.notifications.all() - user_designation=HoldsDesignation.objects.select_related('user','working','designation').filter(user=request.user) - design=request.session['currentDesignationSelected'] - - - designation = Designation.objects.get(name=request.session['currentDesignationSelected']) - des_id = designation.id - - courseProposal = Proposal_Tracking.objects.filter(current_design = design,current_id= des.user) - - - return render(request, 'programme_curriculum/faculty/outward_course_forms.html',{'courseProposals': courseProposal,'design':request.session['currentDesignationSelected'],'data':data,'notifications': notifs,}) - -def update_course_proposal_file(request, course_id): - des = HoldsDesignation.objects.all().filter(user = request.user).first() - if request.session['currentDesignationSelected'] == "Associate Professor" or request.session['currentDesignationSelected'] == "Professor" or request.session['currentDesignationSelected'] == "Assistant Professor": - pass - elif request.session['currentDesignationSelected'] in ["acadadmin", "studentacadadmin"]: - return HttpResponseRedirect('/programme_curriculum/admin_programmes') - else: - return HttpResponseRedirect('/programme_curriculum/programmes/') - - uploader = request.user.extrainfo - design=request.session['currentDesignationSelected'] - course = get_object_or_404(Course, Q(id=course_id)) - file_data=course.code+' - '+course.name - form = NewCourseProposalFile(initial={'uploader':des.user,'designation':design},instance=course) - submitbutton= request.POST.get('Submit') - - if submitbutton: - if request.method == 'POST': - form = NewCourseProposalFile(request.POST) - if form.is_valid(): - previous = Course.objects.all().filter(code=course.code).order_by('version').last() - course.version=previous.version - new_course=form.save(commit=False) - new_course.is_read=False - new_course.is_update=True - add=True - ver=0 - # Check if a course with the same values (except version, latest_version, disciplines, and pre_requisit_courses) already exists - old_course=Course.objects.filter(code=new_course.code, name=new_course.name, credit=new_course.credit, lecture_hours=new_course.lecture_hours, tutorial_hours=new_course.tutorial_hours, pratical_hours=new_course.pratical_hours, discussion_hours=new_course.discussion_hours, project_hours=new_course.project_hours, pre_requisits=new_course.pre_requisits, syllabus=new_course.syllabus, percent_quiz_1=new_course.percent_quiz_1, percent_midsem=new_course.percent_midsem, percent_quiz_2=new_course.percent_quiz_2, percent_endsem=new_course.percent_endsem, percent_project=new_course.percent_project, percent_lab_evaluation=new_course.percent_lab_evaluation, percent_course_attendance=new_course.percent_course_attendance, ref_books=new_course.ref_books) - if old_course: - # Check if disciplines or pre_requisit_courses have been changed - for i in old_course: - if set(form.cleaned_data['pre_requisit_courses']) != set(i.pre_requisit_courses.all()): - add=True - else: - add=False - ver=i.version - break - if add: - new_course.save() # Save the new course first before adding many-to-many fields - new_course.pre_requisit_courses.set(form.cleaned_data['pre_requisit_courses']) - course = Course.objects.last() - messages.success(request, "Added successful") - return HttpResponseRedirect("/programme_curriculum/admin_course/" + str(course.id) + "/") - else: - form.add_error(None, 'A Course with the same values already exists at "Version Number '+' ' +str(ver)+'"') - else: - new_course.save() # Save the new course first before adding many-to-many fields - new_course.pre_requisit_courses.set(form.cleaned_data['pre_requisit_courses']) - course = Course.objects.last() - messages.success(request, "Added successful") - return HttpResponseRedirect("/programme_curriculum/admin_course/" + str(course.id) + "/") - - return render(request,'programme_curriculum/faculty/course_proposal_form.html',{'form':form,'submitbutton': submitbutton,'file_info':file_data,}) - - - - -def forward_course_forms(request,ProposalId): - de= ProposalId - des = HoldsDesignation.objects.all().filter(user = request.user).first() - - courseform = Proposal_Tracking.objects.all().filter(id=ProposalId) - - uploader = request.user.extrainfo - design=request.session['currentDesignationSelected'] - file = get_object_or_404(Proposal_Tracking, Q(id=ProposalId)) - file_id = int(file.file_id) - file2 = get_object_or_404(NewProposalFile, Q(id=file_id)) - file_data=file2.code + ' ' + file2.name - Proposal_D = file.id - - if request.session['currentDesignationSelected'] == "Dean Academic" : - file = get_object_or_404(Proposal_Tracking, Q(id=ProposalId)) - file_id = int(file.file_id) - file2 = get_object_or_404(NewProposalFile, Q(id=file_id)) - course =Course.objects.all().filter(code=file2.code).last() - version_error='' - if(course): - previous = Course.objects.all().filter(code=course.code).order_by('version').last() - course.version=previous.version - form = CourseForm(instance=file2,initial={'disciplines':file.disciplines}) - submitbutton= request.POST.get('Submit') - if submitbutton: - if request.method == 'POST': - form = CourseForm(request.POST) - if form.is_valid() : - - new_course = form.save(commit=False) - if(new_course.version>previous.version): - previous.latest_version=False - previous.save() - file.is_added=True - file.is_submitted=True - file.save() - form.latest_version=True - form.save() - course = Course.objects.last() - - receiver=file2.uploader - receiver_id = User.objects.get(username=receiver) - data=f'The Course " {file2.code} -{file2.name}" Updated Successfully' - # data=file.subject - prog_and_curr_notif(request.user,receiver_id,data) - messages.success(request, "Updated "+ file2.code+'-'+file2.name +" successful") - return HttpResponseRedirect("/programme_curriculum/course/" + str(course.id) + "/") - else: - version_error+=f'The version should be greater than {previous.version}' - - return render(request,'programme_curriculum/faculty/dean_view_a_course_proposal.html',{'course':course, 'form':form, 'submitbutton': submitbutton,'version_error':version_error,'id':Proposal_D}) - else: - form = CourseForm(instance=file2,initial={'disciplines':file.disciplines}) - # course1 =Course.objects.filter(code=file2.code,name=file2.name,credit=file2.credit,lecture_hours=file2.lecture_hours,tutorial_hours=file2.tutorial_hours,pratical_hours=file2.pratical_hours,discussion_hours=file2.discussion_hours,project_hours=file2.project_hours,pre_requisits=file2.pre_requisits,syllabus=file2.syllabus,percent_quiz_1=file2.percent_quiz_1,percent_midsem=file2.percent_midsem,percent_quiz_2=file2.percent_quiz_2,percent_project=file2.percent_project,percent_endsem=file2.percent_endsem,percent_lab_evaluation=file2.percent_lab_evaluation,percent_course_attendance=file2.percent_course_attendance,ref_books=file2.ref_books) - submitbutton= request.POST.get('Submit') - - if submitbutton: - if request.method == 'POST': - form = CourseForm(request.POST) - - if form.is_valid(): - file.is_added=True - file.is_submitted=True - file.save() - form.save() - receiver=file2.uploader - receiver_id = User.objects.get(username=receiver) - data='The Course "'+ file2.code+ ' - '+ file2.name + '" Added Successfully' - # data=file.subject - prog_and_curr_notif(request.user,receiver_id,data) - course =Course.objects.all().filter(code=file2.code).last() - messages.success(request, "Added "+ file2.name +" successful") - return HttpResponseRedirect("/programme_curriculum/course/" + str(course.id) + "/") - - - - return render(request, 'programme_curriculum/faculty/dean_view_a_course_proposal.html', {'course': file2 ,'form':form,'submitbutton': submitbutton,'id':Proposal_D}) - - elif 'hod' in request.session['currentDesignationSelected'].lower(): - - form=CourseProposalTrackingFile(initial={'current_id':des.user,'current_design':request.session['currentDesignationSelected'],'file_id':file.file_id,'disciplines':file.disciplines}) - - # The above code is trying to retrieve the value of the 'Submit' key from the POST request - # data. It is using the `get` method on the `request.POST` dictionary to access the value - # associated with the 'Submit' key. - submitbutton= request.POST.get('Submit') - - if submitbutton: - if request.method == 'POST': - form = CourseProposalTrackingFile(request.POST) - if form.is_valid(): - try: - file.is_submitted=True - file.save() - form.is_read=False - form.save() - - receiver=request.POST.get('receive_id') - - uploader=request.POST.get('current_id') - uploader_design=request.POST.get('current_design') - - - receiver_design=request.POST.get('receive_design') - receiver_des= Designation.objects.get(id=receiver_design) - receiver_id = User.objects.get(id=receiver) - messages.success(request, "Added successful") - data='Received as '+ str(receiver_id) +'-'+str(receiver_des) +' Course Proposal of Course '+file_data +' By '+str(uploader)+' - '+str(uploader_design) - - prog_and_curr_notif(request.user,receiver_id,data) - return HttpResponseRedirect('/programme_curriculum/outward_files/') - except IntegrityError as e: - form.add_error(None, 'Proposal_ tracking with this File id, Current id, Current design and Disciplines already exists.') - elif request.session['currentDesignationSelected'] in ["acadadmin", "studentacadadmin"]: - return HttpResponseRedirect('/programme_curriculum/admin_programmes') - else: - return HttpResponseRedirect('/programme_curriculum/programmes') - - - - return render(request,'programme_curriculum/faculty/forward.html',{'form':form,'receive_date':file.receive_date,'proposal':file2,'submitbutton': submitbutton,'id':Proposal_D}) - - -def view_inward_files(request,ProposalId): - - if request.session['currentDesignationSelected'] == "Associate Professor" or request.session['currentDesignationSelected'] == "Professor" or request.session['currentDesignationSelected'] == "Assistant Professor" or request.session['currentDesignationSelected'] == "Dean Academic": - pass - elif 'hod' in request.session['currentDesignationSelected'].lower(): - pass - elif request.session['currentDesignationSelected'] in ["acadadmin", "studentacadadmin"]: - return HttpResponseRedirect('/programme_curriculum/admin_programmes/') - else: - return HttpResponseRedirect('/programme_curriculum/programmes/') - - des = HoldsDesignation.objects.all().filter(user = request.user).first() - uploader = request.user.extrainfo - design=request.session['currentDesignationSelected'] - file = get_object_or_404(Proposal_Tracking, Q(id=ProposalId)) - file_id = int(file.file_id) - file2 = get_object_or_404(NewProposalFile, Q(id=file_id)) - file_data='' - file_data2='' - - - if(file.is_rejected): - file_data='"'+str(file2.name) + '"'+' Course Rejected by ' + str(file.receive_id) + ' - ' +str(file.receive_design) - if(file.is_added and not file.is_rejected): - file_data2='"'+str(file2.code)+' - '+str(file2.name) +'" Course Added Succesfully' - - courseProposal = Proposal_Tracking.objects.filter(file_id=file.file_id,disciplines=file.disciplines) - form=CourseProposalTrackingFile(initial={'current_id':des.user,'current_design':request.session['currentDesignationSelected'],'file_id':file.file_id,'disciplines':file.disciplines}) - - return render(request,'programme_curriculum/faculty/view_file.html',{'form':form,'receive_date':file.receive_date,'proposal':file2,'trackings':courseProposal,'file_info':file_data,'file_sucess':file_data2}) - -def reject_form(request,ProposalId): - - if request.session['currentDesignationSelected'] == "Associate Professor" or request.session['currentDesignationSelected'] == "Professor" or request.session['currentDesignationSelected'] == "Assistant Professor" or request.session['currentDesignationSelected'] == "Dean Academic": - pass - elif 'hod' in request.session['currentDesignationSelected'].lower(): - pass - elif request.session['currentDesignationSelected'] in ["acadadmin", "studentacadadmin"]: - return HttpResponseRedirect('/programme_curriculum/admin_programmes/') - else: - return HttpResponseRedirect('/programme_curriculum/programmes/') - - track=get_object_or_404(Proposal_Tracking, Q(id=ProposalId)) - file2 = get_object_or_404(NewProposalFile,Q(id = track.file_id)) - if(not track.is_added and not track.is_submitted): - track.is_rejected=True - track.is_submitted=True - track.save() - messages.success(request, "Course Proposal Form Rejected") - receiver=file2.uploader - receiver_id = User.objects.get(username=receiver) - data='The Course "'+ file2.code+ ' - '+ file2.name + '" was Rejected by ' + str(request.user) + ' - ' +str(request.session['currentDesignationSelected']) - prog_and_curr_notif(request.user,receiver_id,data) - - - else: - messages.error(request, "course already forwarded or added can't be rejected") - return HttpResponseRedirect('/programme_curriculum/inward_files/') - - -def tracking_unarchive(request,ProposalId): - if request.session['currentDesignationSelected'] == "Associate Professor" or request.session['currentDesignationSelected'] == "Professor" or request.session['currentDesignationSelected'] == "Assistant Professor" or request.session['currentDesignationSelected'] == "Dean Academic": - pass - elif 'hod' in request.session['currentDesignationSelected'].lower(): - pass - elif request.session['currentDesignationSelected'] in ["acadadmin", "studentacadadmin"]: - return HttpResponseRedirect('/programme_curriculum/admin_programmes/') - else: - return HttpResponseRedirect('/programme_curriculum/programmes/') - - track=get_object_or_404(Proposal_Tracking, Q(id=ProposalId)) - file = get_object_or_404(NewProposalFile,Q(id = track.file_id)) - print(request.user) - if str(track.current_design)==str(request.session['currentDesignationSelected']) and str(track.current_id)==str(request.user): - track.sender_archive=False - track.save() - messages.success(request, "File UnArchived") - return HttpResponseRedirect('/programme_curriculum/outward_files/') - else : - track.receiver_archive=False - track.save() - messages.success(request, "File UnArchived") - return HttpResponseRedirect('/programme_curriculum/inward_files/') - - - -def tracking_archive(request,ProposalId): - - if request.session['currentDesignationSelected'] == "Associate Professor" or request.session['currentDesignationSelected'] == "Professor" or request.session['currentDesignationSelected'] == "Assistant Professor" or request.session['currentDesignationSelected'] == "Dean Academic": - pass - elif 'hod' in request.session['currentDesignationSelected'].lower(): - pass - elif request.session['currentDesignationSelected'] in ["acadadmin", "studentacadadmin"]: - return HttpResponseRedirect('/programme_curriculum/admin_programmes/') - else: - return HttpResponseRedirect('/programme_curriculum/programmes/') - - track=get_object_or_404(Proposal_Tracking, Q(id=ProposalId)) - if str(track.current_design)==str(request.session['currentDesignationSelected']) and str(track.current_id)==str(request.user): - track.sender_archive=True - track.save() - messages.success(request, "File Archived") - return HttpResponseRedirect('/programme_curriculum/outward_files/') - - else: - track.receiver_archive=True - track.save() - messages.success(request, "File Archived") - return HttpResponseRedirect('/programme_curriculum/inward_files/') - -def file_archive(request,FileId): - if request.session['currentDesignationSelected'] == "Associate Professor" or request.session['currentDesignationSelected'] == "Professor" or request.session['currentDesignationSelected'] == "Assistant Professor" or request.session['currentDesignationSelected'] == "Dean Academic": - pass - elif 'hod' in request.session['currentDesignationSelected'].lower(): - pass - elif request.session['currentDesignationSelected'] in ["acadadmin", "studentacadadmin"]: - return HttpResponseRedirect('/programme_curriculum/admin_programmes/') - else: - return HttpResponseRedirect('/programme_curriculum/programmes/') - - file = get_object_or_404(NewProposalFile,Q(id = FileId)) - file.is_archive=True - file.save() - return HttpResponseRedirect('/programme_curriculum/view_course_proposal_forms/') - -def file_unarchive(request,FileId): - if request.session['currentDesignationSelected'] == "Associate Professor" or request.session['currentDesignationSelected'] == "Professor" or request.session['currentDesignationSelected'] == "Assistant Professor" or request.session['currentDesignationSelected'] == "Dean Academic": - pass - elif 'hod' in request.session['currentDesignationSelected'].lower(): - pass - elif request.session['currentDesignationSelected'] in ["acadadmin", "studentacadadmin"]: - return HttpResponseRedirect('/programme_curriculum/admin_programmes/') - else: - return HttpResponseRedirect('/programme_curriculum/programmes/') - - file = get_object_or_404(NewProposalFile,Q(id = FileId)) - file.is_archive=False - file.save() - return HttpResponseRedirect('/programme_curriculum/view_course_proposal_forms/') - -@login_required(login_url='/accounts/login') -def add_course_instructor(request): - if request.session['currentDesignationSelected'] in ["acadadmin", "studentacadadmin"]: - if request.method == 'POST': - if 'form_submit' in request.POST: - form = CourseInstructorForm(request.POST) - if form.is_valid(): - form.save() # Save the form data to the database - return redirect('/programme_curriculum/admin_instructor/') # Redirect to a success page after saving - else: - messages.error(request, f"An error occured while adding instructor.") - elif 'excel_submit' in request.POST: - manual_instructor_xsl = request.FILES['manual_instructor_xsl'] - try: - excel = xlrd.open_workbook(file_contents=manual_instructor_xsl.read()) - sheet = excel.sheet_by_index(0) - - # Start reading rows (assuming first row contains headers) - with transaction.atomic(): - all_success = True - for i in range(1, sheet.nrows): # Skip the header row - try: - course_code = str(sheet.cell(i, 0).value).strip() - print(course_code) - course_version = float(sheet.cell(i, 1).value) - print(course_version) - instructor_id = str(sheet.cell(i, 2).value).strip() - print(instructor_id) - year = int(sheet.cell(i, 3).value) - print(year) - semester_no = int(sheet.cell(i, 4).value) - print(semester_no) - course = Course.objects.filter( - Q(code__iexact=course_code), - version=course_version - ).first() - print(course) - instructor = Faculty.objects.get(id=instructor_id) - print(instructor,"\n") - - course_instructor = CourseInstructor( - course_id=course, - instructor_id=instructor, - year=year, - semester_no=semester_no - ) - course_instructor.save() - except Exception as e: - all_success = False - print(f"Error processing Excel file in row {i}: {e}") - messages.error(request, f"Error processing Excel file in row {i}: {e}") - if all_success: - messages.success(request, "Instructors added successfully from Excel!") - return redirect('/programme_curriculum/admin_instructor/') - except Exception as e: - messages.error(request, f"Error processing Excel file: {e}") - form = CourseInstructorForm() - - return render(request, 'programme_curriculum/acad_admin/add_course_instructor.html', {'form': form}) - return HttpResponseRedirect('/programme_curriculum/') - -@login_required(login_url='/accounts/login') -def admin_view_all_course_instructor(request): - if request.session.get('currentDesignationSelected') in ["acadadmin", "studentacadadmin"]: - course_instructors = CourseInstructor.objects.all() - - # Apply filtering - course_instructor_filter = CourseInstructorFilter(request.GET, queryset=course_instructors) - filtered_course_instructors = course_instructor_filter.qs - - return render(request, 'programme_curriculum/acad_admin/admin_view_all_course_instructor.html', { - 'course_instructors': filtered_course_instructors, - 'courseinstructorfilter': course_instructor_filter - }) - - return HttpResponseRedirect('/programme_curriculum/') - -@login_required(login_url='/accounts/login') -def update_course_instructor_form(request, instructor_id): - - if request.session.get('currentDesignationSelected') in ["acadadmin", "studentacadadmin"]: - # Retrieve the CourseInstructor object or return 404 if not found - course_instructor = get_object_or_404(CourseInstructor, id=instructor_id) - - if request.method == 'POST': - # Bind the form to the POST data for validation and save - form = CourseInstructorForm(request.POST, instance=course_instructor) - if form.is_valid(): - form.save() # Save the updated data to the database - return redirect('/programme_curriculum/admin_instructor/') # Redirect after successful update - else: - # Create the form with existing data (pre-populated) - form = CourseInstructorForm(instance=course_instructor) - - return render(request, 'programme_curriculum/acad_admin/add_course_instructor.html', {'form': form, 'instructor': course_instructor}) - - # Redirect to the main page if the user is not 'acadadmin' - return HttpResponseRedirect('/programme_curriculum/') \ No newline at end of file + return render(request, 'programme_curriculum/acad_admin/add_curriculum_form.html',{'form':form, 'submitbutton': submitbutton}) \ No newline at end of file diff --git a/FusionIIIT/applications/programme_curriculum/views_password_email.py b/FusionIIIT/applications/programme_curriculum/views_password_email.py deleted file mode 100644 index 029422c8c..000000000 --- a/FusionIIIT/applications/programme_curriculum/views_password_email.py +++ /dev/null @@ -1,508 +0,0 @@ -# Password Email Management Views for Student Management - -import json -import smtplib -import secrets -import string -import uuid -from email.mime.text import MIMEText -from email.mime.multipart import MIMEMultipart -from datetime import datetime - -from django.http import JsonResponse -from django.views.decorators.csrf import csrf_exempt -from django.views.decorators.http import require_http_methods -from django.contrib.auth.decorators import login_required -from django.contrib.auth.models import User -from django.conf import settings -from django.utils import timezone -from django.db import transaction -from django.core.mail import send_mail -from django.template.loader import render_to_string -from django.contrib.auth.hashers import check_password - -from .models_student_management import StudentBatchUpload -from .models_password_email import ( - PasswordEmailLog, BulkPasswordEmailOperation, - StudentPasswordHistory, EmailTemplate -) - - -def send_password_email_smtp(student_email, student_name, password, roll_number, student=None): - """ - Send password email using SMTP configuration - """ - @staticmethod - def generate_secure_password(length=12): - """Generate cryptographically secure password with enhanced security""" - import secrets - import string - - lowercase = string.ascii_lowercase - uppercase = string.ascii_uppercase - digits = string.digits - special = "!@#$%^&*" - - password = [ - secrets.choice(lowercase), - secrets.choice(uppercase), - secrets.choice(digits), - secrets.choice(special) - ] - - all_chars = lowercase + uppercase + digits + special - for _ in range(length - 4): - password.append(secrets.choice(all_chars)) - - secrets.SystemRandom().shuffle(password) - - return ''.join(password) - - -def get_client_ip(request): - """Get client IP address from request""" - x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR') - if x_forwarded_for: - ip = x_forwarded_for.split(',')[0] - else: - ip = request.META.get('REMOTE_ADDR') - return ip - - -def get_email_template_context(student, password, additional_context=None): - """ - Create context for email templates - """ - context = { - 'student_name': student.name, - 'roll_number': student.roll_number or 'Not Assigned', - 'institute_email': student.institute_email, - 'jee_app_no': student.jee_app_no, - 'password': password, - 'programme': student.get_programme_name(), - 'branch': student.get_display_branch(), - 'year': student.year, - 'fusion_url': getattr(settings, 'FUSION_URL', 'http://fusion.iiitdmj.ac.in'), - 'current_date': datetime.now().strftime('%B %d, %Y'), - 'email_host_user': settings.EMAIL_HOST_USER, - 'institute_name': 'PDPM IIITDM Jabalpur' - } - - if additional_context: - context.update(additional_context) - - return context - - -def send_password_email_smtp(student_email, student_name, password, roll_number, student=None): - """ - Send password email using SMTP configuration - """ - try: - # Validate required email settings - if not hasattr(settings, 'EMAIL_HOST_USER') or not settings.EMAIL_HOST_USER: - return False, "EMAIL_HOST_USER setting is not configured" - - if not hasattr(settings, 'EMAIL_HOST_PASSWORD') or not settings.EMAIL_HOST_PASSWORD: - return False, "EMAIL_HOST_PASSWORD setting is not configured" - - # Get email template or use default - try: - template = EmailTemplate.objects.get( - template_type='INITIAL_PASSWORD', - is_active=True - ) - - context = get_email_template_context(student, password) - subject = template.render_subject(context) - html_content = template.render_html(context) - - except EmailTemplate.DoesNotExist: - # Default email template - subject = f"FUSION - Login Credentials - {roll_number}" - html_content = f""" - - -
    -
    -

    Welcome to FUSION Portal

    -

    PDPM IIITDM Jabalpur

    -
    - -

    Dear {student_name},

    - -

    Your FUSION account has been created successfully. Please find your login credentials below:

    - -
    -

    URL: http://fusion.iiitdmj.ac.in

    -

    Username: {roll_number}

    -

    Password: {password}

    -
    - -
    -

    📞 Need Help?

    -

    - For any login issues or technical support, please contact:
    - 📧 {settings.EMAIL_HOST_USER}
    - 🏢 PDPM IIITDM Jabalpur -

    -
    - -
    -

    Best regards,
    - Fusion Development Team
    - PDPM IIITDM Jabalpur

    -
    - -
    - This is an automated message generated by FUSION. Please do not reply to this email. -
    Generated on {datetime.now().strftime('%B %d, %Y at %I:%M %p')} -
    -
    - - - """ - - # Use Django's email backend - from django.core.mail import EmailMultiAlternatives - - email = EmailMultiAlternatives( - subject=subject, - body=f"Your FUSION login credentials - Username: {roll_number}, Password: {password}", - from_email=settings.EMAIL_HOST_USER, - to=[student_email] - ) - - email.attach_alternative(html_content, "text/html") - email.send() - - return True, "Email sent successfully" - - except Exception as e: - return False, str(e) - - -def send_bulk_password_emails(student_data_list): - """ - Send password emails to multiple students - """ - success_count = 0 - failed_count = 0 - results = [] - - for student_data in student_data_list: - success, message = send_password_email_smtp( - student_data['email'], - student_data['name'], - student_data['password'], - student_data['roll_number'], - student_data.get('student') - ) - - results.append({ - 'student': student_data['name'], - 'email': student_data['email'], - 'success': success, - 'message': message - }) - - if success: - success_count += 1 - else: - failed_count += 1 - - return { - 'success_count': success_count, - 'failed_count': failed_count, - 'results': results - } - - -@csrf_exempt -@login_required -@require_http_methods(["POST"]) -def bulk_send_passwords(request): - """ - Send passwords to multiple students in bulk - """ - try: - data = json.loads(request.body) - student_ids = data.get('studentIds', []) - - if not student_ids: - return JsonResponse({'success': False, 'error': 'No students selected'}) - - operation_id = str(uuid.uuid4())[:8] - - results = [] - successful_sends = 0 - failed_sends = 0 - - for student_id in student_ids: - try: - student = StudentBatchUpload.objects.get(id=student_id) - - # Use institute email or personal email - email_address = student.institute_email or student.personal_email - if not email_address: - results.append({ - 'student_id': student_id, - 'student_name': student.name, - 'success': False, - 'message': 'No email address found' - }) - failed_sends += 1 - continue - - - # Generate password and create user - with transaction.atomic(): - # Check if this is an initial password before creating/updating the account - is_initial_password = not student.has_user_account() - - # Use the student model's built-in user account creation - if student.has_user_account(): - # User exists, generate new password - password = student.generate_secure_password() - student.update_user_password(password) - user = student.get_user_account() - else: - # Create new user account with hashed password in auth_user table - user, password = student.create_user_account() - - - # Send email - success, message = send_password_email_smtp( - student_email=email_address, - student_name=student.name, - password=password, - roll_number=student.roll_number or student.jee_app_no, - student=student - ) - - if success: - successful_sends += 1 - results.append({ - 'student_id': student_id, - 'student_name': student.name, - 'success': True, - 'message': f'Password sent to {email_address}' - }) - else: - # email_log.mark_as_failed(message) - DISABLED - failed_sends += 1 - results.append({ - 'student_id': student_id, - 'student_name': student.name, - 'success': False, - 'message': f'Failed to send: {message}' - }) - - except StudentBatchUpload.DoesNotExist: - failed_sends += 1 - results.append({ - 'student_id': student_id, - 'success': False, - 'message': 'Student not found' - }) - except Exception as e: - failed_sends += 1 - results.append({ - 'student_id': student_id, - 'success': False, - 'message': str(e) - }) - - - return JsonResponse({ - 'success': True, - 'message': f'Bulk operation completed. {successful_sends}/{len(student_ids)} emails sent successfully.', - 'operation_id': operation_id, - 'successful_sends': successful_sends, - 'failed_sends': failed_sends, - 'results': results - }) - - except json.JSONDecodeError: - return JsonResponse({'success': False, 'error': 'Invalid JSON data'}) - except Exception as e: - return JsonResponse({'success': False, 'error': str(e)}) - - -@csrf_exempt -@login_required -@require_http_methods(["GET"]) -def password_email_status(request, email_log_id): - """ - Get status of a specific password email - """ - try: - email_log = PasswordEmailLog.objects.get(id=email_log_id) - - return JsonResponse({ - 'success': True, - 'email_log': { - 'id': email_log.id, - 'student_name': email_log.student.name, - 'sent_to_email': email_log.sent_to_email, - 'email_status': email_log.email_status, - 'sent_at': email_log.sent_at.isoformat(), - 'attempts_count': email_log.attempts_count, - 'error_message': email_log.error_message - } - }) - - except PasswordEmailLog.DoesNotExist: - return JsonResponse({'success': False, 'error': 'Email log not found'}) - - -@csrf_exempt -@login_required -@require_http_methods(["GET"]) -def bulk_operation_status(request, operation_id): - """ - Get status of a bulk password email operation - """ - try: - operation = BulkPasswordEmailOperation.objects.get(operation_id=operation_id) - - return JsonResponse({ - 'success': True, - 'operation': { - 'operation_id': operation.operation_id, - 'operation_status': operation.operation_status, - 'total_students': operation.total_students, - 'emails_sent': operation.emails_sent, - 'emails_failed': operation.emails_failed, - 'completion_rate': operation.calculate_completion_rate(), - 'start_time': operation.start_time.isoformat(), - 'end_time': operation.end_time.isoformat() if operation.end_time else None, - 'duration_seconds': operation.duration_seconds - } - }) - - except BulkPasswordEmailOperation.DoesNotExist: - return JsonResponse({'success': False, 'error': 'Bulk operation not found'}) - - -@csrf_exempt -@login_required -@require_http_methods(["GET", "POST"]) -def manage_email_templates(request): - """ - Manage email templates for password emails - """ - if request.method == "GET": - templates = EmailTemplate.objects.all() - return JsonResponse({ - 'success': True, - 'templates': [ - { - 'id': t.id, - 'template_type': t.template_type, - 'template_type_display': t.get_template_type_display(), - 'subject_template': t.subject_template, - 'is_active': t.is_active, - 'updated_at': t.updated_at.isoformat() - } - for t in templates - ] - }) - - elif request.method == "POST": - try: - data = json.loads(request.body) - - template, created = EmailTemplate.objects.update_or_create( - template_type=data['template_type'], - defaults={ - 'subject_template': data['subject_template'], - 'html_template': data['html_template'], - 'text_template': data.get('text_template', ''), - 'is_active': data.get('is_active', True), - 'created_by': request.user - } - ) - - return JsonResponse({ - 'success': True, - 'message': 'Template saved successfully', - 'template_id': template.id, - 'created': created - }) - - except Exception as e: - return JsonResponse({'success': False, 'error': str(e)}) - -@csrf_exempt -@login_required -@require_http_methods(["POST"]) -def send_student_password(request): - """ - Send password email to individual student - """ - try: - data = json.loads(request.body) - student_id = data.get('studentId') - student_email = data.get('studentEmail') - student_name = data.get('studentName') - - # Validate required fields - if not all([student_id, student_email, student_name]): - return JsonResponse({ - 'success': False, - 'error': 'Missing required fields: studentId, studentEmail, studentName' - }) - - # Get student from database - try: - student = StudentBatchUpload.objects.get(id=student_id) - except StudentBatchUpload.DoesNotExist: - return JsonResponse({ - 'success': False, - 'error': f'Student with ID {student_id} not found' - }) - - # Generate password and create/update user account - with transaction.atomic(): - # Check if this is an initial password before creating/updating the account - is_initial_password = not student.has_user_account() - - # Use the student model's built-in user account creation - if student.has_user_account(): - # User exists, generate new password - password = student.generate_secure_password() - student.update_user_password(password) - user = student.get_user_account() - else: - # Create new user account with hashed password in auth_user table - user, password = student.create_user_account() - - # Send email - success, message = send_password_email_smtp( - student_email=student_email, - student_name=student_name, - password=password, - roll_number=student.roll_number or student.jee_app_no, - student=student - ) - - if success: - # email_log.mark_as_sent(password) - DISABLED - return JsonResponse({ - 'success': True, - 'message': f'Password sent successfully to {student_email}', - 'email_log_id': None - }) - else: - # email_log.mark_as_failed(message) - DISABLED - return JsonResponse({ - 'success': False, - 'error': f'Failed to send email: {message}', - 'email_log_id': None - }) - - except json.JSONDecodeError: - return JsonResponse({'success': False, 'error': 'Invalid JSON data'}) - except Exception as e: - return JsonResponse({'success': False, 'error': str(e)}) diff --git a/FusionIIIT/applications/examination/migrations/__init__.py b/FusionIIIT/applications/ps1/__init__.py similarity index 100% rename from FusionIIIT/applications/examination/migrations/__init__.py rename to FusionIIIT/applications/ps1/__init__.py diff --git a/FusionIIIT/applications/ps1/admin.py b/FusionIIIT/applications/ps1/admin.py index ec612790d..8dd0ad5ee 100644 --- a/FusionIIIT/applications/ps1/admin.py +++ b/FusionIIIT/applications/ps1/admin.py @@ -2,8 +2,6 @@ # Register your models here. -from applications.ps1.models import StockEntry,IndentFile,StockItem,StockTransfer +from applications.ps1.models import StockEntry,IndentFile admin.site.register(StockEntry) -admin.site.register(IndentFile) -admin.site.register(StockItem) -admin.site.register(StockTransfer) \ No newline at end of file +admin.site.register(IndentFile) \ No newline at end of file diff --git a/FusionIIIT/applications/ps1/api/serializers.py b/FusionIIIT/applications/ps1/api/serializers.py deleted file mode 100644 index bdd05635c..000000000 --- a/FusionIIIT/applications/ps1/api/serializers.py +++ /dev/null @@ -1,45 +0,0 @@ -from rest_framework import serializers #type:ignore -from applications.ps1.models import IndentFile, File ,StockEntry,StockItem,StockTransfer -from applications.globals.models import ExtraInfo, HoldsDesignation -from applications.filetracking.models import Tracking - -class FileSerializer(serializers.ModelSerializer): - class Meta: - model = File - fields = '__all__' - -class IndentFileSerializer(serializers.ModelSerializer): - class Meta: - model = IndentFile - fields = '__all__' - -class ExtraInfoSerializer(serializers.ModelSerializer): - class Meta: - model = ExtraInfo - fields = '__all__' - -class HoldsDesignationSerializer(serializers.ModelSerializer): - class Meta: - model = HoldsDesignation - fields = '__all__' - -class TrackingSerializer(serializers.ModelSerializer): - class Meta: - model = Tracking - fields = '__all__' - -class StockEntrySerializer(serializers.ModelSerializer): - class Meta: - model = StockEntry - fields = '__all__' - -class StockItemSerializer(serializers.ModelSerializer): - class Meta: - model = StockItem - fields = '__all__' - -class StockTransferSerializer(serializers.ModelSerializer): - class Meta: - model = StockTransfer - fields = '__all__' - diff --git a/FusionIIIT/applications/ps1/api/urls.py b/FusionIIIT/applications/ps1/api/urls.py deleted file mode 100644 index df8ec1719..000000000 --- a/FusionIIIT/applications/ps1/api/urls.py +++ /dev/null @@ -1,53 +0,0 @@ -from django.conf.urls import url - -from . import views - -urlpatterns = [ - # to create a new indent file - url(r'^create_proposal/', views.createProposal, name='create-proposal'), - - # PENDING : TO CREATE A INDENT FILE DRAFT - - # GET DESIGNATIONS USING USER TOKEN - url(r'^getDesignations/', views.getDesignations, name='get-designations'), - - # to get the indent files created by the user - url(r'^indentview/(?P\d+)$', views.indentView, name='indent-view'), - - # to get the indent Files drafts by a user - url(r'^draftview/(?P\d+)$', views.draftView, name='draft-view'), - - # to get all the indent files inwarded to the user 'id' is holdsDesignation id. - url(r'^inwardIndents/(?P\d+)$', views.inwardIndents, name='inward-indents'), - - # to see the details of a specifc indent file - url(r'^indentFile/(?P\d+)$', views.indentFile, name='indent-file'), - - # to forward a indent File - url(r'^indentFile/forward/(?P\d+)$', views.ForwardIndentFile, name='forward-indent-file'), - - # STOCKS API - # to return all the indent files (GET) as well as to get the information of any specific indent file (POST) to further add stock to it (just info). - url(r'^entry/(?P\d+)$', views.entry, name='entry'), - - # To add stock corresponding to a Indent File - url(r'^stockEntry/(?P\d+)$', views.stockEntry, name='stock-entry'), - - # To view all the stock entry details - url(r'^stock_entry_view/(?P\d+)$', views.stockEntryView, name='stock-entry-view'), - - - # To view the current_stock_view based on filters - url(r'^current_stock_view/(?P\d+)$', views.currentStockView, name='current-stock-view'), - - # to view all the items present in any stock entry - url(r'^stock_entry_item_view/(?P\d+)$', views.stock_entry_item_view, name='stock-entry-item-view'), - - # to delete any stock entry - url(r'^stock_item_delete/(?P\d+)$', views.stockDelete, name='stock-delete'), - - url(r'^stock_transfer/(?P\d+)$', views.stockTransfer, name='stock-transfer'), - - - url(r'^perform_transfer/(?P\d+)$', views.performTransfer, name='perform-transfer'), -] diff --git a/FusionIIIT/applications/ps1/api/views.py b/FusionIIIT/applications/ps1/api/views.py deleted file mode 100644 index 587c02e8a..000000000 --- a/FusionIIIT/applications/ps1/api/views.py +++ /dev/null @@ -1,611 +0,0 @@ -from rest_framework.permissions import IsAuthenticated #type:ignore -from rest_framework.response import Response #type:ignore -from rest_framework import status #type:ignore -from rest_framework.decorators import api_view, permission_classes #type:ignore -from applications.ps1.models import IndentFile, File,StockTransfer,StockEntry,StockItem -from applications.globals.models import HoldsDesignation, Designation,ExtraInfo,DepartmentInfo -from applications.filetracking.models import Tracking -from .serializers import IndentFileSerializer ,FileSerializer,ExtraInfoSerializer,HoldsDesignationSerializer,TrackingSerializer,StockEntrySerializer,StockItemSerializer,StockTransferSerializer -from django.utils import timezone -from applications.filetracking.sdk.methods import * -from datetime import datetime -from django.http import HttpResponseForbidden -from django.db.models import Q,Count -from django.shortcuts import get_object_or_404 -from django.http import HttpResponse -import ast - -dept_admin_to_dept = { - "deptadmin_cse": "CSE", - "deptadmin_ece": "ECE", - "deptadmin_me": "ME", - "deptadmin_sm": "SM", - "deptadmin_design": "Design", - "deptadmin_liberalarts": "Liberal Arts", - "deptadmin_ns": "Natural Science", -} - -dept_admin_design = ["deptadmin_cse", "deptadmin_ece", "deptadmin_me","deptadmin_sm", "deptadmin_design", "deptadmin_liberalarts","deptadmin_ns" ] - - -@api_view(['GET']) -@permission_classes([IsAuthenticated]) -def getDesignations(request): - if request.method == 'GET': - designations = HoldsDesignation.objects.filter(user=request.user) - designations_serialized = HoldsDesignationSerializer(designations,many=True) - return Response(designations_serialized.data) - else: - return Response({"message": "Method not allowed"}, status=status.HTTP_405_METHOD_NOT_ALLOWED) - - - -@api_view(['POST']) -@permission_classes([IsAuthenticated]) -def createProposal(request): - if request.method == 'POST': - # print('our requested data :',request.data) - # Process the POST request data - uploader = request.user.extrainfo - subject = request.data.get('title') - description = request.data.get('desc') - design = request.data.get('design') - designation = Designation.objects.get(id=HoldsDesignation.objects.select_related('user', 'working', 'designation').get(id=design).designation_id) - upload_file = request.FILES.get('myfile') - item_name = request.data.get('item_name') - quantity = request.data.get('quantity') - present_stock = request.data.get('present_stock') - estimated_cost = request.data.get('estimated_cost') - purpose = request.data.get('purpose') - specification = request.data.get('specification') - item_type = request.data.get('item_type') - nature = request.data.get('nature') - indigenous = request.data.get('indigenous') - replaced = request.data.get('replaced') - budgetary_head = request.data.get('budgetary_head') - expected_delivery = request.data.get('expected_delivery') - sources_of_supply = request.data.get('sources_of_supply') - head_approval = False - director_approval = False - financial_approval = False - purchased = False - - # Create File object - file = File.objects.create( - uploader=uploader, - description=description, - subject=subject, - designation=designation, - upload_file=upload_file - ) - - # Create IndentFile object - indent_file = IndentFile.objects.create( - file_info=file, - item_name=item_name, - quantity=quantity, - present_stock=present_stock, - estimated_cost=estimated_cost, - purpose=purpose, - specification=specification, - item_type=item_type, - nature=nature, - indigenous=indigenous, - replaced=replaced, - budgetary_head=budgetary_head, - expected_delivery=expected_delivery, - sources_of_supply=sources_of_supply, - head_approval=head_approval, - director_approval=director_approval, - financial_approval=financial_approval, - purchased=purchased, - ) - - # Serialize response data - file_serializer = FileSerializer(file) - indent_file_serializer = IndentFileSerializer(indent_file) - - # Return response - return Response({ - 'file': file_serializer.data, - 'indent_file': indent_file_serializer.data, - 'message': 'Indent Filed Successfully!' - }) - - -@api_view(['GET']) -@permission_classes([IsAuthenticated]) -def indentView(request, id): - if request.method == 'GET': - tracking_objects = Tracking.objects.all() - tracking_obj_ids = [obj.file_id for obj in tracking_objects] - draft_indent = IndentFile.objects.filter(file_info__in=tracking_obj_ids) - draft = [indent.file_info.id for indent in draft_indent] - draft_files = File.objects.filter(id__in=draft).order_by('-upload_date') - indents = [file.indentfile for file in draft_files] - abcd = HoldsDesignation.objects.get(pk=id) - s = str(abcd).split(" - ") - - serializer = IndentFileSerializer(indents, many=True) - return Response(serializer.data) - else: - return Response({"message": "Method not allowed"}, status=status.HTTP_405_METHOD_NOT_ALLOWED) - - -# TO GET ALL THE USER DRAFTS USING HOLDS DESIGNATION ID -@api_view(['GET']) -@permission_classes([IsAuthenticated]) -def draftView(request, id): - if request.method == 'GET': - designation = str(Designation.objects.get(id=HoldsDesignation.objects.select_related('user', 'working', 'designation').get(id=id).designation_id)) - if designation == "student": - return Response({"message": "Unauthorized"}, status=status.HTTP_401_UNAUTHORIZED) - - indents = IndentFile.objects.filter(file_info__in=request.user.extrainfo.uploaded_files.all()).select_related('file_info') - indent_ids = [indent.file_info for indent in indents] - filed_indents = Tracking.objects.filter(file_id__in=indent_ids) - filed_indent_ids = [indent.file_id for indent in filed_indents] - draft = list(set(indent_ids) - set(filed_indent_ids)) - draft_indent = IndentFile.objects.filter(file_info__in=draft).values("file_info") - draft_files = File.objects.filter(id__in=draft_indent).order_by('-upload_date') - abcd = HoldsDesignation.objects.get(pk=id) - s = str(abcd).split(" - ") - serializer = FileSerializer(draft_files, many=True) - return Response(serializer.data) - else: - return Response({"message": "Method not allowed"}, status=status.HTTP_405_METHOD_NOT_ALLOWED) - - -@api_view(['GET']) -@permission_classes([IsAuthenticated]) -def inwardIndents(request, id): - if request.method == 'GET': - designation = str(Designation.objects.get(id=HoldsDesignation.objects.select_related('user', 'working', 'designation').get(id=id).designation_id)) - abcd = HoldsDesignation.objects.get(pk=id) - - data = view_inbox(request.user.username, designation, "ps1") - # outboxd = view_outbox(request.user.username, designations, "ps1") - - data = sorted(data, key=lambda x: datetime.fromisoformat(x['upload_date']), reverse=True) - for item in data: - item['upload_date'] = datetime.fromisoformat(item['upload_date']) - - response_data = { - 'receive_design': str(abcd), - 'in_file': data - } - - return Response(response_data) - else: - return Response({"message": "Method not allowed"}, status=status.HTTP_405_METHOD_NOT_ALLOWED) - - -@api_view(['GET']) -@permission_classes([IsAuthenticated]) -def indentFile(request, id): - if request.method == 'GET': - try: - indent_file = IndentFile.objects.select_related('file_info').get(file_info=id) - except IndentFile.DoesNotExist: - return Response({"message": "Indent file does not exist"}, status=status.HTTP_404_NOT_FOUND) - - # Retrieve tracking details for the indent file - track = Tracking.objects.select_related('file_id__uploader__user','file_id__uploader__department','file_id__designation','current_id__user','current_id__department','current_design__user','current_design__working','current_design__designation','receiver_id','receive_design').filter(file_id=indent_file.file_info) - - # Retrieve ExtraInfo, HoldsDesignation, and filtered designations - extrainfo = ExtraInfo.objects.select_related('user','department').all() - holdsdesignations = HoldsDesignation.objects.select_related('user','working','designation').all() - designations = HoldsDesignation.objects.select_related('user','working','designation').filter(user=request.user) - - - # Serialize the data - indent_serializer = IndentFileSerializer(indent_file) - track_serializer = TrackingSerializer(track, many=True) - extrainfo_serializer = ExtraInfoSerializer(extrainfo, many=True) - holdsdesignations_serializer = HoldsDesignationSerializer(holdsdesignations, many=True) - designations_serializer = HoldsDesignationSerializer(designations, many=True) - - # Construct response data - response_data = { - 'indent_file': indent_serializer.data, - 'track': track_serializer.data, - 'extrainfo': extrainfo_serializer.data, - 'holdsdesignations': holdsdesignations_serializer.data, - 'designations': designations_serializer.data - } - - return Response(response_data) - else: - return Response({"message": "Method not allowed"}, status=status.HTTP_405_METHOD_NOT_ALLOWED) - - - -# Forwarding Indent -@api_view(['POST']) -@permission_classes([IsAuthenticated]) -def ForwardIndentFile(request, id): - - if request.method == 'POST': - # print('hdfjaldfalk' , request.data); - try: - indent = IndentFile.objects.select_related('file_info').get(file_info=id) - file = indent.file_info - track = Tracking.objects.select_related('file_id__uploader__user','file_id__uploader__department','file_id__designation','current_id__user','current_id__department','current_design__user','current_design__working','current_design__designation','receiver_id','receive_design').filter(file_id=file) - except IndentFile.DoesNotExist: - return Response({"message": "Indent file does not exist"}, status=status.HTTP_404_NOT_FOUND) - - # Extracting data from request.POST - remarks = request.data.get('remarks') - sender_design_id = request.data.get('sender') - receiverHdid = request.data.get('receive') - upload_file = request.FILES.get('myfile') - - # Retrieving sender and receiver designations - sender_designationobj = HoldsDesignation.objects.get(id=sender_design_id).designation - sender_designation_name = sender_designationobj.name - receiverHdobj = HoldsDesignation.objects.get(id=receiverHdid) - receiver = receiverHdobj.user.username - receive_design = receiverHdobj.designation.name - - try: - receiver_id = User.objects.get(username=receiver) - except User.DoesNotExist: - return Response({"message": "Enter a valid destination"}, status=status.HTTP_400_BAD_REQUEST) - - try: - receive_design = Designation.objects.get(name=receive_design) - except Designation.DoesNotExist: - return Response({"message": "Enter a valid designation"}, status=status.HTTP_400_BAD_REQUEST) - - # Forwarding the file - forwarded_file_id = forward_file( - file_id=file.id, - receiver=receiver_id, - receiver_designation=receive_design, - file_extra_JSON={"key": 2}, - remarks=remarks, - file_attachment=upload_file - ) - - # Updating indent approvals if necessary - if (str(receive_design) in dept_admin_design): - indent.head_approval=True - elif (( - (sender_designation_name in dept_admin_design) - or - (sender_designation_name == "ps_admin") - ) - and (str(receive_design) == "Accounts Admin")): - indent.director_approval=True - indent.financial_approval=True - indent.head_approval=True - - indent.save() - - # Serializing the data - indent_serializer = IndentFileSerializer(indent) - track_serializer = TrackingSerializer(track, many=True) - - # Constructing response data - response_data = { - 'indent_file': indent_serializer.data, - 'track': track_serializer.data, - 'message': 'Indent File Forwarded successfully' - } - - return Response(response_data, status=status.HTTP_200_OK) - else: - return Response({"message": "Method not allowed"}, status=status.HTTP_405_METHOD_NOT_ALLOWED) - - -@api_view(['GET', 'POST']) -@permission_classes([IsAuthenticated]) -def entry(request,id): - - designation = str(Designation.objects.get(id=HoldsDesignation.objects.select_related('user', 'working', 'designation').get(id=id).designation_id)) - if request.method == 'GET': - - if designation not in dept_admin_design + ["ps_admin"]: - return Response({"message": "Not authorized"}, status=status.HTTP_403_FORBIDDEN) - - # Get department name - department = request.user.extrainfo.department.name - - # Filter indent files based on user's designation - if request.session.get('currentDesignationSelected') == "dept_admin": - indent_files = IndentFile.objects.filter(file_info__uploader__department__name=department) - else: - indent_files = IndentFile.objects.all() - - serializer = IndentFileSerializer(indent_files, many=True) - return Response(serializer.data) - - elif request.method == 'POST': - print(designation) - if str(designation) not in dept_admin_design + ["ps_admin"]: - return Response({"message": "Not authorized"}, status=status.HTTP_403_FORBIDDEN) - - id = request.data.get('id') - if not id: - return Response({"message": "ID parameter is required"}, status=status.HTTP_400_BAD_REQUEST) - - try: - request_file = File.objects.select_related('uploader').get(id=id) - requester = request_file.uploader.user - # persons = ExtraInfo.objects.filter(user_type__in=["staff"]) - corresponding_indent_file = IndentFile.objects.get(file_info=request_file) - - serializer = { - 'request_file': FileSerializer(request_file).data, - 'requester': requester.username, - # 'persons': persons.values(), - 'corresponding_indent_file': IndentFileSerializer(corresponding_indent_file).data - } - return Response(serializer) - except File.DoesNotExist: - return Response({"message": "File with given ID does not exist"}, status=status.HTTP_404_NOT_FOUND) - except IndentFile.DoesNotExist: - return Response({"message": "Corresponding indent file does not exist"}, status=status.HTTP_404_NOT_FOUND) - - -@api_view(['POST']) -@permission_classes([IsAuthenticated]) -def stockEntry(request,id): - - # print(request.data); - - designation = str(Designation.objects.get(id=HoldsDesignation.objects.select_related('user', 'working', 'designation').get(id=id).designation_id)) - # print(designation) - if str(designation) not in dept_admin_design + ["ps_admin"]: - return Response({"message": "Not authorized"}, status=status.HTTP_403_FORBIDDEN) - - if request.method == 'POST': - - id = request.POST.get('id') - vendor = request.POST.get('vendor') - current_stock = request.POST.get('current_stock') - # received_date = request.POST.get('received_date') - bill = request.FILES.get('bill') - location = request.POST.get('location') - - try: - temp1 = File.objects.get(id=id) - temp = IndentFile.objects.get(file_info=temp1) - except (File.DoesNotExist, IndentFile.DoesNotExist): - return Response({"message": "File with given ID does not exist"}, status=status.HTTP_404_NOT_FOUND) - - item_id = temp - dealing_assistant_id = request.user.extrainfo - - print(request.data) - - stock_entry = StockEntry.objects.create( - item_id=item_id, - vendor=vendor, - current_stock=current_stock, - dealing_assistant_id=dealing_assistant_id, - bill=bill, - # received_date=received_date, - location=location - ) - - # Marking the indent file as done - temp.purchased = True - temp.save() - - serializer = StockEntrySerializer(stock_entry) - return Response(serializer.data, status=status.HTTP_201_CREATED) - - -@api_view(['GET']) -@permission_classes([IsAuthenticated]) -def stockEntryView(request,id): - # print(request.user.id); - - designation =str(Designation.objects.get(id=HoldsDesignation.objects.select_related('user', 'working', 'designation').get(id=id).designation_id)) - if str(designation) not in dept_admin_design + ["ps_admin"]: - return Response({"message": "Not authorized"}, status=status.HTTP_403_FORBIDDEN) - - department = request.user.extrainfo.department - - if designation in dept_admin_design: - stocks = StockEntry.objects.filter(item_id__file_info__uploader__department=department) - elif designation == "ps_admin": - stocks = StockEntry.objects.all() - else: - return Response({"message": "Invalid designation"}, status=status.HTTP_400_BAD_REQUEST) - - serializer = StockEntrySerializer(stocks, many=True) - return Response(serializer.data) - - -# to check the current stock situation in the module -@api_view(['GET', 'POST']) -@permission_classes([IsAuthenticated]) -def currentStockView(request,id): - - designation =str(Designation.objects.get(id=HoldsDesignation.objects.select_related('user', 'working', 'designation').get(id=id).designation_id)) - - if request.method == 'GET': - # Check if the user is authorized - if str(designation) not in dept_admin_design + ["ps_admin"]: - return Response({"message": "Not authorized"}, status=status.HTTP_403_FORBIDDEN) - - # Handle GET request - department = request.user.extrainfo.department - - if designation in dept_admin_design: - # Only show stocks of the department for department admin - stocks = StockItem.objects.filter(department=department) - elif designation == "ps_admin": - # Show all stocks for PS admin - stocks = StockItem.objects.all() - else: - return Response({"message": "Invalid designation"}, status=status.HTTP_403_FORBIDDEN) - - grouped_items = stocks.values('StockEntryId__item_id__item_type', 'department').annotate(total_quantity=Count('id')) - - grouped_items_list = [ - { - 'item_type': item['StockEntryId__item_id__item_type'], - 'department': DepartmentInfo.objects.get(id=item['department']).name, - 'total_quantity': item['total_quantity'] - } - for item in grouped_items - ] - - return Response(grouped_items_list) - - elif request.method == 'POST': - # Handle POST request - department = request.data.get('department') - item_type = request.data.get('item_type') - - if not department or not item_type: - return Response({"message": "Missing required parameters"}, status=status.HTTP_400_BAD_REQUEST) - - # Filter StockItem based on provided filters - StockItems = StockItem.objects.filter( - department=department, - StockEntryId__item_id__item_type=item_type - ) - - grouped_items = StockItems.values('StockEntryId__item_id__item_type', 'department').annotate(total_quantity=Count('id')) - - grouped_items_list = [ - { - 'item_type': item['StockEntryId__item_id__item_type'], - 'department': DepartmentInfo.objects.get(id=department).name, - 'total_quantity': item['total_quantity'] - } - for item in grouped_items - ] - - # Serialize the data - # serializer = StockItemSerializer(grouped_items_list, many=True) - - firstStock = StockItemSerializer(StockItems.first()) - - return Response({'stocks': grouped_items_list, 'first_stock': firstStock.data}) - - -@api_view(['POST']) -@permission_classes([IsAuthenticated]) -def stock_entry_item_view(request,id): - designation = str(Designation.objects.get(id=HoldsDesignation.objects.select_related('user', 'working', 'designation').get(id=id).designation_id)) - - if str(designation) not in dept_admin_design + ["ps_admin"]: - return Response({"message": "Not authorized"}, status=status.HTTP_403_FORBIDDEN) - department = request.data.get('department') - file_id=request.data.get('file_id') - temp=File.objects.get(id=file_id) - temp1=IndentFile.objects.get(file_info=temp) - stock_entry=StockEntry.objects.get(item_id=temp1) - - stocks=StockItem.objects.filter(StockEntryId=stock_entry) - serializer = StockItemSerializer(stocks, many=True) - return Response(serializer.data) - - -@api_view(['POST']) -@permission_classes([IsAuthenticated]) -def stockDelete(request,id): - designation = str(Designation.objects.get(id=HoldsDesignation.objects.select_related('user', 'working', 'designation').get(id=id).designation_id)) - - if str(designation) not in dept_admin_design + ["ps_admin"]: - return Response({"message": "Not authorized"}, status=status.HTTP_403_FORBIDDEN) - - - id = request.POST.get('id') - try: - stock = StockItem.objects.get(id=id) - except StockItem.DoesNotExist: - return Response({"message": 'Stock item with given ID does not exist',"id":id}, status=status.HTTP_404_NOT_FOUND) - - stock.delete() - return Response({"message": "Stock item deleted successfully"}, status=status.HTTP_200_OK) - -@api_view(['POST']) -@permission_classes([IsAuthenticated]) -def stockTransfer(request,id): - designation = str(Designation.objects.get(id=HoldsDesignation.objects.select_related('user', 'working', 'designation').get(id=id).designation_id)) - - if str(designation) not in dept_admin_design + ["ps_admin"]: - return Response({"message": "Not authorized"}, status=status.HTTP_403_FORBIDDEN) - - id=request.POST.get('id') - temp=File.objects.get(id=id) - temp1=IndentFile.objects.get(file_info=temp) - - item_type_required =temp1.item_type - - available_items=StockItem.objects.filter( - StockEntryId__item_id__item_type=item_type_required, - inUse=False - ) - - print(available_items) - serializer = StockItemSerializer(available_items, many=True) - return Response(serializer.data) - - - -@api_view(['POST']) -@permission_classes([IsAuthenticated]) -def performTransfer(request,id): - designation = str(Designation.objects.get(id=HoldsDesignation.objects.select_related('user', 'working', 'designation').get(id=id).designation_id)) - - if str(designation) not in dept_admin_design + ["ps_admin"]: - return Response({"message": "Not authorized"}, status=status.HTTP_403_FORBIDDEN) - - selected_stock_items = request.data.getlist('selected_stock_items[]') - indentId = request.data.get('indentId') - dest_location = request.data.get('dest_location') - - list = selected_stock_items[0]; - stock_items_list = ast.literal_eval(list) - - myIndent = IndentFile.objects.get(file_info=indentId) - - moreStocksRequired = myIndent.quantity - len(stock_items_list) - # print('dest_destination : ', myIndent.file_info.uploader.department) - - stock_transfers = [] - for item in stock_items_list: - stock_item = StockItem.objects.get(id=item) - # print('src dest : ', stock_item.department) - - store_cur_dept = stock_item.department; - store_cur_location = stock_item.location; - - # changing the attributes for this stock item as being transferred. - stock_item.department=myIndent.file_info.uploader.department; - stock_item.location=dest_location; - stock_item.inUse= True - stock_item.isTransferred= True - # if a stock_item is been transferred then obviously it will be put into use. - stock_item.save(); - - - stock_transfer = StockTransfer.objects.create( - indent_file=myIndent, - src_dept=store_cur_dept, - dest_dept=myIndent.file_info.uploader.department, - stockItem=stock_item, - src_location=store_cur_location, - dest_location=dest_location - ) - - stock_transfers.append(stock_transfer) - - - if(moreStocksRequired==0): - myIndent.purchased=True - else: - myIndent.quantity=moreStocksRequired; - - myIndent.save(); - - - serializer = StockTransferSerializer(stock_transfers,many=True) - return Response(serializer.data, status=status.HTTP_201_CREATED) diff --git a/FusionIIIT/applications/ps1/migrations/0001_initial.py b/FusionIIIT/applications/ps1/migrations/0001_initial.py index 5b3086e09..4cd74fe33 100644 --- a/FusionIIIT/applications/ps1/migrations/0001_initial.py +++ b/FusionIIIT/applications/ps1/migrations/0001_initial.py @@ -1,8 +1,7 @@ -# Generated by Django 3.1.5 on 2024-07-16 15:44 +# Generated by Django 3.1.5 on 2023-03-15 18:53 from django.db import migrations, models import django.db.models.deletion -import django.utils.timezone class Migration(migrations.Migration): @@ -25,8 +24,7 @@ class Migration(migrations.Migration): ('estimated_cost', models.IntegerField(null=True)), ('purpose', models.CharField(max_length=250)), ('specification', models.CharField(max_length=250)), - ('item_type', models.CharField(max_length=250)), - ('item_subtype', models.CharField(default='computers', max_length=250)), + ('indent_type', models.CharField(max_length=250)), ('nature', models.BooleanField(default=False)), ('indigenous', models.BooleanField(default=False)), ('replaced', models.BooleanField(default=False)), @@ -42,54 +40,19 @@ class Migration(migrations.Migration): 'db_table': 'IndentFile', }, ), - migrations.CreateModel( - name='StockItem', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('nomenclature', models.CharField(max_length=100, unique=True)), - ('inUse', models.BooleanField(default=True)), - ('location', models.CharField(choices=[('SR1', 'LHTC'), ('SR2', 'Computer Center'), ('SR3', 'Panini Hostel'), ('SR4', 'Lab complex'), ('SR5', 'Admin Block')], default='SR1', max_length=100)), - ('isTransferred', models.BooleanField(default=False)), - ('department', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='globals.departmentinfo')), - ], - options={ - 'db_table': 'StockItem', - }, - ), migrations.CreateModel( name='StockEntry', fields=[ ('item_id', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True, serialize=False, to='ps1.indentfile')), ('vendor', models.CharField(max_length=250)), + ('item_name', models.CharField(max_length=250)), ('current_stock', models.IntegerField()), ('recieved_date', models.DateField()), ('bill', models.FileField(upload_to='')), - ('location', models.CharField(choices=[('SR1', 'LHTC'), ('SR2', 'Computer Center'), ('SR3', 'Panini Hostel'), ('SR4', 'Lab complex'), ('SR5', 'Admin Block')], default='SR1', max_length=100)), ('dealing_assistant_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='globals.extrainfo')), ], options={ 'db_table': 'StockEntry', }, ), - migrations.CreateModel( - name='StockTransfer', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('src_location', models.CharField(choices=[('SR1', 'LHTC'), ('SR2', 'Computer Center'), ('SR3', 'Panini Hostel'), ('SR4', 'Lab complex'), ('SR5', 'Admin Block')], default='SR1', max_length=100)), - ('dest_location', models.CharField(choices=[('SR1', 'LHTC'), ('SR2', 'Computer Center'), ('SR3', 'Panini Hostel'), ('SR4', 'Lab complex'), ('SR5', 'Admin Block')], default='SR2', max_length=100)), - ('dateTime', models.DateTimeField(default=django.utils.timezone.now)), - ('dest_dept', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='dept_dest_transfers', to='globals.departmentinfo')), - ('indent_file', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='ps1.indentfile')), - ('src_dept', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='dept_src_transfers', to='globals.departmentinfo')), - ('stockItem', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='ps1.stockitem')), - ], - options={ - 'db_table': 'StockTransfer', - }, - ), - migrations.AddField( - model_name='stockitem', - name='StockEntryId', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='ps1.stockentry'), - ), ] diff --git a/FusionIIIT/applications/ps1/models.py b/FusionIIIT/applications/ps1/models.py index b34683836..bee76f36e 100644 --- a/FusionIIIT/applications/ps1/models.py +++ b/FusionIIIT/applications/ps1/models.py @@ -1,22 +1,18 @@ from django.db import models from django.contrib.auth.models import User -from applications.globals.models import Staff,ExtraInfo,DepartmentInfo +from applications.globals.models import Staff,ExtraInfo from applications.filetracking.models import File -from django.db.models.signals import post_save -from django.dispatch import receiver -from django.utils import timezone class IndentFile(models.Model): file_info=models.OneToOneField(File, on_delete=models.CASCADE,primary_key=True) - item_name=models.CharField(max_length=250,blank=False) # Apple pro vision 3.0 + item_name=models.CharField(max_length=250,blank=False) quantity= models.IntegerField(blank=False) present_stock=models.IntegerField(blank=False) estimated_cost=models.IntegerField(null=True, blank=False) purpose=models.CharField(max_length=250,blank=False ) specification=models.CharField(max_length=250) - item_type=models.CharField(max_length=250) - item_subtype = models.CharField(max_length=250,blank=False,default='computers') + indent_type=models.CharField(max_length=250) nature=models.BooleanField(default = False) indigenous= models.BooleanField(default = False) replaced =models.BooleanField(default = False) @@ -31,82 +27,15 @@ class IndentFile(models.Model): class Meta: db_table = 'IndentFile' - -class Constants: - Locations = ( - ('SR1', 'LHTC'), - ('SR2', 'Computer Center'), - ('SR3', 'Panini Hostel'), - ('SR4', 'Lab complex'), - ('SR5', 'Admin Block'), - ) - class StockEntry(models.Model): item_id=models.OneToOneField(IndentFile, on_delete=models.CASCADE,primary_key=True) dealing_assistant_id=models.ForeignKey(ExtraInfo, on_delete=models.CASCADE) vendor=models.CharField(max_length=250, blank=False) + item_name=models.CharField(max_length=250, blank=False) current_stock=models.IntegerField(blank=False) - recieved_date = models.DateField(blank=False) + recieved_date=models.DateField(blank=False) bill=models.FileField(blank=False) - location = models.CharField(max_length=100 ,choices=Constants.Locations,default='SR1') - class Meta: - db_table = 'StockEntry' - -# Individual Stock Item -class StockItem(models.Model): - # this represents the id of the StockEntry instance (StockEntryId_id) - - # this StockEntryId will never ever change onces StockItem instance is created and will therefore be used for obtaining the grade,indentfile etc. details. - - StockEntryId = models.ForeignKey(StockEntry,on_delete=models.CASCADE) - nomenclature = models.CharField(max_length=100, unique=True) # Unique identifier for each StockItem - inUse = models.BooleanField(default=True) - - # as department is going to be a variable field here I have to add a Department fiedl for making our job a bit easir and same goes for location as well. Redundancy Hurts :). - - department = models.ForeignKey( - DepartmentInfo, on_delete=models.CASCADE, null=True, blank=True) - location = models.CharField(max_length=100,choices=Constants.Locations,default='SR1') - isTransferred = models.BooleanField(default=False) - - class Meta: - db_table = 'StockItem' - - def save(self, *args, **kwargs): - # Generate nomenclature when saving the StockItem instance - if not self.nomenclature: - # Construct nomenclature using StockEntry primary key and a sequential number - max_existing_number = StockItem.objects.filter(StockEntryId=self.StockEntryId_id).count() - new_number = max_existing_number + 1 - self.nomenclature = f"{self.StockEntryId.item_id}_{new_number}" - super().save(*args, **kwargs) - - -# this table will be used to keep a track of all the stockTransfer things. -class StockTransfer(models.Model): - indent_file=models.ForeignKey(IndentFile, on_delete=models.CASCADE) - - src_dept= models.ForeignKey( - DepartmentInfo, on_delete=models.CASCADE, null=True, blank=True, related_name='dept_src_transfers') - dest_dept= models.ForeignKey( - DepartmentInfo, on_delete=models.CASCADE, null=True, blank=True, related_name='dept_dest_transfers') - - stockItem= models.ForeignKey(StockItem,on_delete=models.CASCADE) - src_location = models.CharField(max_length=100,choices=Constants.Locations,default='SR1') - dest_location = models.CharField(max_length=100,choices=Constants.Locations,default='SR2') - dateTime = models.DateTimeField(default=timezone.now) - - class Meta : - db_table = 'StockTransfer' - - -@receiver(post_save, sender=StockEntry) -def create_stock_items(sender, instance, created, **kwargs): - if created: - # Automatically create 'n' number of StockItem instances based on current_stock of StockEntry - # instance is stockEntry - current_stock = int(instance.current_stock) - for _ in range(current_stock): - StockItem.objects.create(StockEntryId=instance,location=instance.location,department=instance.item_id.file_info.uploader.department) + class Meta: + db_table = 'StockEntry' \ No newline at end of file diff --git a/FusionIIIT/applications/ps1/urls.py b/FusionIIIT/applications/ps1/urls.py index dc0b33bae..e8b3ad417 100644 --- a/FusionIIIT/applications/ps1/urls.py +++ b/FusionIIIT/applications/ps1/urls.py @@ -1,4 +1,4 @@ -from django.conf.urls import url,include +from django.conf.urls import url from . import views @@ -7,71 +7,22 @@ urlpatterns = [ url(r'^$', views.ps1, name='ps1'), - url(r'^create_proposal/$', views.create_proposal, name='create_proposal'), # url(r'^compose_indent/$', views.compose_indent, name='compose_indent'), url(r'^composed_indents/$', views.composed_indents, name='composed_indents'), - - # here id is the Holdsdesignation id. - url(r'^indentview/(?P\d+)/$', views.indentview, name='indentview'), - url(r'^archieveview/(?P\d+)/$', views.archieveview, name='archieveview'), - + url(r'^indentview/(?P\d+)$', views.indentview, name='indentview'), url(r'^drafts/$', views.drafts, name='drafts'), - url(r'^draftview/(?P\d+)/$', views.draftview, name='draftview'), + url(r'^draftview/(?P\d+)$', views.draftview, name='draftview'), url(r'^inwardIndent/$', views.inward, name='inward'), - - # indentview2 is to get all the indentFiles inwarded towards the request.user. - - url(r'^indentview2/(?P\d+)/$', views.indentview2, name='indentview2'), - url(r'^confirmdelete/(?P\d+)/$', views.confirmdelete, name='confirm_delete'), - url(r'^delete/(?P\d+)/$',views.delete, name='delete'), - # forward Indent is to see a specific forwarded indent to ourselves + url(r'^indentview2/(?P\d+)$', views.indentview2, name='indentview2'), + url(r'^confirmdelete/(?P\d+)$', views.confirmdelete, name='confirm_delete'), + url(r'^delete/(?P\d+)$',views.delete, name='delete'), url(r'^forwardindent/(?P\d+)/$', views.forwardindent, name='forwardindent'), url(r'^createdindent/(?P\d+)/$', views.createdindent, name='createdindent'), - url(r'^forwardedIndent/(?P\d+)/$', views.forwardedIndent, name='forwardedIndent'), - - url(r'^entry/$', views.entry, name='entry'), url(r'^StockEntry/$', views.Stock_Entry, name='Stock_Entry'), - - # stock_view is now stock_entry_view which will tell us about the new stock that is entered in any department via a new StockEntry - url(r'^stock_entry_view/$', views.stock_entry_view, name='stock_view'), - - # current_stock_view will tell us about the current situation of stocks present in any department (both transferred to the department and new stocks added in the department) - url(r'^current_stock_view/$', views.current_stock_view, name='current_stock_view'), - - # to display individual items belonging to a certain stock entry record. - url(r'^stock_entry_item_view/$', views.stock_entry_item_view, name='stock_entry_item_view'), - - # to display stock items which are having similar item_type ,grade and department.(used in current_stock_view) - url(r'^stock_item_view/$', views.stock_item_view, name='stock_item_view'), - - url(r'^archieved_indents/$', views.archieved_files, name='archieved_indents_view'), - - - - + url(r'^stock_view/$', views.stock_view, name='stock_view'), url(r'^stock_delete/$', views.stock_delete, name='stock_delete'), url(r'^stock_edit/$', views.stock_edit, name='stock_edit'), url(r'^stock_update/$', views.stock_update, name='stock_update'), - url(r'^stock_login/$', views.dealing_assistant, name='dealing_assistant'), - - - url(r'^generate_report/$', views.generate_report, name='generate_report'), - url(r'^report/$', views.report, name='report'), # !not clear - url(r'view-bill//$', views.view_bill, name='view_bill'), - - - url(r'^perform_transfer/$', views.perform_transfer, name='perform_transfer'), - url(r'^stock_transfer/$', views.stock_transfer, name='stock_transfer'), - url(r'^view_transfer/$', views.view_transfer, name='view_transfer'), - - url(r'^outboxview2/(?P\d+)/$', views.outboxview2, name='outboxview2'), - url(r'^outboxview/$', views.outboxview, name='outboxview'), - - - url(r'^update_stock_item_inUse/$', views.updateStockItemInUse, name='stockItemInUse'), - url(r'^item_detail/(?P\d+)/$', views.item_detail, name='item_detail'), - - # BASE API - url(r'^api/',include('applications.ps1.api.urls')), - + url(r'^entry/$', views.entry, name='entry'), + url(r'^stock_login/$', views.dealing_assistant, name='dealing_assistant') ] diff --git a/FusionIIIT/applications/ps1/views.py b/FusionIIIT/applications/ps1/views.py index 971df43b0..15168a6b9 100644 --- a/FusionIIIT/applications/ps1/views.py +++ b/FusionIIIT/applications/ps1/views.py @@ -1,7 +1,7 @@ from django.contrib import messages from django.shortcuts import render, get_object_or_404, redirect -from applications.filetracking.sdk.methods import * -from applications.ps1.models import IndentFile,StockEntry,StockItem,StockTransfer +from applications.filetracking.models import File, Tracking +from applications.ps1.models import IndentFile,StockEntry from applications.globals.models import ExtraInfo, HoldsDesignation, Designation from django.template.defaulttags import csrf_token from django.http import HttpResponse, HttpResponseRedirect, JsonResponse @@ -11,59 +11,9 @@ from django.contrib.auth.models import User from timeit import default_timer as time from notification.views import office_module_notif -from django.utils import timezone -from datetime import datetime -from applications.globals.models import DepartmentInfo -from applications.filetracking.sdk.methods import * -from datetime import datetime -import re -import json - -from django.db.models import Q,Count -from datetime import datetime -from datetime import datetime - -dept_admin_to_dept = { - "deptadmin_cse": "CSE", - "deptadmin_ece": "ECE", - "deptadmin_me": "ME", - "deptadmin_sm": "SM", - "deptadmin_design": "Design", - "deptadmin_liberalarts": "Liberal Arts", - "deptadmin_ns": "Natural Science", - "Admin IWD":"IWD", - "Compounder":"Health Center" -} - -dept_admin_design = ["deptadmin_cse", "deptadmin_ece", "deptadmin_me","deptadmin_sm", "deptadmin_design", "deptadmin_liberalarts","deptadmin_ns" ,"Admin IWD","Compounder"] - @login_required(login_url = "/accounts/login/") def ps1(request): - notifs = request.user.notifications.all() - - des_obj = HoldsDesignation.objects.filter(user=request.user) - - if des_obj: - designations = [des.designation.name for des in des_obj] - - # request.session['currentDesignationSelected'] - currentDes = request.session['currentDesignationSelected'] - - print('currentDes : ',currentDes); - - isdept_admin = any(designation in dept_admin_to_dept for designation in designations) - - if(isdept_admin or currentDes=="ps_admin"): - return redirect('/purchase-and-store/entry/') - elif not("student" in designations) and str((request.user.extrainfo.department.name)): - return redirect('/purchase-and-store/create_proposal/') - else: - return redirect('/dashboard') - - -@login_required(login_url = "/accounts/login/") -def create_proposal(request): """ The function is used to create indents by faculty. It adds the indent datails to the indet_table of Purchase and Store module @@ -84,7 +34,7 @@ def create_proposal(request): estimated_cost=request.POST.get('estimated_cost') purpose=request.POST.get('purpose') specification=request.POST.get('specification') - item_type=request.POST.get('item_type') + indent_type=request.POST.get('indent_type') nature=request.POST.get('nature') indigenous=request.POST.get('indigenous') replaced =request.POST.get('replaced') @@ -96,34 +46,29 @@ def create_proposal(request): financial_approval=False purchased =request.POST.get('purchased') """ - if request.session['currentDesignationSelected'] in dept_admin_design + ["ps_admin"]: - return redirect('/purchase-and-store/inwardIndent') - print("request.user.id : ", request.user.id) - notifs = request.user.notifications.all() + des = HoldsDesignation.objects.all().select_related().filter(user = request.user).first() + if str(des.designation) == "student": + return redirect('/dashboard') + if request.user.extrainfo.id == '132': + return redirect("/purchase-and-store/entry/") if request.method =="POST": try: if 'save' in request.POST: uploader = request.user.extrainfo subject = request.POST.get('title') description = request.POST.get('desc') - # design = request.POST.get('design') - design = HoldsDesignation.objects.filter(user=request.user, designation__name=request.session['currentDesignationSelected']).first() - - #print('design : ',design,'design2' , design2); - + design = request.POST.get('design') + designation = Designation.objects.get(id = HoldsDesignation.objects.select_related('user','working','designation').get(id = design).designation_id) + upload_file = request.FILES.get('myfile') - designation = Designation.objects.get(id = HoldsDesignation.objects.select_related('user','working','designation').get(id = design.id).designation_id) - upload_file = request.FILES.get('myfile') item_name=request.POST.get('item_name') quantity= request.POST.get('quantity') present_stock=request.POST.get('present_stock') estimated_cost=request.POST.get('estimated_cost') purpose=request.POST.get('purpose') specification=request.POST.get('specification') - item_type=request.POST.get('item_type') - item_subtype=request.POST.get('item_subtype') - + indent_type=request.POST.get('indent_type') nature=request.POST.get('nature') indigenous=request.POST.get('indigenous') replaced =request.POST.get('replaced') @@ -135,28 +80,23 @@ def create_proposal(request): financial_approval=False purchased =False - ##get the uploader username - uploader = request.user.username - print("uploader : ",uploader) - file_id=create_draft( + file=File.objects.create( uploader=uploader, - uploader_designation=designation, - src_module="ps1", - src_object_id="", - file_extra_JSON={"value": 2}, - attached_file=upload_file + description=description, + subject=subject, + designation=designation, + upload_file=upload_file ) IndentFile.objects.create( - file_info=get_object_or_404(File, pk=file_id), + file_info=file, item_name= item_name, quantity=quantity, present_stock=present_stock, estimated_cost=estimated_cost, purpose=purpose, specification=specification, - item_type=item_type, - item_subtype=item_subtype, + indent_type=indent_type, nature=nature, indigenous=indigenous, replaced = replaced , @@ -170,36 +110,21 @@ def create_proposal(request): ) if 'send' in request.POST: - - - # print('request.POST : ',request.POST); - - uploader = request.user.extrainfo subject = request.POST.get('title') description = request.POST.get('desc') - # design = request.POST.get('design') - - design = HoldsDesignation.objects.filter(user=request.user, designation__name=request.session['currentDesignationSelected']).first() - - designation = Designation.objects.get(id = HoldsDesignation.objects.select_related('user','working','designation').get(id = design.id).designation_id) - - # print(designation) - # print(design) - # print('design : ',design.id); + design = request.POST.get('design') + designation = Designation.objects.get(id = HoldsDesignation.objects.select_related('user','working','designation').get(id = design).designation_id) - - upload_file = request.FILES.get('myfile') + item_name=request.POST.get('item_name') quantity= request.POST.get('quantity') present_stock=request.POST.get('present_stock') estimated_cost=request.POST.get('estimated_cost') purpose=request.POST.get('purpose') specification=request.POST.get('specification') - item_type=request.POST.get('item_type') - item_subtype=request.POST.get('item_subtype') - + indent_type=request.POST.get('indent_type') nature=request.POST.get('nature') indigenous=request.POST.get('indigenous') replaced =request.POST.get('replaced') @@ -210,55 +135,25 @@ def create_proposal(request): director_approval=False financial_approval=False purchased = False - designations = HoldsDesignation.objects.select_related('user','working','designation').filter(user = request.user) - - if request.session['currentDesignationSelected'] == "Director": - head_approval=True - director_approval=True - financial_approval=True - - - - # current_id = request.user.extrainfo - # remarks = request.POST.get('remarks') - - receiver = request.POST.get('receiver') - try: - receiver_id = User.objects.get(username=receiver) - except Exception as e: - messages.error(request, 'Enter a valid Username') - return redirect('/filetracking/') - recieve = request.POST.get('recieve') - try: - receive_design = Designation.objects.get(name=recieve) - except Exception as e: - receive_design = Designation.objects.get(name=recieve) - messages.error(request, 'Enter a valid Designation') - return redirect('/purchase-and-store/create_proposal/') - - - file_id = create_file( - uploader=request.user, - uploader_designation=designation, - receiver= receiver_id, - receiver_designation=receive_design, - src_module="ps1", - src_object_id="", - file_extra_JSON={"value": 2}, - attached_file=upload_file + file = File.objects.create( + uploader=uploader, + description=description, + subject=subject, + designation=designation, + upload_file=upload_file ) + IndentFile.objects.create( - file_info=get_object_or_404(File, pk=file_id), + file_info=file, item_name= item_name, quantity=quantity, present_stock=present_stock, estimated_cost=estimated_cost, purpose=purpose, specification=specification, - item_type=item_type, - item_subtype=item_subtype, + indent_type=indent_type, nature=nature, indigenous=indigenous, replaced = replaced , @@ -271,6 +166,37 @@ def create_proposal(request): purchased =purchased, ) + + current_id = request.user.extrainfo + remarks = request.POST.get('remarks') + + sender = request.POST.get('design') + current_design = HoldsDesignation.objects.select_related('user','working','designation').get(id=sender) + + receiver = request.POST.get('receiver') + try: + receiver_id = User.objects.get(username=receiver) + except Exception as e: + messages.error(request, 'Enter a valid Username') + return redirect('/filetracking/') + receive = request.POST.get('recieve') + try: + receive_design = Designation.objects.get(name=receive) + except Exception as e: + messages.error(request, 'Enter a valid Designation') + return redirect('/ps1/') + + upload_file = request.FILES.get('myfile') + + Tracking.objects.create( + file_id=file, + current_id=current_id, + current_design=current_design, + receive_design=receive_design, + receiver_id=receiver_id, + remarks=remarks, + upload_file=upload_file, + ) office_module_notif(request.user, receiver_id) messages.success(request,'Indent Filed Successfully!') @@ -281,60 +207,33 @@ def create_proposal(request): extrainfo = ExtraInfo.objects.select_related('user','department').all() holdsdesignations = HoldsDesignation.objects.select_related('user','working','designation').all() designations = HoldsDesignation.objects.select_related('user','working','designation').filter(user = request.user) - today = timezone.now().strftime('%Y-%m-%d') - - currDesig = request.session['currentDesignationSelected'] - dept_admin_designs = dept_admin_design - isAdmin = currDesig == 'ps_admin' or currDesig in dept_admin_designs - notifs = request.user.notifications.all() context = { 'file': file, 'extrainfo': extrainfo, 'holdsdesignations': holdsdesignations, 'designations': designations, - 'today': today, - 'isAdmin': isAdmin, - 'notifications':notifs } return render(request, 'ps1/composeIndent.html', context) +# @login_required(login_url = "/accounts/login") +# def compose_indent(request): +# file = File.objects.select_related('uploader__user','uploader__department','designation').all() +# extrainfo = ExtraInfo.objects.select_related('user','department').all() +# holdsdesignations = HoldsDesignation.objects.select_related('user','working','designation').all() +# designations = HoldsDesignation.objects.select_related('user','working','designation').filter(user = request.user) - -@login_required(login_url = "/accounts/login") -def composed_indents(request): - """ - The function is used to get all the files created by user(employee). - It gets all files created by user by filtering file(table) object by user i.e, uploader. - It displays user and file details of a file(table) of filetracking(model) in the - template of 'Saved files' tab. - - @param: - request - trivial. - - @variables: - draft - The File object filtered by uploader(user). - extrainfo - The Extrainfo object. - context - Holds data needed to make necessary changes in the template. - """ - - # draft = File.objects.filter(uploader=request.user.extrainfo) - # draft = File.objects.filter(uploader=request.user.extrainfo).order_by('-upload_date') - - # print(File.objects) - # extrainfo = ExtraInfo.objects.all() - # designation = Designation.objects.get(id=HoldsDesignation.objects.get(user=request.user).designation_id) - if request.session['currentDesignationSelected'] == "student": - return redirect('/dashboard') - - designation = HoldsDesignation.objects.filter(user=request.user, designation__name=request.session['currentDesignationSelected']).first() +# context = { +# 'file': file, +# 'extrainfo': extrainfo, +# 'holdsdesignations': holdsdesignations, +# 'designations': designations, +# } +# return render(request, 'ps1/composeIndent.html', context) - print(designation) - - return redirect(f'/purchase-and-store/indentview/{designation.id}') @login_required(login_url = "/accounts/login") -def archieved_files(request): +def composed_indents(request): """ The function is used to get all the files created by user(employee). It gets all files created by user by filtering file(table) object by user i.e, uploader. @@ -356,36 +255,13 @@ def archieved_files(request): # print(File.objects) # extrainfo = ExtraInfo.objects.all() # designation = Designation.objects.get(id=HoldsDesignation.objects.get(user=request.user).designation_id) - if request.session['currentDesignationSelected'] == "student": - return redirect('/dashboard') - - designation = HoldsDesignation.objects.filter(user=request.user, designation__name=request.session['currentDesignationSelected']).first() - - - return redirect(f'/purchase-and-store/archieveview/{designation.id}') - - - -@login_required(login_url = "/accounts/login") -def drafts_for_multiple_item(request): - """ - The function is used to get all the designations hold by the user. - - @param: - request - trivial. - - @variables: - context - Holds data needed to make necessary changes in the template. - """ - des = HoldsDesignation.objects.all().select_related().filter(user = request.user).first() - if request.session['currentDesignationSelected'] == "student": - return redirect('/dashboard') designation = HoldsDesignation.objects.filter(user=request.user) context = { + # 'draft': draft, + # 'extrainfo': extrainfo, 'designation': designation, } - return render(request, 'ps1/drafts1.html', context) - + return render(request, 'ps1/composed_indents.html', context) def drafts(request): """ @@ -409,24 +285,18 @@ def drafts(request): # print(File.objects) # extrainfo = ExtraInfo.objects.all() # designation = Designation.objects.get(id=HoldsDesignation.objects.get(user=request.user).designation_id) - if request.session['currentDesignationSelected'] == "student": - return redirect('/dashboard') - - designation = HoldsDesignation.objects.filter(user=request.user, designation__name=request.session['currentDesignationSelected']).first() - - - return redirect(f'/purchase-and-store/draftview/{designation.id}') + designation = HoldsDesignation.objects.filter(user=request.user) + context = { + # 'draft': draft, + # 'extrainfo': extrainfo, + 'designation': designation, + } + return render(request, 'ps1/drafts.html', context) @login_required(login_url = "/accounts/login") def indentview(request,id): - print("id : ",id) - if request.session['currentDesignationSelected'] == "student": - return redirect('/dashboard') - - designation = HoldsDesignation.objects.filter(user=request.user, designation__name=request.session['currentDesignationSelected']).first() - - if str(id) != str(designation.id): - return redirect(f'/purchase-and-store/indentview/{designation.id}') + + tracking_objects=Tracking.objects.all() tracking_obj_ids=[obj.file_id for obj in tracking_objects] draft_indent = IndentFile.objects.filter(file_info__in=tracking_obj_ids) @@ -437,105 +307,16 @@ def indentview(request,id): abcd = HoldsDesignation.objects.get(pk=id) s = str(abcd).split(" - ") designations = s[1] - notifs = request.user.notifications.all() context = { 'indents' : indents, 'extrainfo': extrainfo, 'designations': designations, - 'notifications':notifs } return render(request, 'ps1/indentview.html', context) -@login_required(login_url = "/accounts/login") -def archieveview(request,id): - if request.session['currentDesignationSelected'] == "student": - return redirect('/dashboard') - - designation = HoldsDesignation.objects.filter(user=request.user, designation__name=request.session['currentDesignationSelected']).first() - - if str(id) != str(designation.id): - return redirect(f'/purchase-and-store/archieveview/{designation.id}') - print("id : ",id); - print("request.user : ",request.user); - - abcd = HoldsDesignation.objects.get(pk=id) - s = str(abcd).split(" - ") - designations = s[1] - print("designations : ",designations); - - archived_files = view_archived( - username=request.user, - designation=designations, - src_module="ps1" - ) - - print("archived_files : ",archived_files); - for files in archived_files: - files['upload_date']=datetime.fromisoformat(files['upload_date']) - files['upload_date']=files['upload_date'].strftime("%B %d, %Y, %I:%M %p") - - notifs = request.user.notifications.all() - context = { - 'archieves' : archived_files, - 'designations': designations, - 'notifications':notifs - } - return render(request, 'ps1/archieve_view.html', context) - - -# @login_required(login_url = "/accounts/login") -# def draftview_multiple_items_indent(request,id): -# """ -# The function is used to get all the files created by user(employee). -# It gets all files created by user by filtering file(table) object by user i.e, uploader. -# It displays user and file details of a file(table) of filetracking(model) in the -# template of 'Saved files (new)' tab for indentfile with multiple items. - -# @param: -# request - trivial. - -# @variables: -# draft - The File object filtered by uploader(user). -# extrainfo - The Extrainfo object. -# context - Holds data needed to make necessary changes in the template. -# """ - - -# des = HoldsDesignation.objects.all().select_related().filter(user = request.user).first() -# if request.session['currentDesignationSelected'] == "student": -# return redirect('/dashboard') - -# indents= IndentFile2.objects.filter(file_info__in=request.user.extrainfo.uploaded_files.all()).select_related('file_info') -# indent_ids=[indent.file_info for indent in indents] -# filed_indents=Tracking.objects.filter(file_id__in=indent_ids) -# filed_indent_ids=[indent.file_id for indent in filed_indents] -# draft = list(set(indent_ids) - set(filed_indent_ids)) -# draft_indent=IndentFile2.objects.filter(file_info__in=draft).values("file_info") -# draft_files=File.objects.filter(id__in=draft_indent).order_by('-upload_date') -# extrainfo = ExtraInfo.objects.all() -# abcd = HoldsDesignation.objects.get(pk=id) -# s = str(abcd).split(" - ") -# designations = s[1] - -# context = { -# 'draft':draft_files, -# 'extrainfo': extrainfo, -# 'designations': designations, -# } -# return render(request, 'ps1/draftview1.html', context) - - - @login_required(login_url = "/accounts/login") def draftview(request,id): - if request.session['currentDesignationSelected'] == "student": - return redirect('/dashboard') - - designation = HoldsDesignation.objects.filter(user=request.user, designation__name=request.session['currentDesignationSelected']).first() - - if str(id) != str(designation.id): - return redirect(f'/purchase-and-store/draftview/{designation.id}') indents= IndentFile.objects.filter(file_info__in=request.user.extrainfo.uploaded_files.all()).select_related('file_info') indent_ids=[indent.file_info for indent in indents] @@ -548,55 +329,36 @@ def draftview(request,id): abcd = HoldsDesignation.objects.get(pk=id) s = str(abcd).split(" - ") designations = s[1] - notifs = request.user.notifications.all() context = { 'draft': draft_files, 'extrainfo': extrainfo, 'designations': designations, - 'notifications':notifs } return render(request, 'ps1/draftview.html', context) @login_required(login_url = "/accounts/login") def indentview2(request,id): - if request.session['currentDesignationSelected'] == "student": - return redirect('/dashboard') - - designation = HoldsDesignation.objects.filter(user=request.user, designation__name=request.session['currentDesignationSelected']).first() - - if str(id) != str(designation.id): - return redirect(f'/purchase-and-store/indentview2/{designation.id}') - abcd = HoldsDesignation.objects.get(pk=id) - s = str(abcd).split(" - ") - designations = s[1] - data = view_inbox(request.user.username, designations, "ps1") - outboxd = view_outbox(request.user.username, designations, "ps1") - data = sorted(data, key=lambda x: datetime.fromisoformat(x['upload_date']), reverse=True) - for item in data: - item['upload_date'] = datetime.fromisoformat(item['upload_date']) + indent_files = IndentFile.objects.all().values('file_info') + print(indent_files) + in_file = Tracking.objects.filter(file_id__in=indent_files,receiver_id=request.user).order_by("-receive_date") + + #print (File.designation) + abcd = HoldsDesignation.objects.get(pk=id) + s = str(abcd).split(" - ") + designations = s[1] - notifs = request.user.notifications.all() - context = { - 'receive_design':abcd, - 'in_file': data, - 'notifications':notifs + + 'in_file': in_file, + 'designations': designations, } return render(request, 'ps1/indentview2.html', context) - - - - - - - - @login_required(login_url = "/accounts/login") def inward(request): """ @@ -610,24 +372,18 @@ def inward(request): in_file - The Tracking object filtered by receiver_id i.e, present working user. context - Holds data needed to make necessary changes in the template. """ - if request.session['currentDesignationSelected'] == "student": - return redirect('/dashboard') - - designation = HoldsDesignation.objects.filter(user=request.user, designation__name=request.session['currentDesignationSelected']).first() - - - return redirect(f'/purchase-and-store/indentview2/{designation.id}') - - - + designation = HoldsDesignation.objects.filter(user=request.user) + in_file=Tracking.objects.filter(receiver_id=request.user).order_by('-receive_date') + context = { + 'in_file': in_file, + 'designation': designation, + } + return render(request, 'ps1/inwardIndent.html', context) @login_required(login_url = "/accounts/login") def confirmdelete(request,id): - des = HoldsDesignation.objects.all().select_related().filter(user = request.user).first() - if request.session['currentDesignationSelected'] == "student": - return redirect('/dashboard') file = File.objects.get(pk = id) context = { @@ -663,67 +419,29 @@ def forwardindent(request, id): # start = timer() # end = timer() - if request.session['currentDesignationSelected'] == "student": - return redirect('/dashboard') indent=IndentFile.objects.select_related('file_info').get(file_info=id) file=indent.file_info # start = timer() track = Tracking.objects.select_related('file_id__uploader__user','file_id__uploader__department','file_id__designation','current_id__user','current_id__department', -'current_design__user','current_design__working','current_design__designation','receiver_id','receive_design').filter(file_id=file).order_by('forward_date') +'current_design__user','current_design__working','current_design__designation','receiver_id','receive_design').filter(file_id=file) # end = timer() + - lastTrackInstance = track.last(); - # print(lastTrackInstance.receiver_id ," : " ,lastTrackInstance.receive_design) - # print(request.user); - # print(request.user == lastTrackInstance.receiver_id) - fileHistory=view_history(file.id) - lastElement = fileHistory[-1] - current_id=lastElement['current_id'] - isArchivable = False - print(request.user.username,"request.user.username") - print(current_id,"current_id") - if current_id == request.user.username: - isArchivable = True - if request.method == "POST": - # print('Mohit Will Win : ' , request.POST); - if 'finish' in request.POST: file.complete_flag = True file.save() - if 'Archieve' in request.POST: - print("inside archieve") - is_archived = archive_file(file_id=file.id) - print("is_archived : ",is_archived) - - if is_archived: - messages.success(request, 'Indent File Archived successfully') - return redirect('/purchase-and-store/forwardedIndent/{0}'.format(file.id)) - else: - messages.error(request, 'Indent File could not be archived') - if 'send' in request.POST: current_id = request.user.extrainfo remarks = request.POST.get('remarks') - sender_design_id = HoldsDesignation.objects.filter(user=request.user, designation__name=request.session['currentDesignationSelected']).first().id - - - sender_designationobj = HoldsDesignation.objects.get(id=sender_design_id).designation - sender_designation_name = sender_designationobj.name - - receiverHdid = request.POST.get('receive') - receiverHdobj = HoldsDesignation.objects.get(id=receiverHdid) - receiver = receiverHdobj.user.username - receive_design = receiverHdobj.designation.name - - # print("sender_design_id : ", sender_design_id ); - - + sender = request.POST.get('sender') + current_design = HoldsDesignation.objects.select_related('user','working','designation').get(id=sender) + receiver = request.POST.get('receiver') try: receiver_id = User.objects.get(username=receiver) except Exception as e: @@ -731,73 +449,102 @@ def forwardindent(request, id): designations = HoldsDesignation.objects.select_related('user','working','designation').filter(user=request.user) context = { + # 'extrainfo': extrainfo, + # 'holdsdesignations': holdsdesignations, 'designations': designations, 'file': file, 'track': track, } return render(request, 'ps1/forwardindent.html', context) + receive = request.POST.get('recieve') try: - receive_design = Designation.objects.get(name=receive_design) + receive_design = Designation.objects.get(name=receive) except Exception as e: messages.error(request, 'Enter a valid Designation') designations = HoldsDesignation.objects.select_related('user','working','designation').filter(user=request.user) context = { + # 'extrainfo': extrainfo, + # 'holdsdesignations': holdsdesignations, 'designations': designations, 'file': file, 'track': track, - 'lastTrackingInstance':lastTrackInstance } return render(request, 'ps1/forwardindent.html', context) - + # receive_design = receive_designation[0] upload_file = request.FILES.get('myfile') - forwarded_file_id = forward_file( - file_id=file.id, - receiver=receiver_id, - receiver_designation=receive_design, - file_extra_JSON={"key": 2}, + # return HttpResponse ("success") + Tracking.objects.create( + file_id=file, + current_id=current_id, + current_design=current_design, + receive_design=receive_design, + receiver_id=receiver_id, remarks=remarks, - file_attachment=upload_file + upload_file=upload_file, ) + check=str(request.user) + val=str(request.POST.get('approval')) + + + # if val=="accept": + # print("correct") + # if check=="ptandon" or check=="atul" or check=="prabin16" or check=="subirs" or check=="prabir": + # indent.head_approval=True + # elif check=="director": + # indent.director_approval=True + # elif check=="rizwan": + # indent.financial_approval=True + + # else: + # if check=="ptandon" or check=="atul" or check=="prabin16" or check=="subirs" or check=="prabir": + # indent.head_approval=False + # elif check=="director": + # indent.director_approval=False + # elif check=="rizwan": + # indent.financial_approval=False - # CREATOR -> HOD -> DIRECTOR/REGISTRAR -> DEPT_ADMIN -> - if((sender_designation_name in ["HOD (CSE)", "HOD (ECE)", "HOD (ME)", "HOD (SM)", "HOD (Design)", "HOD (Liberal Arts)", "HOD (Natural Science)"]) and (str(receive_design) in ["Director","Registrar"])): - indent.head_approval=True - elif ((sender_designation_name in ["Director","Registrar"]) and (str(receive_design) in dept_admin_design)): - indent.director_approval=True - elif ((sender_designation_name == "Accounts Admin") and ((str(receive_design) in dept_admin_design) or str(receive_design) == "ps_admin")): - indent.financial_approval=True - - + designs =[] designations = HoldsDesignation.objects.select_related('user','working','designation').filter(user=request.user) for designation in designations : s = str(designation).split(" - ") designs.append(s[1]) + + if val=="accept": + if any(d in designs for d in ("HOD (ME)", "HOD (ECE)", "CSE HOD", "HOD (Design)", "HOD (NS)")): + indent.head_approval=True + elif "Director" in designs: + indent.director_approval=True + indent.financial_approval=True + + else: + if any(d in designs for d in ("HOD (ME)", "HOD (ECE)", "CSE HOD", "HOD (Design)", "HOD (NS)")): + indent.head_approval=False + elif "Director" in designs: + indent.director_approval=False + indent.financial_approval=False + + indent.save() - office_module_notif(request.user, receiver_id) - messages.success(request, 'Indent File Forwarded successfully') - + + messages.success(request, 'Indent File sent successfully') + # start = timer() extrainfo = ExtraInfo.objects.select_related('user','department').all() holdsdesignations = HoldsDesignation.objects.select_related('user','working','designation').all() designations = HoldsDesignation.objects.select_related('user','working','designation').filter(user=request.user) - - - context = { - 'extrainfo': extrainfo, - 'holdsdesignations': holdsdesignations, + # 'extrainfo': extrainfo, + # 'holdsdesignations': holdsdesignations, 'designations':designations, 'file': file, 'track': track, 'indent':indent, - 'isArchivable':isArchivable, - 'lastTrackingInstance':lastTrackInstance } return render(request, 'ps1/forwardindent.html', context) @@ -820,9 +567,6 @@ def createdindent(request, id): holdsdesignations = HoldsDesignation objects. context - Holds data needed to make necessary changes in the template. """ - des = HoldsDesignation.objects.all().select_related().filter(user = request.user).first() - if request.session['currentDesignationSelected'] == "student": - return redirect('/dashboard') # start = timer() # end = timer() @@ -849,9 +593,6 @@ def createdindent(request, id): current_design = HoldsDesignation.objects.select_related('user','working','designation').get(id=sender) receiver = request.POST.get('receiver') - print("receiver: ", receiver) - print("receiverid : ", User.objects.get(username=receiver)) - try: receiver_id = User.objects.get(username=receiver) except Exception as e: @@ -881,32 +622,30 @@ def createdindent(request, id): 'track': track, } return render(request, 'ps1/createdindent.html', context) + + # receive_design = receive_designation[0] upload_file = request.FILES.get('myfile') # return HttpResponse ("success") - receiver_obj = User.objects.get(username=receiver) - forwarded_file_id = forward_file( - file_id=file.id, - receiver=receiver, - receiver_designation=receive_design, - file_extra_JSON={"key": 2}, + Tracking.objects.create( + file_id=file, + current_id=current_id, + current_design=current_design, + receive_design=receive_design, + receiver_id=receiver_id, remarks=remarks, - file_attachment=upload_file + upload_file=upload_file, ) - # Tracking.objects.create( - # file_id=file, - # current_id=current_id, - # current_design=current_design, - # receive_design=receive_design, - # receiver_id=receiver_id, - # remarks=remarks, - # upload_file=upload_file, - # ) messages.success(request, 'Indent File sent successfully') + # start = timer() + extrainfo = ExtraInfo.objects.select_related('user','department').all() + holdsdesignations = HoldsDesignation.objects.select_related('user','working','designation').all() designations = HoldsDesignation.objects.select_related('user','working','designation').filter(user=request.user) context = { + # 'extrainfo': extrainfo, + # 'holdsdesignations': holdsdesignations, 'designations':designations, 'file': file, 'track': track, @@ -915,57 +654,11 @@ def createdindent(request, id): return render(request, 'ps1/createdindent.html', context) -@login_required(login_url = "/accounts/login") -def forwardedIndent(request, id): - """ - The function is used to forward created indent files by user(employee) . - @param: - request - trivial. - id - id of the file object which the user intends to forward to other employee. - @variables: - file - The File object. - track - The Tracking object. - remarks = Remarks posted by user. - receiver = Receiver to be selected by user for forwarding file. - receiver_id = Receiver_id who has been selected for forwarding file. - upload_file = File attached by user. - extrainfo = ExtraInfo object. - holdsdesignations = HoldsDesignation objects. - context - Holds data needed to make necessary changes in the template. - """ - des = HoldsDesignation.objects.all().select_related().filter(user = request.user).first() - if request.session['currentDesignationSelected'] == "student": - return redirect('/dashboard') - # start = timer() - - # end = timer() - indent=IndentFile.objects.select_related('file_info').get(file_info=id) - file=indent.file_info - # start = timer() - track = Tracking.objects.select_related('file_id__uploader__user','file_id__uploader__department','file_id__designation','current_id__user','current_id__department', -'current_design__user','current_design__working','current_design__designation','receiver_id','receive_design').filter(file_id=file) - # end = timer() - - - - - - - designations = HoldsDesignation.objects.select_related('user','working','designation').filter(user=request.user) - context = { - 'designations':designations, - 'file': file, - 'track': track, - 'indent':indent, - } - return render(request, 'ps1/forwardedIndent.html', context) def AjaxDropdown1(request): - des = HoldsDesignation.objects.all().select_related().filter(user = request.user).first() - if request.session['currentDesignationSelected'] == "student": - return redirect('/dashboard') + print('brefore post') if request.method == 'POST': value = request.POST.get('value') # print(value) @@ -983,6 +676,7 @@ def AjaxDropdown1(request): def AjaxDropdown(request): + print('asdasdasdasdasdasdasdas---------------\n\n') # Name = ['student','co-ordinator','co co-ordinator'] # design = Designation.objects.filter(~Q(name__in=(Name))) # hold = HoldsDesignation.objects.filter(Q(designation__in=(design))) @@ -991,9 +685,6 @@ def AjaxDropdown(request): # for h in hold: # arr.append(ExtraInfo.objects.filter(user=h.user)) - des = HoldsDesignation.objects.all().select_related().filter(user = request.user).first() - if request.session['currentDesignationSelected'] == "student": - return redirect('/dashboard') if request.method == 'POST': value = request.POST.get('value') @@ -1012,9 +703,6 @@ def test(request): @login_required(login_url = "/accounts/login") def delete(request,id): - - if request.session['currentDesignationSelected'] not in dept_admin_design + ["ps_admin"]: - return redirect('/dashboard') file = File.objects.get(pk = id) file.delete() @@ -1033,40 +721,76 @@ def delete(request,id): return redirect('/ps1/composed_indents/') +@login_required(login_url = "/accounts/login") +def Stock_Entry(request): + + + if request.method=='GET' : + + return HttpResponseRedirect('../stock_view') -# stock Edit will provide the prepoulated form for updation of information. + if request.method =="POST": + + + #dealing_assistant_id=request.POST.get('dealing_assistant_id') + id=request.POST.get('id') + + + temp1=File.objects.get(id=id) + temp=IndentFile.objects.get(file_info=temp1) + + + + dealing_assistant_id=request.user.extrainfo + + item_id=temp + item_name=request.POST.get('item_name') + vendor=request.POST.get('vendor') + current_stock=request.POST.get('current_stock') + recieved_date=request.POST.get('recieved_date') + bill=request.FILES.get('bill') + + + # staff=Staff.objects.get(id=request.user.extrainfo) + + StockEntry.objects.create(item_id=item_id,item_name= item_name,vendor=vendor,current_stock=current_stock,dealing_assistant_id=dealing_assistant_id,bill=bill,recieved_date=recieved_date,) + IndentFile.objects.filter(file_info=temp).update(purchased=True) + + return HttpResponseRedirect('../stock_view') + + + + + + @login_required(login_url = "/accounts/login") def stock_edit(request): - - if request.session['currentDesignationSelected'] not in dept_admin_design + ["ps_admin"]: - return redirect('/dashboard') + # stocks=StockEntry.objects.get(pk=id) + # return render(request,'ps1/stock_edit.html',{'StockEntry':stocks}) + if request.method =="POST": id=request.POST.get('id') temp=File.objects.get(id=id) temp1=IndentFile.objects.get(file_info=temp) - Stock_Entry=StockEntry.objects.get(item_id=temp1) - - print(Stock_Entry.recieved_date); - formatted_received_date = Stock_Entry.recieved_date.strftime("%Y-%m-%d") - - persons = ExtraInfo.objects.filter(user_type__in=["staff"]) - return render(request,'ps1/stock_edit.html',{'StockEntry':Stock_Entry,'persons':persons,'formatted_received_date': formatted_received_date}) - - return HttpResponseRedirect('../stock_entry_view') + stocks=StockEntry.objects.get(item_id=temp1) + return render(request,'ps1/stock_edit.html',{'StockEntry':stocks}) + + # if 'save' in request.POST: + # stocks.item_name=request.POST.get('item_name') + # stocks.vendor=request.POST.get('vendor') + # stocks.current_stock=request.POST.get('current_stock') + # stocks.recieved_date=request.POST.get('recieved_date') + # stocks.bill=request.FILES.get('bill') + # stocks.save() + + return HttpResponseRedirect('../stock_view') #else: # print("ELSE") # return render(request,'ps1/stock_edit.html',{'StockEntry':stocks}) - - -# This view defines the logic for stock -@login_required(login_url = "/accounts/login") + def stock_update(request): - - if request.session['currentDesignationSelected'] not in dept_admin_design + ["ps_admin"]: - return redirect('/dashboard') - if request.method =="POST": if 'save' in request.POST: id=request.POST.get('id') @@ -1074,184 +798,36 @@ def stock_update(request): temp1=IndentFile.objects.get(file_info=temp) stocks=StockEntry.objects.get(item_id=temp1) - # Retrieve the ExtraInfo instance based on the provided string value - - print(request.POST); - - dealing_assistant_id_str = request.POST.get('dealing_assistant_id').split('-')[0].strip(); - print({dealing_assistant_id_str}) - - dealing_assistant_instance = ExtraInfo.objects.get(id=dealing_assistant_id_str) - - stocks.dealing_assistant_id = dealing_assistant_instance - + stocks.item_name=request.POST.get('item_name') stocks.vendor=request.POST.get('vendor') stocks.current_stock=request.POST.get('current_stock') - # stocks.recieved_date=request.POST.get('recieved_date') + #stocks.recieved_date=request.POST.get('recieved_date') stocks.bill=request.FILES.get('bill') - stocks.location = request.POST.get('location') - stocks.recieved_date = request.POST.get('recieved_date') - stocks.save() - return HttpResponseRedirect('../stock_entry_view') - + return HttpResponseRedirect('../stock_view') + -# this is know the current situation of stocks present in any department. - -@login_required(login_url = "/accounts/login") -def current_stock_view(request): - - if request.session['currentDesignationSelected'] not in dept_admin_design + ["ps_admin"]: - return redirect('/dashboard') - - # for handling the search after submission of filters form - if request.method=="POST": - print('the requested data : ', request.POST) - department = request.POST.get('department') - type = request.POST.get('type') - - # StockEntryId__item_id__file_info_grade - StockItems = StockItem.objects.filter( - department=department, - StockEntryId__item_id__item_type=type - ) - - grouped_items = StockItems.values('StockEntryId__item_id__item_type','StockEntryId__item_id__item_subtype', 'department').annotate(total_quantity=Count('id')) - - grouped_items_list = [ - { - 'item_type': item['StockEntryId__item_id__item_type'], - 'item_subtype': item['StockEntryId__item_id__item_subtype'], - 'department': DepartmentInfo.objects.get(id=department), - 'total_quantity': item['total_quantity'] - } - for item in grouped_items - ] - - - firstStock=StockItems.first() - - print(grouped_items_list) - - - return render(request,'ps1/current_stock_view.html',{'stocks':grouped_items_list,'first_stock':firstStock, - 'stockItems':StockItems}) - # return render(request,'ps1/current_stock_view.html',{'stocks':StockItems,'quantity':StockItems.count()}) - - - # THIS IS HARDCODED FOR NOW . - itemsTypes=['Equipment','Machinery','Furniture','Fixture'] - departmentUser=request.user.extrainfo.department - - if request.session['currentDesignationSelected'] in dept_admin_design: - # if department admin then only show the stocks of that department to them - - print(dept_admin_to_dept[request.session['currentDesignationSelected']]); - deptId = DepartmentInfo.objects.values('id', 'name').get(name=dept_admin_to_dept[request.session['currentDesignationSelected']]) - - - - departments=[deptId] - - StockItems = StockItem.objects.filter(department=deptId['id']) - - elif request.session['currentDesignationSelected'] == "ps_admin": - departments=DepartmentInfo.objects.values('id','name').all() - - StockItems = StockItem.objects.all() - else : - return redirect('/dashboard') - - - grouped_items = StockItems.values('StockEntryId__item_id__item_type','StockEntryId__item_id__item_subtype','department').annotate(total_quantity=Count('id')) - - grouped_items_list = [ - { - 'item_type': item['StockEntryId__item_id__item_type'], - 'item_subtype': item['StockEntryId__item_id__item_subtype'], - 'department': DepartmentInfo.objects.values('id','name').get(id=item['department']), - 'departmentId': item['department'], - 'total_quantity': item['total_quantity'] - } - for item in grouped_items - ] - - - return render(request,'ps1/current_stock_view_filter.html',{'itemsTypes':itemsTypes,'departments':departments,'stocks':grouped_items_list}) - -# to display stock items which are having similar item_type and department.(used in current_stock_view) - -@login_required(login_url = "/accounts/login") -def stock_item_view(request): - - if request.session['currentDesignationSelected'] not in dept_admin_design + ["ps_admin"]: - return redirect('/dashboard') - - if request.method=="GET": - return HttpResponseRedirect('../current_stock_view') - - if request.method=="POST": - departmentId = request.POST.get('departmentId') - type = request.POST.get('item_type') - subtype = request.POST.get('item_subtype') - - # StockEntryId__item_id__file_info_grade - StockItems = StockItem.objects.filter( - department=departmentId, - StockEntryId__item_id__item_type=type, - StockEntryId__item_id__item_subtype=subtype - ) - return render(request,'ps1/stock_item_view.html',{'stocks':StockItems}) -@login_required(login_url = "/accounts/login") -def stock_entry_view(request): - - # ! CHALLENGE HERE IS HOW TO SHOW THE TRANSFERRED STOCKS AND CURRENTLY PRESENT. - if request.session['currentDesignationSelected'] not in dept_admin_design + ["ps_admin"]: - return redirect('/dashboard') - - - if request.session['currentDesignationSelected'] in dept_admin_design: - # if department admin then only show the stocks of that department to them - print("I am a department admin "); - deptId = DepartmentInfo.objects.get(name=dept_admin_to_dept[request.session['currentDesignationSelected']]) - stocks=StockEntry.objects.filter(item_id__file_info__uploader__department=deptId) - elif request.session['currentDesignationSelected'] == "ps_admin": - print("I am a PS admin "); - stocks=StockEntry.objects.all() - else : - return redirect('/dashboard') - - print('stocks : ',stocks) +# def stock_view(request): +# sto=StockEntry.objects.all() +# return render(request,'ps1/stock_view.html',{'StockEntry':sto}) +# @login_required(login_url = "/accounts/login") +def stock_view(request): - return render(request,'ps1/stock_entry_view.html',{'stocks':stocks}) - -@login_required(login_url = "/accounts/login") -def stock_entry_item_view(request): - - if request.session['currentDesignationSelected'] not in dept_admin_design + ["ps_admin"]: - return redirect('/dashboard') - - department = request.user.extrainfo.department - file_id = request.POST.get('id') - temp=File.objects.get(id=file_id) - temp1=IndentFile.objects.get(file_info=temp) - Stock_Entry=StockEntry.objects.get(item_id=temp1) - - - stocks=StockItem.objects.filter(StockEntryId=Stock_Entry) - - return render(request,'ps1/stock_entry_item_view.html',{'stocks':stocks,'first_stock':stocks.first()}) - - - - + sto=StockEntry.objects.all() + if sto: + temp=sto.first() + + if temp.item_id.purchased: + print("Purchase Succesful") + print() + print() + + return render(request,'ps1/stock_view.html',{'sto':sto}) @login_required(login_url = "/accounts/login") def stock_delete(request): - if request.session['currentDesignationSelected'] not in dept_admin_design + ["ps_admin"]: - return redirect('/dashboard') if request.method=='POST': @@ -1262,387 +838,26 @@ def stock_delete(request): temp1=IndentFile.objects.get(file_info=temp) stocks=StockEntry.objects.get(item_id=temp1) stocks.delete() - return HttpResponseRedirect('../stock_entry_view') - - - - - + return HttpResponseRedirect('../stock_view') @login_required(login_url = "/accounts/login") def entry(request): - if request.session['currentDesignationSelected'] not in dept_admin_design + ["ps_admin"]: - return redirect('/dashboard') - - # To Add stock corresponding to 'id' indent File. + if request.method=='POST': - # id is the id of the File Object here id=request.POST.get('id') - RequestFile = File.objects.select_related('uploader').get(id=id) - - # Requester is the person who uploaded the IndentFile Initially and you can access all of the requesters info available in User model (AbstractUser) like username ,email etc. - Requester = RequestFile.uploader.user - - # RequestFile.uploader contains the Extra information about the uploader of the RequestFile (using ExtraInfo Foreign key) - - - persons = ExtraInfo.objects.filter(user_type__in=["staff"]) - - CorrespondingIndentFile=IndentFile.objects.get(file_info=RequestFile) - return render(request,'ps1/StockEntry.html',{'id':id, 'indent':CorrespondingIndentFile,'RequestFile':RequestFile,'persons':persons}) - - - - # ent=IndentFile.objects.all() - des = HoldsDesignation.objects.all().select_related().filter(user = request.user).first() - department = request.user.extrainfo.department.name - - # this is to filter out the indent files only specific to a department like if A is a department Admin then he should only see the indent files corresponding to his department. - if request.session['currentDesignationSelected'] in dept_admin_design: - IndentFiles=IndentFile.objects.filter(file_info__uploader__department__name=dept_admin_to_dept[request.session['currentDesignationSelected']]) - else : - # otherwise all the indent files will be shown. - IndentFiles=IndentFile.objects.all() - return render(request,'ps1/entry.html',{'IndentFiles':IndentFiles}) - - -@login_required(login_url = "/accounts/login") -def Stock_Entry(request): - - if request.session['currentDesignationSelected'] not in dept_admin_design + ["ps_admin"]: - return redirect('/dashboard') - else : - if request.method=='GET' : - return HttpResponseRedirect('../stock_entry_view') + temp=File.objects.get(id=id) + temp1=IndentFile.objects.get(file_info=temp) + return render(request,'ps1/StockEntry.html',{'id':id, 'indent':temp1}) - if request.method =="POST": - - id=request.POST.get('id') - temp1=File.objects.get(id=id) - temp=IndentFile.objects.get(file_info=temp1) - - item_id=temp - dealing_assistant_id=request.user.extrainfo - vendor=request.POST.get('vendor') - current_stock=request.POST.get('current_stock') - recieved_date=request.POST.get('recieved_date') - bill=request.FILES.get('bill') - location= request.POST.get('location') - - StockEntry.objects.create( - item_id=item_id, - vendor=vendor, - current_stock=current_stock, - dealing_assistant_id=dealing_assistant_id, - bill=bill, - recieved_date=recieved_date, - location=location - ) - - # marking the indent file as done - IndentFile.objects.filter(file_info=temp).update(purchased=True) - return HttpResponseRedirect('../stock_entry_view') - + + ent=IndentFile.objects.all() + return render(request,'ps1/entry.html',{'ent':ent}) + def dealing_assistant(request): - des = HoldsDesignation.objects.all().select_related().filter(user = request.user).first() - if request.session['currentDesignationSelected'] == "student": - return redirect('/dashboard') print(request.user.extrainfo.id) print(type(request.user.extrainfo.id)) if request.user.extrainfo.id=='132' : - return redirect('/purchase-and-store/entry/') + return redirect('/ps1/entry/') else: return redirect('/ps1') - - -@login_required(login_url = "/accounts/login") -def generate_report(request): - - if request.session['currentDesignationSelected'] not in dept_admin_design + ["ps_admin"]: - return redirect('/dashboard') - - - if request.method =='GET': - if request.session['currentDesignationSelected'] in dept_admin_design: - - deptId = DepartmentInfo.objects.values('id', 'name').get(name=dept_admin_to_dept[request.session['currentDesignationSelected']]) - departments=[deptId] - - elif request.session['currentDesignationSelected'] == "ps_admin": - departments=DepartmentInfo.objects.values('id','name').all() - - return render(request,'ps1/generate_report_filter.html',{'departments':departments}) - - - if request.method=="POST": - - - departments = request.POST.getlist('departments') - start_date = request.POST.get('start_date'); - finish_date = request.POST.get('finish_date'); - - - - start_date = datetime.strptime(start_date, '%Y-%m-%d').date() - finish_date = datetime.strptime(finish_date, '%Y-%m-%d').date() - - if(departments[0]=='all'): - StockItems = StockItem.objects.filter(StockEntryId__recieved_date__range=(start_date, finish_date)) - else : - StockItems = StockItem.objects.filter(department__in=departments, StockEntryId__recieved_date__range=(start_date, finish_date)) - - - grouped_items = StockItems.values('StockEntryId__item_id__item_type','StockEntryId__item_id__item_subtype','department').annotate(total_quantity=Count('id')) - - grouped_items_list = [ - { - 'item_type': item['StockEntryId__item_id__item_type'], - 'item_subtype': item['StockEntryId__item_id__item_subtype'], - 'department': DepartmentInfo.objects.values('id','name').get(id=item['department']), - 'departmentId': item['department'], - 'total_quantity': item['total_quantity'], - 'StockItems': StockItem.objects.filter( - department=item['department'], - StockEntryId__item_id__item_type=item['StockEntryId__item_id__item_type'] - ) - } - for item in grouped_items - ] - - - return render(request,'ps1/generate_report.html',{'departments':departments,'stocks':grouped_items_list}) - - -@login_required(login_url = "/accounts/login") -def report(request): - id=request.POST.get('id') - designations = HoldsDesignation.objects.select_related('user','working','designation').filter(user=request.user) - indent=IndentFile.objects.select_related('file_info').get(file_info=id) - sto=StockEntry.objects.select_related('item_id').get(item_id=indent) - file=indent.file_info - total_stock = indent.quantity + indent.present_stock - - print(sto.recieved_date) - - context = { - 'designations':designations, - 'file': file, - 'indent':indent, - 'sto' : sto, - 'total_stock': total_stock - } - - return render(request,'ps1/report.html',context) - - -def view_bill(request, stock_entry_id): - if request.session['currentDesignationSelected'] not in dept_admin_design + ["ps_admin"]: - return redirect('/dashboard') - - print('Hello I am here ' , stock_entry_id) - stock_entry = get_object_or_404(StockEntry, pk=stock_entry_id) - - print(stock_entry); - - # Check if the bill file exists - if stock_entry.bill: - # Read the contents of the bill file - bill_content = stock_entry.bill.read() - - # Return the bill file as a response - response = HttpResponse(bill_content, content_type='application/pdf') - response['Content-Disposition'] = f'attachment; filename="{stock_entry.bill.name}"' - return response - else: - # If the bill file does not exist, return a 404 response - return HttpResponse("Bill not found", status=404) - - - -# This is to return a list of stocks available for transfer. -@login_required(login_url = "/accounts/login") -def perform_transfer(request): - - if request.session['currentDesignationSelected'] not in dept_admin_design + ["ps_admin"]: - return redirect('/dashboard') - - if request.method == "POST": - - selected_stock_items = request.POST.getlist('selected_stock_items[]') - indentId = request.POST.get('indentId') - dest_location = request.POST.get('dest_location') - - # print(selected_stock_items) - # print('dest_location : ',dest_location); - - # Use regular expression to extract the number - match = re.search(r'\((\d+)\)', indentId) - - number = match.group(1) # Extract the number from the match - - myIndent = IndentFile.objects.get(file_info=number) - - moreStocksRequired = myIndent.quantity - len(selected_stock_items) - # print('dest_destination : ', myIndent.file_info.uploader.department) - - for item in selected_stock_items: - stock_item = StockItem.objects.get(id=item) - # print('src dest : ', stock_item.department) - - store_cur_dept = stock_item.department; - store_cur_location = stock_item.location; - - # changing the attributes for this stock item as being transferred. - stock_item.department=myIndent.file_info.uploader.department; - stock_item.location=dest_location; - stock_item.inUse= True - stock_item.isTransferred= True - # if a stock_item is been transferred then obviously it will be put into use. - stock_item.save(); - - StockTransfer.objects.create( - indent_file = myIndent, - src_dept=store_cur_dept, - dest_dept=myIndent.file_info.uploader.department, - stockItem=stock_item, - src_location=store_cur_location, - dest_location=dest_location - ) - - messages.success(request,'Stock Transfer Done Successfully.!') - # if the quantity required for this indent file is fulfilled we should mark this indentfile as done. - if(moreStocksRequired<=0): - myIndent.purchased=True - else : - myIndent.quantity=moreStocksRequired; - - myIndent.save(); - - department = request.user.extrainfo.department.name - - return JsonResponse({'success':True}) - -# This is to get the list of all the available stock items for transfer. -@login_required(login_url = "/accounts/login") -def stock_transfer(request): - if request.session['currentDesignationSelected'] not in dept_admin_design + ["ps_admin"]: - return redirect('/dashboard') - - if request.method =="POST": - # id is the of File instance id in db. - id=request.POST.get('id') - - temp=File.objects.get(id=id) - temp1=IndentFile.objects.get(file_info=temp) - - item_type_required =temp1.item_type - - available_items = StockItem.objects.filter( - StockEntryId__item_id__item_type=item_type_required, # Foreign key traversal to IndentFile model - inUse=False # Filter for inUse=False - ) - - print(available_items) - return render(request,'ps1/stock_transfer.html',{'indentFile': temp1,'available_items':available_items}) - - return HttpResponseRedirect('../stock_transfer') - - -# to view the transfers -@login_required(login_url = "/accounts/login") -def view_transfer(request): - curr_desg = request.session['currentDesignationSelected'] - if curr_desg not in dept_admin_design + ["ps_admin"]: - return redirect('/dashboard') - - if curr_desg=="ps_admin": - stockTransfers = StockTransfer.objects.all(); - - elif curr_desg in dept_admin_design: - user_dept = DepartmentInfo.objects.get(name=dept_admin_to_dept[curr_desg]) - stockTransfers = StockTransfer.objects.filter( - Q(src_dept=user_dept) | Q(dest_dept=user_dept) - ) - - # print('stock Transfers : ',stockTransfers); - - return render(request,'ps1/view_transfer.html',{'stockTransfers': stockTransfers}) - -@login_required(login_url = "/accounts/login") -def outboxview2(request,id): - abcd = HoldsDesignation.objects.get(pk=id) - s = str(abcd).split(" - ") - designations = s[1] - data = view_outbox(request.user.username, designations, "ps1") - data = sorted(data, key=lambda x: datetime.fromisoformat(x['upload_date']), reverse=True) - - for item in data: - item['upload_date'] = datetime.fromisoformat(item['upload_date']) - - context = { - 'receive_design':abcd, - 'in_file': data, - } - return render(request, 'ps1/outboxview2.html', context) - -@login_required(login_url = "/accounts/login") -def outboxview(request): - - if request.session['currentDesignationSelected'] == "student": - return redirect('/dashboard') - - designation = HoldsDesignation.objects.filter(user=request.user, designation__name=request.session['currentDesignationSelected']).first() - - - return redirect(f'/purchase-and-store/outboxview2/{designation.id}') - - -@login_required(login_url="/accounts/login") -def updateStockItemInUse(request): - print("updatestockiteminuse"); - - if request.session['currentDesignationSelected'] not in dept_admin_design + ["ps_admin"]: - return redirect('/dashboard') - - if request.method=="GET": - return redirect('../current_stock_view') - - - if request.method == "POST": - - # print(request.POST); - - # Get the JSON data from the request body - json_data = json.loads(request.POST.get('selected_stock_items')) - - - # print('jsondata : ', json_data) - - for item in json_data: - item_id = item['id'] - checked = item['checked'] - stock_item = StockItem.objects.get(id=item_id) - stock_item.inUse = checked - stock_item.save() - - return JsonResponse({'success': True}) - - # return HttpResponseRedirect('../current_stock_view') - - -@login_required(login_url="/accounts/login") -def item_detail(request, id): - if request.session['currentDesignationSelected'] not in dept_admin_design + ["ps_admin"]: - return redirect('/dashboard') - - stock_transfers = StockTransfer.objects.filter(stockItem=id).order_by('dateTime') - stock_item = StockItem.objects.filter(id=id).first(); - # Do something with the stock_transfers - - context = { - 'transfers': stock_transfers, - 'item': stock_item - } - - return render(request, 'ps1/stock_item_details.html', context) diff --git a/FusionIIIT/applications/recruitment/migrations/0001_initial.py b/FusionIIIT/applications/recruitment/migrations/0001_initial.py index 489d77996..9cf2d3a77 100644 --- a/FusionIIIT/applications/recruitment/migrations/0001_initial.py +++ b/FusionIIIT/applications/recruitment/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 3.1.5 on 2024-07-16 15:44 +# Generated by Django 3.1.5 on 2023-03-15 18:53 import datetime from django.conf import settings diff --git a/FusionIIIT/applications/research_procedures/admin.py b/FusionIIIT/applications/research_procedures/admin.py index e9cb225c4..77adc5b1c 100644 --- a/FusionIIIT/applications/research_procedures/admin.py +++ b/FusionIIIT/applications/research_procedures/admin.py @@ -1,21 +1,30 @@ from django.contrib import admin -from applications.research_procedures.models import * +from applications.research_procedures.models import Patent, ResearchGroup,ResearchProject,ConsultancyProject,TechTransfer from django.utils.html import format_html # Adding a custom admin view for patent +class PatentAdmin(admin.ModelAdmin): -class ResearchGroupAdmin(admin.ModelAdmin): - list_display = ["name","description"] + list_filter = ('status',) + search_fields = ['title', 'application_id'] -# Register your models here. + def _status(self, obj): + color = "orange" + if obj.status == "Approved": + color = "green" + elif obj.status == "Disapproved": + color = "red" + return format_html('%s' % (color, obj.status)) -admin.site.register(projects) -admin.site.register(staff_allocations) -admin.site.register(financial_outlay) -admin.site.register(category) -# admin.site.register(requests) -# admin.site.register(rspc_inventory) -# admin.site.register(project_staff_info) + list_display = ["faculty_id","title","_status"] +class ResearchGroupAdmin(admin.ModelAdmin): + list_display = ["name","description"] + +# Register your models here. +admin.site.register(Patent,PatentAdmin) +admin.site.register(ResearchProject) +admin.site.register(ConsultancyProject) +admin.site.register(TechTransfer) # admin.site.register(ResearchGroup,ResearchGroupAdmin) \ No newline at end of file diff --git a/FusionIIIT/applications/research_procedures/api/serializers.py b/FusionIIIT/applications/research_procedures/api/serializers.py index 5c4a8bc34..86ed8b726 100644 --- a/FusionIIIT/applications/research_procedures/api/serializers.py +++ b/FusionIIIT/applications/research_procedures/api/serializers.py @@ -1,51 +1,8 @@ from rest_framework.serializers import ModelSerializer -from rest_framework import serializers - -from applications.research_procedures.models import * +from applications.research_procedures.models import Patent # Create a Serializer for Model Patent -class ProjectSerializer(ModelSerializer): - class Meta: - # model = projects - fields = '__all__' - -class Project_serializer(serializers.ModelSerializer): - class Meta: - model = projects - fields = '__all__' - - def create(self, validated_data): - return projects.objects.create(**validated_data) - -class financial_outlay_serializer(serializers.ModelSerializer): - class Meta: - model = financial_outlay - fields = '__all__' - - def create(self, validated_data): - return financial_outlay.objects.create(**validated_data) - -class category_serializer(serializers.ModelSerializer): +class PatentSerializer(ModelSerializer): class Meta: - model = category - fields = '__all__' - - def create(self, validated_data): - return category.objects.create(**validated_data) - -class staff_allocations_serializer(serializers.ModelSerializer): - class Meta: - model = staff_allocations - fields = '__all__' - - def create(self, validated_data): - return staff_allocations.objects.create(**validated_data) - -class requests_serializer(serializers.ModelSerializer): - class Meta: - model = requests - fields = '__all__' - - def create(self, validated_data): - return requests.objects.create(**validated_data) - + model = Patent + fields = '__all__' \ No newline at end of file diff --git a/FusionIIIT/applications/research_procedures/api/urls.py b/FusionIIIT/applications/research_procedures/api/urls.py index 55c56e10b..67a7e1fd3 100644 --- a/FusionIIIT/applications/research_procedures/api/urls.py +++ b/FusionIIIT/applications/research_procedures/api/urls.py @@ -1,45 +1,8 @@ from rest_framework.routers import DefaultRouter -from .views import * -from django.urls import include,path -from django.conf.urls import url -from . import views - -app_name="research_procedures" +from .views import PatentViewSet router = DefaultRouter() -# router.register(r'patent', PatentViewSet) +router.register(r'patent', PatentViewSet) urlpatterns = router.urls - -urlpatterns = [ - # url(r'^$', views.view_projects, name='patent_registration'), - # url(r'^update$', views.patent_status_update, name='patent_status_update'), - # url(r'^research_group$', views.research_group_create, name='research_group_create'), - # url(r'^project_insert$',views.project_insert,name='project_insert'), - # url(r'^consult_insert$',views.consult_insert,name='consult_insert'), - # url(r'^add_projects$',views.add_projects,name='add_projects'), - # url(r'^view_projects$',views.view_projects,name='view_projects'), - # # path('add_requests///',views.add_requests,name='add_requests'), - # url(r'^api/',include('applications.research_procedures.api.urls')), - # path('view_requests//',views.view_requests), - path('projects',views.view_projects), - path('view_project_info//',views.view_project_info), - # path('submit_closure_report//',views.submit_closure_report, name="submit_closure_report"), - # path('add_fund_requests//',views.add_fund_requests, name="add_fund_requests"), - # path('add_staff_requests//',views.add_staff_requests, name="add_staff_requests"), - path('view_project_inventory//',views.view_project_inventory, name="view_project_inventory"), - path('view_project_staff//',views.view_project_staff, name="view_project_staff"), - # path('add_financial_outlay//',views.add_financial_outlay, name="add_financial_outlay"), - # path('financial_outlay//',views.financial_outlay_form, name="financial_outlay_form"), - path('view_financial_outlay//',views.view_financial_outlay, name="view_financial_outlay"), - # path('add_staff_details//',views.add_staff_details, name="add_staff_details"), - path('view_staff_details//',views.view_staff_details, name="view_staff_details"), - # path('add_staff_request//',views.add_staff_request, name="add_staff_request"), - # path('inbox',views.inbox, name="inbox"), - # path('view_request_inbox',views.view_request_inbox, name="view_request_inbox"), - # path('forward_request',views.forward_request, name="forward_request"), - - - -] print("URL patterns",urlpatterns) \ No newline at end of file diff --git a/FusionIIIT/applications/research_procedures/api/views.py b/FusionIIIT/applications/research_procedures/api/views.py index 20483cb8e..169ffde1a 100644 --- a/FusionIIIT/applications/research_procedures/api/views.py +++ b/FusionIIIT/applications/research_procedures/api/views.py @@ -1,790 +1,9 @@ from rest_framework.viewsets import ModelViewSet -from applications.research_procedures.models import * -from .serializers import * -from rest_framework.decorators import api_view +from applications.research_procedures.models import Patent +from .serializers import PatentSerializer from rest_framework.permissions import IsAuthenticatedOrReadOnly -from django.shortcuts import redirect, render, get_object_or_404 -from rest_framework.response import Response -from django.http import JsonResponse -from django.contrib import messages -from applications.research_procedures.models import * -from applications.globals.models import ExtraInfo, HoldsDesignation, Designation -from django.core.files.storage import FileSystemStorage -from django.core.exceptions import ObjectDoesNotExist -from notification.views import research_procedures_notif -from django.urls import reverse -from django.contrib.auth.decorators import login_required -import datetime -from django.utils import timezone - -from collections import defaultdict -from applications.filetracking.sdk.methods import * - -# # Faculty can file patent and view status of it. - -# @login_required -# def patent_registration(request): - - -# return render(request ,"rs/research.html") - -# @login_required -# #dean_rspc can update status of patent. -# def patent_status_update(request): - -# user = request.user -# user_extra_info = ExtraInfo.objects.get(user=user) -# user_designations = HoldsDesignation.objects.filter(user=user) -# if request.method=='POST': -# if(user_designations.exists()): -# if(user_designations.first().designation.name == "dean_rspc" and user_extra_info.user_type == "faculty"): -# patent_application_id = request.POST.get('id') -# patent = Patent.objects.get(application_id=patent_application_id) -# patent.status = request.POST.get('status') -# patent.save() -# messages.success(request, 'Patent status updated successfully') -# # Create a notification for the user about the patent status update -# dean_rspc_user = HoldsDesignation.objects.get(designation=Designation.objects.filter(name='dean_rspc').first()).working -# research_procedures_notif(dean_rspc_user,patent.faculty_id.user,request.POST.get('status')) -# else: -# messages.error(request, 'Only Dean RSPC can update status of patent') -# return redirect(reverse("research_procedures:patent_registration")) - -# @login_required -# def research_group_create(request): - -# user = request.user -# user_extra_info = ExtraInfo.objects.get(user=user) -# if request.method=='POST': -# if user_extra_info.user_type == "faculty": -# form = ResearchGroupForm(request.POST) - -# if form.is_valid(): -# form.save() -# messages.success(request, 'Research group created successfully') -# else: -# messages.error(request, 'Only Faculty can create research group') -# return redirect(reverse("research_procedures:patent_registration")) - -# @login_required -# def project_insert(request): -# user = get_object_or_404(ExtraInfo, user=request.user) -# pf = user.id - -# research_project = ResearchProject() -# research_project.user = request.user -# research_project.pf_no = pf -# research_project.pi = request.POST.get('pi') -# research_project.co_pi = request.POST.get('co_pi') -# research_project.title = request.POST.get('title') -# research_project.financial_outlay = request.POST.get('financial_outlay') -# research_project.funding_agency = request.POST.get('funding_agency') -# research_project.status = request.POST.get('status') -# x = request.POST.get('start') -# if x[:5] == "Sept." : -# x = "Sep." + x[5:] -# if (request.POST.get('start') != None and request.POST.get('start') != '' and request.POST.get('start') != 'None'): -# try: -# research_project.start_date = datetime.datetime.strptime(x, "%B %d, %Y") -# except: -# research_project.start_date = datetime.datetime.strptime(x, "%b. %d, %Y") -# x = request.POST.get('end') -# if x[:5] == "Sept." : -# x = "Sep." + x[5:] -# if (request.POST.get('end') != None and request.POST.get('end') != '' and request.POST.get('end') != 'None'): -# try: -# research_project.finish_date = datetime.datetime.strptime(x, "%B %d, %Y") -# except: -# research_project.finish_date = datetime.datetime.strptime(x, "%b. %d, %Y") -# x = request.POST.get('sub') -# if x[:5] == "Sept." : -# x = "Sep." + x[5:] -# if (request.POST.get('sub') != None and request.POST.get('sub') != '' and request.POST.get('sub') != 'None'): -# try: -# research_project.date_submission = datetime.datetime.strptime(x, "%B %d, %Y") -# except: -# research_project.date_submission = datetime.datetime.strptime(x, "%b. %d, %Y") -# research_project.save() -# messages.success(request, 'Successfully created research project') -# return redirect(reverse("research_procedures:patent_registration")) - -# @login_required -# def consult_insert(request): -# user = get_object_or_404(ExtraInfo, user=request.user) -# pf = user.id -# consultancy_project = ConsultancyProject() -# consultancy_project.user = request.user -# consultancy_project.pf_no = pf -# consultancy_project.consultants = request.POST.get('consultants') -# consultancy_project.client = request.POST.get('client') -# consultancy_project.title = request.POST.get('title') -# consultancy_project.financial_outlay = request.POST.get('financial_outlay') -# x = request.POST.get('start') -# if x[:5] == "Sept." : -# x = "Sep." + x[5:] -# if (request.POST.get('start') != None and request.POST.get('start') != '' and request.POST.get('start') != 'None'): -# try: -# consultancy_project.start_date = datetime.datetime.strptime(x, "%B %d, %Y") -# except: -# consultancy_project.start_date = datetime.datetime.strptime(x, "%b. %d, %Y") -# x = request.POST.get('end') -# if x[:5] == "Sept." : -# x = "Sep." + x[5:] -# if (request.POST.get('end') != None and request.POST.get('end') != '' and request.POST.get('end') != 'None'): -# try: -# consultancy_project.end_date = datetime.datetime.strptime(x, "%B %d, %Y") -# except: -# consultancy_project.end_date = datetime.datetime.strptime(x, "%b. %d, %Y") -# consultancy_project.save() -# messages.success(request,"Successfully created consultancy project") -# return redirect(reverse("research_procedures:patent_registration")) - -def add_projects(request): - if request.method== "POST": - obj= request.POST - projectname= obj.get('project_name') - projecttype= obj.get('project_type') - fo= obj.get('financial_outlay') - pid= obj.get('project_investigator_id') - copid=obj.get('co_project_investigator_id') - sa= obj.get('sponsored_agency') - startd= obj.get('start_date') - subd= obj.get('finish_date') - finishd= obj.get('finish_date') - years= obj.get('number_of_years') - # project_description= obj.get('description') - project_info_file= request.FILES.get('project_info_file') - - check = User.objects.filter(username=pid) - # print(check[0].username) - - - - check= HoldsDesignation.objects.filter(user__username=pid , designation__name= "Professor") - if not check.exists(): - check= HoldsDesignation.objects.filter(user__username=pid , designation__name= "Assistant Professor") - - if not check.exists(): - messages.error(request,"Request not added, no such project investigator exists 2") - return render(request,"rs/projects.html") - - - check= HoldsDesignation.objects.filter(user__username=copid , designation__name= "Professor") - if not check.exists(): - check= HoldsDesignation.objects.filter(user__username=copid , designation__name= "Assistant Professor") - - if not check.exists(): - messages.error(request,"Request not added, no such project investigator exists 2") - return render(request,"rs/projects.html") - - - obj= projects.objects.all() - if len(obj)==0 : - projectid=1 - - else : - projectid= obj[0].project_id+1 - - userpi_instance = User.objects.get(username=pid) - usercpi_instance = User.objects.get(username=copid) - - projects.objects.create( - project_id=projectid, - project_name=projectname, - project_type=projecttype, - status=0, - project_investigator_id=userpi_instance, - co_project_investigator_id=usercpi_instance, - sponsored_agency=sa, - start_date=startd, - submission_date=finishd, - finish_date=finishd, - years=years, - project_info_file=project_info_file - - ) - project_investigator_designation = HoldsDesignation.objects.get(user=userpi_instance).designation - - file_x= create_file( - uploader=request.user.username, - uploader_designation="rspc_admin", - receiver= pid, - receiver_designation=project_investigator_designation, - src_module="research_procedures", - src_object_id= projectid, - file_extra_JSON= { "message": "Project added successfully"}, - attached_file= project_info_file, - ) - - messages.success(request,"Project added successfully") - categories = category.objects.all() - - data = { - "pid": pid, - "years": list(range(1, int(years) + 1)), - "categories": categories, - } - - return redirect("/research_procedures/financial_outlay/"+str(projectid)) - return render(request,"rs/projects.html") - -def add_fund_requests(request,pj_id): - data= { - "pj_id": pj_id - } - return render(request,"rs/add_fund_requests.html",context=data) - -def add_staff_requests(request,pj_id): - data= { - "pj_id": pj_id - } - return render(request,"rs/add_staff_requests.html",context=data) - -def add_projects(request): - - # designation = getDesignation(request.user.username) - # print("designation is " + designation) - # if designation != 'rspc_admin': - # messages.error(request, 'Only RSPC Admin can add projects') - # return redirect("/research_procedures") - - if request.method== "POST": - obj= request.POST - projectname= obj.get('project_name') - projecttype= obj.get('project_type') - fo= obj.get('financial_outlay') - pid= obj.get('project_investigator_id') - copid=obj.get('co_project_investigator_id') - sa= obj.get('sponsored_agency') - startd= obj.get('start_date') - subd= obj.get('finish_date') - finishd= obj.get('finish_date') - years= obj.get('number_of_years') - # project_description= obj.get('description') - project_info_file= request.FILES.get('project_info_file') - - check = User.objects.filter(username=pid) - - # print(check[0].username) - - - - - check= get_obj_by_username_and_designation(pid, "Professor") #checking for pid to exist - - if not check.exists(): - check= HoldsDesignation.objects.filter(user__username=pid , designation__name= "Assistant Professor") - - if not check.exists(): - messages.error(request,"Request not added, no such project investigator exists ") - return render(request,"rs/projects.html") - - - - check= get_obj_by_username_and_designation(copid, "Professor") #checking for copid to exist - - if not check.exists(): - check= HoldsDesignation.objects.filter(user__username=copid , designation__name= "Assistant Professor") - - if not check.exists(): - messages.error(request,"Request not added, no such co project investigator exists ") - return render(request,"rs/projects.html") - - - obj= projects.objects.all() - - - if len(obj)==0 : - projectid=1 - - else : - projectid= obj[0].project_id+1 - - for project in obj: - if project.project_name==projectname: - messages.error(request,"Request not added, project name already exists") - return render(request,"rs/projects.html") - - - - - - userpi_instance = User.objects.get(username=pid) - usercpi_instance = User.objects.get(username=copid) - - projects.objects.create( - project_id=projectid, - project_name=projectname, - project_type=projecttype, - status=0, - project_investigator_id=userpi_instance, - co_project_investigator_id=usercpi_instance, - sponsored_agency=sa, - start_date=startd, - submission_date=finishd, - finish_date=finishd, - years=years, - project_info_file=project_info_file - - ) - project_investigator_designation = HoldsDesignation.objects.get(user=userpi_instance).designation - - file_x= create_file( - uploader=request.user.username, - uploader_designation="rspc_admin", - receiver= pid, - receiver_designation=project_investigator_designation, - src_module="research_procedures", - src_object_id= projectid, - file_extra_JSON= { "message": "Project added successfully"}, - attached_file= project_info_file, - ) - - messages.success(request,"Project added successfully") - categories = category.objects.all() - - data = { - "pid": pid, - "years": list(range(1, int(years) + 1)), - "categories": categories, - } - - return redirect("/research_procedures/financial_outlay/"+str(projectid)) - return render(request,"rs/projects.html") -@api_view(['GET']) -def view_projects(request): - queryset= projects.objects.all() - print('---------------------------------------------------------------') - rspc_admin = HoldsDesignation.objects.get(designation__name="rspc_admin") - rspc_admin =rspc_admin.user.username - data = Project_serializer(queryset, many=True).data - if request.user.username == rspc_admin: - data= { - "projects": data, - "username": request.user.username, - } - return JsonResponse(data,safe=False) - - queryset= projects.objects.filter(project_investigator_id__username= request.user.username) - data2 = Project_serializer(queryset, many=True).data - data= { - "projects": data2, - "username": request.user.username, - } - # print(data) - # print(request.user.username) - - return JsonResponse(data,safe=False) -def view_project_info(request,id): - id= int(id) - obj= projects.objects.filter(project_id=id) - data = Project_serializer(obj, many=True).data - - - # data = { - # "project": obj, - # } - data = { - "project": data, - } - - return JsonResponse(data , safe=False) - -# def view_requests(request,id): - -# if id== '1': -# queryset= requests.objects.filter(request_type= "staff") -# elif id== '0': -# rspc_admin = HoldsDesignation.objects.get(designation__name="rspc_admin") -# rspc_admin =rspc_admin.user.username -# if request.user.username == rspc_admin : -# queryset= rspc_inventory.objects.all() -# data= { -# "requests": queryset, -# "username": request.user.username -# } -# return render(request,"rs/view_requests.html", context= data) - - -# queryset= rspc_inventory.objects.filter(project_investigator_id = request.user.username ) -# else: -# render(request,"/404.html") - -# data= { -# "requests": queryset, -# "username": request.user.username, -# "id":id, -# } - -# # print(data) -# # print(request.user.username) - -# return render(request,"rs/view_requests.html", context= data) - -def view_financial_outlay(request,pid): - - table_data=financial_outlay.objects.filter(project_id=pid).order_by('category', 'sub_category') - project= projects.objects.get(project_id=pid) - - years = set(table_data.values_list('year', flat=True)) - - category_data = {} - for category in table_data.values_list('category', flat=True).distinct(): - category_data[category] = financial_outlay_serializer(table_data.filter(category=category) , many=True).data - # category_data = category_serializer(category_data , many=True).data - - - data = { - 'table_title': 'Total Budget Outlay', - 'table_caption': '...', # Add caption if needed - 'project_name':project.project_name, - 'years': list(years), - 'category_data': category_data, - } - - # print(data) - return JsonResponse(data , safe=False) - -# def submit_closure_report(request,id): -# id= int(id) -# obj= projects.objects.get(project_id=id) -# obj.status= 1; -# obj.save() - -# queryset= projects.objects.filter(project_investigator_id = request.user.username) - -# # print(queryset) - -# data= { -# "projects": queryset, -# "username": request.user.username -# } -# messages.success(request,"Closure report submitted successfully") -# return render(request,"rs/view_projects_rspc.html",context=data) -@api_view(['GET']) -def view_project_inventory(request,pj_id): - pj_id=int(pj_id) - queryset= (requests.objects.filter(project_id=pj_id,request_type="funds")) - queryset = requests_serializer(queryset , many=True).data - - # print(queryset) - - data= { - "requests": queryset, - "username": request.user.username - } - return JsonResponse(data , safe=True) - -def view_project_staff(request,pj_id): - pj_id=int(pj_id) - queryset= requests.objects.filter(project_id=pj_id,request_type="staff") - queryset = requests_serializer(queryset , many = True).data - - - # print(queryset) - - data= { - "requests": queryset, - "username": request.user.username - } - return JsonResponse(data , safe = True) - -# def projectss(request): -# return render(request,"rs/projects.html") - -# def view_project_info(request,id): -# id= int(id) -# obj= projects.objects.get(project_id=id) - - - -# data = { -# "project": obj, -# } - -# return render(request,"rs/view_project_info.html", context= data) - -# def financial_outlay_form(request,pid): -# pid= int(pid) -# project= projects.objects.get(project_id=pid); -# categories = category.objects.all().distinct(); - -# categories_with_subcategories = category.objects.values('category_name', 'sub_category_name') - -# # Organize the data into a dictionary -# category_subcategory_map = {} -# for item in categories_with_subcategories: -# category_name = item['category_name'] -# subcategory = item['sub_category_name'] -# if category_name in category_subcategory_map: -# category_subcategory_map[category_name].append(subcategory) -# else: -# category_subcategory_map[category_name] = [subcategory] - -# # Pass the organized data to the template - -# data = { -# "project_id": project.project_id, -# "project_name":project.project_name, -# "years": list(range(1, int(project.years) + 1)), -# "category_subcategory_map": category_subcategory_map - -# } - -# return render(request,"rs/add_financial_outlay.html", context= data) -# # return render(request,"rs/add_financial_outlay.html", context= data) - - - -# def add_staff_details(request, pid): -# if request.method == 'POST': -# obj = request.POST -# for key, value in obj.items(): -# if key.startswith('staff_id'): -# year_count = key.split('-')[-2] -# staff_count = key.split('-')[-1] -# staff_id_key = f'staff_id-{year_count}-{staff_count}' -# staff_name_key = f'staff_name-{year_count}-{staff_count}' -# qualification_key = f'qualification-{year_count}-{staff_count}' -# stipend_key = f'stipend-{year_count}-{staff_count}' -# year = year_count -# staff_id = obj.get(staff_id_key, '').strip() -# staff_name = obj.get(staff_name_key, '').strip() -# qualification = obj.get(qualification_key, '').strip() -# stipend = obj.get(stipend_key, '').strip() -# project_instance = projects.objects.get(project_id=pid) -# # print(type(staff_id)) -# ob = staff_allocations.objects.all() - -# if len(ob) == 0: -# fid = 1 -# else: -# fid = ob[0].staff_allocation_id + 1 - -# staff_id_instance = User.objects.get(username=staff_id) - - -# staff_allocations.objects.create( -# staff_allocation_id=fid, -# project_id=project_instance, -# staff_id=staff_id_instance, -# staff_name=staff_name, -# qualification=qualification, -# year=year, -# stipend=stipend -# ) - -# return redirect("/research_procedures/view_staff_details/"+str(pid)) - -# project = projects.objects.get(project_id=pid) - -# years_passed = int((datetime.datetime.now().date() - project.start_date).days / 365.25) - -# data = { -# "project_id": project.project_id, -# "project_name" : project.project_name, -# "years": list(range(1, int(project.years) + 1)), -# "year": int(years_passed) + 1, -# } - -# return render(request, "rs/add_staff_details.html", context=data) - -@api_view(['GET']) -def view_staff_details(request, pid): - staff_records = staff_allocations.objects.filter(project_id=pid) - data_by_year = {} - project = projects.objects.get(project_id=pid) - # project = Project_serializer(project , many=True).data - - for record in staff_records: - year = record.year - if year not in data_by_year: - data_by_year[year] = [] - data_by_year[year].append({ - 'staff_id': int(record.staff_id.id), - 'staff_name': record.staff_name, - 'qualification': record.qualification, - 'stipend': record.stipend - }) - - context = { - 'data_by_year': data_by_year, - 'project_name': project.project_name - # Add other necessary fields from project here - } - - rspc_admin = HoldsDesignation.objects.get(designation__name="rspc_admin") - - return JsonResponse(context, safe=True) - - -# def add_financial_outlay(request,pid): -# if request.method == 'POST': - -# project = projects.objects.get(project_id=pid) -# project.financial_outlay_status = 1 -# project.save() - -# obj = request.POST -# for key, value in obj.items(): -# if key.startswith('category-select'): -# year_count = key.split('-')[-2] -# category_count = key.split('-')[-1] -# subcategory_key = f'subcategory-select-{year_count}-{category_count}' -# amount_key = f'amount-{year_count}-{category_count}' - -# category = value -# subcategory = obj.get(subcategory_key, ['']) -# amount = obj.get(amount_key, ['']) -# year = int(year_count) - -# # print(year) -# # print(amount) -# # print(subcategory) -# # print(category) -# project_instance=projects.objects.get(project_id=pid) - - -# ob= financial_outlay.objects.all() -# if len(ob)==0 : -# fid=1 - -# else : -# fid= ob[0].financial_outlay_id+1 -# financial_outlay.objects.create( -# financial_outlay_id=fid, -# project_id=project_instance, -# category=category, -# sub_category=subcategory, -# amount=amount, -# year=year, -# status=0, -# staff_limit=0 -# ) - - -# return redirect("/research_procedures/view_financial_outlay/"+str(pid)) - -# def inbox(request): - - -# user_designation= getDesignation(request.user.username) -# print(user_designation) -# data = view_inbox(request.user.username,user_designation, "research_procedures") -# files= [] -# count =0 -# for i in data: -# count+=1 -# file1= File.objects.get(id=i['id']) -# files.append((count, file1)) - - -# data={ - -# "inbox": data, -# "files": files -# } -# # print(data) -# return render(request, "rs/inbox.html",context= data) - -# def add_staff_request(request,id): -# if request.method == 'POST': -# obj= request.POST -# projectid = int(id) -# receiver = obj.get('receiver') - - -# sender = request.user.username -# file_to_forward= request.FILES.get('file_to_forward') -# project_instance=projects.objects.get(project_id=projectid) -# receiver_instance=User.objects.get(username=receiver) -# sender_designation= HoldsDesignation.objects.get(user= request.user).designation -# receiver_designation = HoldsDesignation.objects.get(user= receiver_instance).designation - -# file_x= create_file( -# uploader=sender, -# uploader_designation=sender_designation, -# receiver= receiver_instance.username, -# receiver_designation=receiver_designation, -# src_module="research_procedures", -# src_object_id= projectid, -# file_extra_JSON= { "message": "Staff request added ("+ str(projectid)+ ")"}, -# attached_file= file_to_forward, -# ) -# messages.success(request,"Staff request added successfully") - -# return redirect("/research_procedures/view_project_info/"+ str(projectid)) - -# def view_request_inbox(request): -# user_designation= getDesignation(request.user.username) -# print(user_designation) -# data = view_inbox(request.user.username,user_designation, "research_procedures") -# files= [] -# count =0 -# for i in data: -# count+=1 -# file1= File.objects.get(id=i['id']) -# files.append((count, file1)) - - -# data={ - -# "inbox": data, -# "files": files -# } -# # print(data) -# # return render(request, "rs/view_request_inbox.html",context= data) -# return Response(data, status=status.HTTP_200_OK) - - -# def forward_request(request): -# if request.method == 'POST': -# obj= request.POST -# fileid = int(obj.get('file_id')) -# receiver = obj.get('receiver') -# message= obj.get('message') -# receiver_instance= User.objects.get(username=receiver) -# receiver_designation= HoldsDesignation.objects.get(user=receiver_instance).designation -# sender = request.user.username - -# filex= get_file_by_id(fileid) - -# file2=create_file( -# uploader=sender, -# uploader_designation= getDesignation(sender), -# receiver= receiver, -# receiver_designation=receiver_designation, -# src_module="research_procedures", -# src_object_id= filex.src_object_id, -# file_extra_JSON= { "message": message}, -# attached_file= filex.upload_file, -# ) - -# delete_file(fileid) -# messages.success(request,"Request forwarded successfully") -# return redirect("/research_procedures/view_request_inbox") - - -# return redirect("/research_procedures/view_request_inbox") - -# def getDesignation(us): -# user_inst = User.objects.get(username= us) -# user_designation= HoldsDesignation.objects.get(user= user_inst).designation -# return user_designation - -# def get_file_by_id(id): -# file1= File.objects.get(id=id) -# print(file1) -# return file1 - -# def delete_file(id): -# file1= File.objects.get(id=id) -# tracking= Tracking.objects.get(file_id=file1) -# tracking.delete() -# file1.delete() -# return - - - - +class PatentViewSet(ModelViewSet): + queryset = Patent.objects.all() + serializer_class = PatentSerializer + permission_classes = [ IsAuthenticatedOrReadOnly ] \ No newline at end of file diff --git a/FusionIIIT/applications/research_procedures/forms.py b/FusionIIIT/applications/research_procedures/forms.py index b291b2dae..f12601a38 100644 --- a/FusionIIIT/applications/research_procedures/forms.py +++ b/FusionIIIT/applications/research_procedures/forms.py @@ -1,40 +1,40 @@ from django import forms -from .models import * +from .models import ResearchGroup from django.contrib.auth.models import User -# class ResearchGroupForm(forms.ModelForm): +class ResearchGroupForm(forms.ModelForm): -# students = forms.ModelMultipleChoiceField(queryset=User.objects.all(), widget=forms.SelectMultiple(attrs={'class':'ui fluid search dropdown'})) -# faculty = forms.ModelMultipleChoiceField(queryset=User.objects.all(), widget=forms.SelectMultiple(attrs={'class':'ui fluid search dropdown'})) + students = forms.ModelMultipleChoiceField(queryset=User.objects.all(), widget=forms.SelectMultiple(attrs={'class':'ui fluid search dropdown'})) + faculty = forms.ModelMultipleChoiceField(queryset=User.objects.all(), widget=forms.SelectMultiple(attrs={'class':'ui fluid search dropdown'})) -# def __init__(self, *args, **kwargs): -# if kwargs.get('instance'): -# initial = kwargs.setdefault('initial', {}) -# initial['students_under_group'] = [t.pk for t in kwargs['instance'].students_under_group.all()] -# initial['faculty_under_group'] = [t.pk for t in kwargs['instance'].faculty_under_group.all()] + def __init__(self, *args, **kwargs): + if kwargs.get('instance'): + initial = kwargs.setdefault('initial', {}) + initial['students_under_group'] = [t.pk for t in kwargs['instance'].students_under_group.all()] + initial['faculty_under_group'] = [t.pk for t in kwargs['instance'].faculty_under_group.all()] -# forms.ModelForm.__init__(self, *args, **kwargs) + forms.ModelForm.__init__(self, *args, **kwargs) -# def save(self, commit=True): -# instance = forms.ModelForm.save(self, False) + def save(self, commit=True): + instance = forms.ModelForm.save(self, False) -# old_save_m2m = self.save_m2m -# def save_m2m(): -# old_save_m2m() -# instance.students_under_group.clear() -# instance.students_under_group.add(*self.cleaned_data['students']) -# instance.faculty_under_group.clear() -# instance.faculty_under_group.add(*self.cleaned_data['faculty']) -# self.save_m2m = save_m2m + old_save_m2m = self.save_m2m + def save_m2m(): + old_save_m2m() + instance.students_under_group.clear() + instance.students_under_group.add(*self.cleaned_data['students']) + instance.faculty_under_group.clear() + instance.faculty_under_group.add(*self.cleaned_data['faculty']) + self.save_m2m = save_m2m -# if commit: -# instance.save() -# self.save_m2m() + if commit: + instance.save() + self.save_m2m() -# return instance + return instance -# class Meta: -# model = ResearchGroup -# fields = ('name','students','faculty','description',) \ No newline at end of file + class Meta: + model = ResearchGroup + fields = ('name','students','faculty','description',) \ No newline at end of file diff --git a/FusionIIIT/applications/research_procedures/migrations/0001_initial.py b/FusionIIIT/applications/research_procedures/migrations/0001_initial.py index 592c0fd36..ec4b77a48 100644 --- a/FusionIIIT/applications/research_procedures/migrations/0001_initial.py +++ b/FusionIIIT/applications/research_procedures/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 3.1.5 on 2024-07-16 15:44 +# Generated by Django 3.1.5 on 2023-03-15 18:53 import datetime from django.conf import settings @@ -12,109 +12,80 @@ class Migration(migrations.Migration): dependencies = [ migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('globals', '0001_initial'), ] operations = [ migrations.CreateModel( - name='category', + name='TechTransfer', fields=[ - ('category_id', models.IntegerField(primary_key=True, serialize=False)), - ('category_name', models.CharField(max_length=500)), - ('sub_category_name', models.CharField(max_length=500)), - ], - options={ - 'ordering': ['-category_id'], - }, - ), - migrations.CreateModel( - name='projects', - fields=[ - ('project_id', models.IntegerField(primary_key=True, serialize=False)), - ('project_name', models.CharField(max_length=600)), - ('project_type', models.CharField(max_length=500)), - ('sponsored_agency', models.CharField(max_length=500)), - ('start_date', models.DateField()), - ('submission_date', models.DateField()), - ('finish_date', models.DateField()), - ('years', models.IntegerField()), - ('status', models.IntegerField(default=0)), - ('project_info_file', models.FileField(blank=True, null=True, upload_to='')), - ('financial_outlay_status', models.IntegerField(default=0)), - ('co_project_investigator_id', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='copi_id', to=settings.AUTH_USER_MODEL)), - ('project_investigator_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='pi_id', to=settings.AUTH_USER_MODEL)), - ], - options={ - 'ordering': ['-project_id'], - }, - ), - migrations.CreateModel( - name='staff_allocations', - fields=[ - ('staff_allocation_id', models.IntegerField(primary_key=True, serialize=False)), - ('staff_name', models.CharField(max_length=500)), - ('qualification', models.CharField(max_length=500)), - ('year', models.IntegerField()), - ('stipend', models.IntegerField()), - ('staff_type', models.CharField(default='research', max_length=100)), - ('start_date', models.DateField(default=datetime.date(2024, 7, 16))), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('pf_no', models.IntegerField()), + ('details', models.CharField(default=' ', max_length=500)), + ('date_entry', models.DateField(blank=True, default=datetime.datetime.now, null=True)), + ('start_date', models.DateField(blank=True, null=True)), ('end_date', models.DateField(blank=True, null=True)), - ('project_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='research_procedures.projects')), - ('staff_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), ], - options={ - 'ordering': ['-staff_allocation_id'], - }, ), migrations.CreateModel( - name='requests', + name='ResearchProject', fields=[ - ('request_id', models.IntegerField(primary_key=True, serialize=False)), - ('request_type', models.CharField(max_length=500)), - ('approval_status', models.IntegerField(default=0)), - ('project_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='research_procedures.projects')), - ('project_investigator_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='rj_pi', to=settings.AUTH_USER_MODEL)), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('pf_no', models.IntegerField()), + ('ptype', models.CharField(default='Research', max_length=100)), + ('pi', models.CharField(default=' ', max_length=1000)), + ('co_pi', models.CharField(default=' ', max_length=1500)), + ('title', models.TextField(default=' ', max_length=5000)), + ('funding_agency', models.CharField(default=' ', max_length=250, null=True)), + ('financial_outlay', models.CharField(default=' ', max_length=150, null=True)), + ('status', models.CharField(choices=[('Awarded', 'Awarded'), ('Submitted', 'Submitted'), ('Ongoing', 'Ongoing'), ('Completed', 'Completed')], max_length=10)), + ('start_date', models.DateField(blank=True, null=True)), + ('finish_date', models.DateField(blank=True, null=True)), + ('date_submission', models.DateField(blank=True, null=True)), + ('date_entry', models.DateField(blank=True, default=datetime.datetime.now, null=True)), + ('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), ], - options={ - 'ordering': ['-request_id'], - }, ), migrations.CreateModel( - name='financial_outlay', + name='ResearchGroup', fields=[ - ('financial_outlay_id', models.IntegerField(primary_key=True, serialize=False)), - ('category', models.CharField(max_length=500)), - ('sub_category', models.CharField(max_length=500)), - ('amount', models.IntegerField()), - ('year', models.IntegerField()), - ('status', models.IntegerField(default=0)), - ('staff_limit', models.IntegerField(default=0)), - ('utilized_amount', models.IntegerField(default=0, null=True)), - ('project_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='research_procedures.projects')), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=120)), + ('description', models.TextField()), + ('faculty_under_group', models.ManyToManyField(related_name='allfaculty', to=settings.AUTH_USER_MODEL)), + ('students_under_group', models.ManyToManyField(related_name='allstudents', to=settings.AUTH_USER_MODEL)), ], - options={ - 'ordering': ['-financial_outlay_id'], - }, ), migrations.CreateModel( - name='co_project_investigator', + name='Patent', fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('co_pi_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), - ('project_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='research_procedures.projects')), + ('application_id', models.AutoField(primary_key=True, serialize=False)), + ('title', models.CharField(max_length=120)), + ('ipd_form', models.FileField(blank=True, null=True, upload_to='')), + ('project_details', models.FileField(blank=True, null=True, upload_to='')), + ('ipd_form_file', models.TextField(blank=True, null=True)), + ('project_details_file', models.TextField(blank=True, null=True)), + ('status', models.CharField(choices=[('Approved', 'Approved'), ('Disapproved', 'Disapproved'), ('Pending', 'Pending')], default='Pending', max_length=20)), + ('faculty_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='globals.extrainfo')), ], - options={ - 'ordering': ['-co_pi_id'], - }, ), migrations.CreateModel( - name='co_pis', + name='ConsultancyProject', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('co_pi', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), - ('project_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='research_procedures.projects')), + ('pf_no', models.IntegerField()), + ('consultants', models.CharField(max_length=150)), + ('title', models.CharField(max_length=1000)), + ('client', models.CharField(max_length=1000)), + ('financial_outlay', models.IntegerField()), + ('start_date', models.DateField(blank=True, null=True)), + ('end_date', models.DateField(blank=True, null=True)), + ('duration', models.CharField(blank=True, max_length=500, null=True)), + ('date_entry', models.DateField(blank=True, default=datetime.datetime.now, null=True)), + ('status', models.CharField(blank=True, choices=[('Completed', 'Completed'), ('Submitted', 'Submitted'), ('Ongoing', 'Ongoing')], default='Ongoing', max_length=10, null=True)), + ('remarks', models.CharField(blank=True, max_length=1000, null=True)), + ('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), ], - options={ - 'ordering': ['-project_id'], - }, ), ] diff --git a/FusionIIIT/applications/research_procedures/migrations/0002_auto_20241020_1126.py b/FusionIIIT/applications/research_procedures/migrations/0002_auto_20241020_1126.py deleted file mode 100644 index e94863c75..000000000 --- a/FusionIIIT/applications/research_procedures/migrations/0002_auto_20241020_1126.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 3.1.5 on 2024-10-20 11:26 - -import datetime -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('research_procedures', '0001_initial'), - ] - - operations = [ - migrations.AlterField( - model_name='staff_allocations', - name='start_date', - field=models.DateField(default=datetime.date.today), - ), - ] diff --git a/FusionIIIT/applications/research_procedures/models.py b/FusionIIIT/applications/research_procedures/models.py index 3a653e374..559b56fd9 100644 --- a/FusionIIIT/applications/research_procedures/models.py +++ b/FusionIIIT/applications/research_procedures/models.py @@ -1,6 +1,6 @@ from django.db import models from django.db import models -from applications.globals.models import * +from applications.globals.models import ExtraInfo from django.contrib.auth.models import User import datetime @@ -11,117 +11,96 @@ class Constants: ('Pending' , 'Pending') ) +class Patent(models.Model): + """ + Holds Patents filed by faculty. + + @fields: + faculty_id - Extra information of the faculty who filed the patent. + title - Title of the patent + ipd_form - IPD form of the patent + ipd_form_file - Contains the url of the ipd_form pdf uploaded by the faculty + project_details - Project details of the patent + project_details_file - Contains the url of the project_details pdf uploaded by the faculty + status - Status of the patent + """ + + application_id = models.AutoField(primary_key=True) + faculty_id = models.ForeignKey(ExtraInfo, on_delete=models.CASCADE) + title = models.CharField(max_length=120) + ipd_form = models.FileField(null = True, blank= True) + project_details = models.FileField(null = True, blank= True) + ipd_form_file = models.TextField(null=True, blank=True) + project_details_file = models.TextField(null=True, blank=True) + status = models.CharField(choices=Constants.RESPONSE_TYPE, max_length=20, default='Pending') + + def _str_(self): + return str(self.title) + + +class ResearchGroup(models.Model): + name = models.CharField(max_length=120) + faculty_under_group = models.ManyToManyField(User,related_name="allfaculty") + students_under_group = models.ManyToManyField(User,related_name="allstudents") + description = models.TextField() + + def _str_(self): + return str(self.name) + + +class ResearchProject(models.Model): + user = models.ForeignKey(User, on_delete=models.CASCADE, blank=True,null=True) + pf_no = models.IntegerField() + ptype = models.CharField(max_length=100, default="Research") + pi = models.CharField(max_length=1000, default=" ") + co_pi = models.CharField(max_length=1500, default=" ") + title = models.TextField(max_length=5000, default=" ") + funding_agency = models.CharField(max_length=250, default=" ", null=True) + financial_outlay = models.CharField(max_length=150, default=" ", null=True) + STATUS_TYPE_CHOICES = ( + ('Awarded', 'Awarded'), + ('Submitted', 'Submitted'), + ('Ongoing', 'Ongoing'), + ('Completed', 'Completed') + ) + status = models.CharField(max_length = 10, choices = STATUS_TYPE_CHOICES) + start_date = models.DateField(null=True, blank=True) + finish_date = models.DateField(null=True, blank=True) + date_submission = models.DateField(null=True, blank=True) + date_entry = models.DateField(null=True, blank=True, default=datetime.datetime.now) -class projects(models.Model): - project_id= models.IntegerField(primary_key= True) - project_name= models.CharField(max_length=600) - project_type= models.CharField(max_length=500) - # financial_outlay= models.IntegerField() - project_investigator_id=models.ForeignKey(User,related_name='pi_id', on_delete=models.CASCADE) - # rspc_admin_id=models.CharField(max_length=500) - co_project_investigator_id=models.ForeignKey(User,related_name='copi_id' ,on_delete=models.CASCADE, null=True) - sponsored_agency= models.CharField(max_length=500) - start_date=models.DateField() - submission_date=models.DateField() - finish_date=models.DateField() - years= models.IntegerField() - status= models.IntegerField(default=0) - project_info_file=models.FileField( null=True, blank=True) - # project_description=models.CharField(max_length=500 ,default="description") - financial_outlay_status=models.IntegerField(default=0) - - - def __str__(self): - return str(self.project_id) - - class Meta: - ordering = ['-project_id'] - -class financial_outlay(models.Model): - financial_outlay_id= models.IntegerField(primary_key=True) - project_id= models.ForeignKey(projects, on_delete=models.CASCADE) - category=models.CharField(max_length=500) - sub_category=models.CharField(max_length=500) - amount=models.IntegerField() - year=models.IntegerField() - status= models.IntegerField(default=0) - staff_limit=models.IntegerField(default=0) - utilized_amount=models.IntegerField(default=0,null= True) - - def __str__(self): - return str(self.financial_outlay_id) - - class Meta: - ordering = ['-financial_outlay_id'] - - -class category(models.Model): - category_id= models.IntegerField(primary_key=True) - category_name= models.CharField(max_length=500) - sub_category_name= models.CharField(max_length=500) - - - def __str__(self): - return str(self.category_id) - - class Meta: - ordering = ['-category_id'] - - -class staff_allocations(models.Model): - staff_allocation_id=models.IntegerField(primary_key=True) - project_id= models.ForeignKey(projects, on_delete=models.CASCADE) - staff_id=models.ForeignKey(User, on_delete=models.CASCADE) - staff_name=models.CharField(max_length=500) - qualification=models.CharField(max_length=500) - year=models.IntegerField() - stipend=models.IntegerField() - staff_type=models.CharField(max_length=100,default="research") - start_date=models.DateField(default=datetime.date.today) - end_date=models.DateField(null=True, blank=True) - def __str__(self): - return str(self.staff_allocation_id) - - - - class Meta: - ordering = ['-staff_allocation_id'] - -class co_pis(models.Model): - co_pi= models.ForeignKey(User, on_delete=models.CASCADE) - project_id= models.ForeignKey(projects, on_delete=models.CASCADE) - - class Meta: - ordering = ['-project_id'] - + return 'PF No.: {} pi: {} title: {}'.format(self.pf_no,self.pi, self.title) + +class ConsultancyProject(models.Model): + user = models.ForeignKey(User, on_delete=models.CASCADE, blank=True,null=True) + pf_no = models.IntegerField() + consultants = models.CharField(max_length=150) + title = models.CharField(max_length=1000) + client = models.CharField(max_length=1000) + financial_outlay = models.IntegerField() + start_date = models.DateField(null=True, blank=True) + end_date = models.DateField(null=True, blank=True) + duration = models.CharField(max_length=500, null=True, blank=True) + date_entry = models.DateField(null=True, blank=True, default=datetime.datetime.now) + STATUS_TYPE_CHOICES = ( + ('Completed', 'Completed'), + ('Submitted', 'Submitted'), + ('Ongoing', 'Ongoing') + ) + status = models.CharField(default = 'Ongoing', max_length = 10, choices = STATUS_TYPE_CHOICES, null=True, blank=True) + remarks = models.CharField(max_length=1000, null=True, blank=True) - -class requests(models.Model): - request_id=models.IntegerField(primary_key=True) - project_id= models.ForeignKey(projects, on_delete=models.CASCADE) - request_type=models.CharField(max_length=500) - project_investigator_id=models.ForeignKey(User, related_name='rj_pi' , on_delete= models.CASCADE) - approval_status= models.IntegerField(default=0) #value 0 means pending - def __str__(self): - return str(self.request_id) + return 'PF No.: {} Consultants: {}'.format(self.pf_no, self.consultants) - class Meta: - ordering = ['-request_id'] - -class co_project_investigator(models.Model): - co_pi_id= models.ForeignKey(User, on_delete=models.CASCADE) - project_id= models.ForeignKey(projects, on_delete=models.CASCADE) +class TechTransfer(models.Model): + user = models.ForeignKey(User, on_delete=models.CASCADE, blank=True,null=True) + pf_no = models.IntegerField() + details = models.CharField(max_length=500, default=" ") + date_entry = models.DateField(null=True, blank=True, default=datetime.datetime.now) + start_date = models.DateField(null=True,blank=True) + end_date = models.DateField(null=True,blank=True) def __str__(self): - return str(self.co_pi_id) - - class Meta: - ordering = ['-co_pi_id'] - - - - - - + return 'PF No.: {} Details: {}'.format(self.pf_no, self.details) \ No newline at end of file diff --git a/FusionIIIT/applications/research_procedures/tests.py b/FusionIIIT/applications/research_procedures/tests.py index e6b4dfb93..c6fa3f281 100644 --- a/FusionIIIT/applications/research_procedures/tests.py +++ b/FusionIIIT/applications/research_procedures/tests.py @@ -1,15 +1,4 @@ from django.test import TestCase -from django.shortcuts import render -from django.http import HttpResponse -from django.http import JsonResponse -import json -from applications.research_procedures.models import * -# Create your tests here . -def testfun(request): - # received_json_data=json.loads(request.body) - # print(received_json_data['name']) - data = {} - - return JsonResponse(list(data),safe=False) +# Create your tests here . \ No newline at end of file diff --git a/FusionIIIT/applications/research_procedures/urls.py b/FusionIIIT/applications/research_procedures/urls.py index 02a3d332a..05e653c74 100644 --- a/FusionIIIT/applications/research_procedures/urls.py +++ b/FusionIIIT/applications/research_procedures/urls.py @@ -1,45 +1,16 @@ -from django.urls import include,path +from django.urls import include from django.conf.urls import url from . import views -from . import tests + app_name="research_procedures" urlpatterns = [ - url(r'^$', views.view_projects, name='patent_registration'), + url(r'^$', views.patent_registration, name='patent_registration'), url(r'^update$', views.patent_status_update, name='patent_status_update'), url(r'^research_group$', views.research_group_create, name='research_group_create'), url(r'^project_insert$',views.project_insert,name='project_insert'), url(r'^consult_insert$',views.consult_insert,name='consult_insert'), - url(r'^add_projects$',views.add_projects,name='add_projects'), - url(r'^view_projects$',views.view_projects,name='view_projects'), - # path('add_requests///',views.add_requests,name='add_requests'), + url(r'^transfer_insert$',views.transfer_insert,name='transfer_insert'), url(r'^api/',include('applications.research_procedures.api.urls')), - # path('view_requests//',views.view_requests), - path('view_project_info//',views.view_project_info), - # path('submit_closure_report//',views.submit_closure_report, name="submit_closure_report"), - # path('add_fund_requests//',views.add_fund_requests, name="add_fund_requests"), - # path('add_staff_requests//',views.add_staff_requests, name="add_staff_requests"), - # path('view_project_inventory//',views.view_project_inventory, name="view_project_inventory"), - # path('view_project_staff//',views.view_project_staff, name="view_project_staff"), - path('add_financial_outlay//',views.add_financial_outlay, name="add_financial_outlay"), - path('financial_outlay//',views.financial_outlay_form, name="financial_outlay_form"), - path('view_financial_outlay//',views.view_financial_outlay, name="view_financial_outlay"), - path('add_staff_details//',views.add_staff_details, name="add_staff_details"), - path('view_staff_details//',views.view_staff_details, name="view_staff_details"), - path('add_staff_request//',views.add_staff_request, name="add_staff_request"), - path('inbox',views.inbox, name="inbox"), - path('view_request_inbox',views.view_request_inbox, name="view_request_inbox"), - path('forward_request//',views.forward_request, name="forward_request"), - path('update_financial_outlay//',views.update_financial_outlay,name="update_financial_outlay"), - path('approve_request//',views.approve_request,name="approve_request"), - path('view_file//',views.view_file,name="view_file"), - path('update_time_period//',views.update_time_period,name="update_time_period"), - path('change_year//',views.change_year,name="change_year"), - path('change_end_date//',views.change_end_date,name="change_end_date"), - path('download_project_pdf//', views.download_project_pdf, name='download_project_pdf'), - # path('serve-pdf//', serve_pdf, name='serve_pdf'), - - - url(r'^test/$',tests.testfun,name = 'test'), ] \ No newline at end of file diff --git a/FusionIIIT/applications/research_procedures/views.py b/FusionIIIT/applications/research_procedures/views.py index 838cf0d62..0b17aa3ee 100644 --- a/FusionIIIT/applications/research_procedures/views.py +++ b/FusionIIIT/applications/research_procedures/views.py @@ -1,37 +1,107 @@ from django.shortcuts import redirect, render, get_object_or_404 from django.contrib import messages -from applications.research_procedures.models import * +from applications.research_procedures.models import Patent, ResearchGroup, ResearchProject, ConsultancyProject, TechTransfer from applications.globals.models import ExtraInfo, HoldsDesignation, Designation from django.core.files.storage import FileSystemStorage -from django.core.exceptions import ObjectDoesNotExist from notification.views import research_procedures_notif from django.urls import reverse -from .forms import * +from .forms import ResearchGroupForm from django.contrib.auth.decorators import login_required import datetime -from django.utils import timezone -from .models import * -from collections import defaultdict -from applications.filetracking.sdk.methods import * -from io import BytesIO -from django.http import HttpResponse -from reportlab.lib import colors -from reportlab.lib.pagesizes import letter -from reportlab.platypus import SimpleDocTemplate, Table, TableStyle,Spacer -from .models import projects, financial_outlay # Faculty can file patent and view status of it. - @login_required def patent_registration(request): - - return render(request ,"rs/research.html") + """ + This function is used to register a patent and to retrieve all patents filed by the faculty. + + @param: + request - contains metadata about the requested page. + + @variables: + user - the user who is currently logged in. + extrainfo - extra information of the user. + user_designations - The designations of the user currently logged in. + patent - The new patent to be registered. + patents - All the patents filed by the faculty. + dean_rspc_user - The Dean RSPC user who can modify status of the patent. + ipd_form_pdf - The pdf file of the IPD form of the patent sent by the user. + project_details_pdf - The pdf file of the project details of the patent sent by the user. + file_system - The file system to store the files. + + + """ + + user = request.user + user_extra_info = ExtraInfo.objects.get(user=user) + user_designations = HoldsDesignation.objects.filter(user=user) + patent = Patent() + context = {} + + context['patents'] = Patent.objects.all() + context['user_extra_info'] = user_extra_info + context['user_designations'] = user_designations + + + if request.method=='POST': + if ("ipd_form_file" in request.FILES) and ("project_details_file" in request.FILES) and ("title" in request.POST): + if(user_extra_info.user_type == "faculty"): + patent.faculty_id = user_extra_info + patent.title = request.POST.get('title') + ipd_form_pdf = request.FILES['ipd_form_file'] + if(ipd_form_pdf.name.endswith('.pdf')): + patent.ipd_form = request.FILES['ipd_form_file'] + file_system = FileSystemStorage() + ipd_form_pdf_name = file_system.save(ipd_form_pdf.name,ipd_form_pdf) + patent.ipd_form_file = file_system.url(ipd_form_pdf_name) + else: + messages.error(request, 'Please upload pdf file') + return render(request ,"rs/research.html",context) + + project_details_pdf = request.FILES['project_details_file'] + if(project_details_pdf.name.endswith('.pdf')): + patent.project_details=request.FILES['project_details_file'] + file_system = FileSystemStorage() + project_details_pdf_name = file_system.save(project_details_pdf.name,project_details_pdf) + patent.project_details_file = file_system.url(project_details_pdf_name) + messages.success(request, 'Patent filed successfully') + else: + messages.error(request, 'Please upload pdf file') + return render(request ,"rs/research.html",context) + + # creating notifications for user and dean_rspc about the patent + dean_rspc_user = HoldsDesignation.objects.get(designation=Designation.objects.filter(name='dean_rspc').first()).working + research_procedures_notif(request.user,request.user,"submitted") + research_procedures_notif(request.user,dean_rspc_user,"created") + patent.status='Pending' + patent.save() + else: + messages.error(request, 'Only Faculty can file patent') + else: + messages.error(request,"All fields are required") + patents = Patent.objects.all() + context['patents'] = patents + context['research_groups'] = ResearchGroup.objects.all() + context['research_group_form'] = ResearchGroupForm() + return render(request ,"rs/research.html",context) @login_required #dean_rspc can update status of patent. def patent_status_update(request): - + """ + This function is used to update the status of the patent. + @param: + request - contains metadata about the requested page. + @variables: + user - the user who is currently logged in. + extrainfo - extra information of the user. + user_designations - The designations of the user currently logged in. + patent - The patent whose status is to be updated. + patents - All the patents filed by the faculty. + dean_rspc_user - The Dean RSPC user who can modify status of the patent. + + """ user = request.user user_extra_info = ExtraInfo.objects.get(user=user) user_designations = HoldsDesignation.objects.filter(user=user) @@ -52,13 +122,24 @@ def patent_status_update(request): @login_required def research_group_create(request): - + """ + This function is used to create a research group. + @param: + request - contains metadata about the requested page. + @variables: + user - the user who is currently logged in. + extrainfo - extra information of the user. + user_designations - The designations of the user currently logged in. + research_group - The research group to be created. + research_groups - All the research groups. + dean_rspc_user - The Dean RSPC user who can modify status of the patent. + + """ user = request.user user_extra_info = ExtraInfo.objects.get(user=user) if request.method=='POST': if user_extra_info.user_type == "faculty": form = ResearchGroupForm(request.POST) - if form.is_valid(): form.save() messages.success(request, 'Research group created successfully') @@ -139,1002 +220,14 @@ def consult_insert(request): messages.success(request,"Successfully created consultancy project") return redirect(reverse("research_procedures:patent_registration")) -@login_required -def add_projects(request): - - # designation = getDesignation(request.user.username) - # print("designation is " + designation) - # if designation != 'rspc_admin': - # messages.error(request, 'Only RSPC Admin can add projects') - # return redirect("/research_procedures") - if request.session.get('currentDesignationSelected') != 'rspc_admin': - messages.error(request, 'Only RSPC Admin can add projects') - return redirect("/research_procedures") - - if request.method== "POST": - obj= request.POST - - projectname= obj.get('project_name') - projecttype= obj.get('project_type') - fo= obj.get('financial_outlay') - pid= obj.get('project_investigator_id') - copid=obj.get('co_project_investigator_id-1') - sa= obj.get('sponsored_agency') - startd= obj.get('start_date') - subd= obj.get('finish_date') - finishd= obj.get('finish_date') - years= obj.get('number_of_years') - # project_description= obj.get('description') - project_info_file= request.FILES.get('project_info_file') - - - - check = User.objects.filter(username=pid) - - # print(check[0].username) - - check= HoldsDesignation.objects.filter(user__username= pid, designation__name= "Professor") #checking for pid to exist - - if not check.exists(): - check= HoldsDesignation.objects.filter(user__username=pid , designation__name= "Assistant Professor") - - if not check.exists(): - messages.error(request,"Request not added, no such project investigator exists ") - return render(request,"rs/projects.html") - - - - check= HoldsDesignation.objects.filter(user__username= copid, designation__name= "Professor") #checking for copid to exist - - if not check.exists(): - check= HoldsDesignation.objects.filter(user__username=copid , designation__name= "Assistant Professor") - - if not check.exists(): - messages.error(request,"Request not added, no such co project investigator exists ") - return render(request,"rs/projects.html") - - - - copi_list = [] - - for key, value in obj.items(): - if key.startswith('co_project_investigator_id-' ): - if value not in copi_list: - check= HoldsDesignation.objects.filter(user__username= value, designation__name= "Professor") #checking for copid to exist - if not check.exists(): - check= HoldsDesignation.objects.filter(user__username=value , designation__name= "Assistant Professor") - if not check.exists(): - messages.error(request,"Request not added, no such co project investigator exists ") - return render(request,"rs/projects.html") - copi_list.append(value) - - obj= projects.objects.all() - - if len(obj)==0 : - projectid=1 - - else : - projectid= obj[0].project_id+1 - - for project in obj: - if project.project_name==projectname: - messages.error(request,"Request not added, project name already exists") - return render(request,"rs/projects.html") - - - for copi in copi_list: - if copi == pid: - messages.error(request,"Request not added, project investigator and co project investigator cannot be same") - return render(request,"rs/projects.html") - - - - - - - userpi_instance = User.objects.get(username=pid) - usercpi_instance = User.objects.get(username=copid) - - projects.objects.create( - project_id=projectid, - project_name=projectname, - project_type=projecttype, - status=0, - project_investigator_id=userpi_instance, - co_project_investigator_id=usercpi_instance, - sponsored_agency=sa, - start_date=startd, - submission_date=finishd, - finish_date=finishd, - years=years, - project_info_file=project_info_file - - ) - pi_designation= HoldsDesignation.objects.get(user=userpi_instance , designation__name="Professor") - if not pi_designation: - pi_designation= HoldsDesignation.objects.get(user=userpi_instance , designation__name="Assistant Professor") - project_investigator_designation= pi_designation.designation - - - file_x= create_file( - uploader=request.user.username, - uploader_designation="rspc_admin", - receiver= pid, - subject= projectname, - receiver_designation=project_investigator_designation, - src_module="research_procedures", - src_object_id= projectid, - file_extra_JSON= { "message": "Project added successfully"}, - attached_file= project_info_file, - ) - - - for copi in copi_list: - co_pis.objects.create( - co_pi= User.objects.get(username=copi), - project_id= projects.objects.get(project_id=projectid) - ) - research_procedures_notif(request.user, userpi_instance, "Project Added") - - tracking_obj = Tracking.objects.get(file_id__id=file_x) - file_obj= File.objects.get(id=file_x) - - tracking_obj.upload_file= file_obj.upload_file - tracking_obj.remarks= "Project added by RSPC Admin" - tracking_obj.save() - messages.success(request,"Project added successfully") - categories = category.objects.all() - - notifs = request.user.notifications.all() - data = { - 'notifications': notifs, - "pid": pid, - "years": list(range(1, int(years) + 1)), - "categories": categories, - } - - return redirect("/research_procedures/financial_outlay/"+str(projectid)) - return render(request,"rs/projects.html") - -@login_required -def add_fund_requests(request,pj_id): - data= { - "pj_id": pj_id - } - return render(request,"rs/add_fund_requests.html",context=data) - -@login_required -def add_staff_requests(request,pj_id): - data= { - "pj_id": pj_id - } - return render(request,"rs/add_staff_requests.html",context=data) - -@login_required -def add_requests(request,id,pj_id): - if request.method == 'POST': - obj=request.POST - - - if(id=='0') : - projectid = pj_id - reqtype = obj.get('request_type') - stats =0 - desc= obj.get('description') - amt= obj.get('amount') - - check= projects.objects.filter(project_id=projectid) - if not check.exists(): - messages.error(request,"Request not added, no such project exists") - return render(request,"rs/add_fund_requests.html") - - check= projects.objects.filter(project_id= projectid, project_investigator_id__username=pi_id) - if not check.exists(): - messages.error(request,"Request not added, no such project investigator exists") - return render(request,"rs/add_fund_requests.html") - - - pi_id_instance=User.objects.get(username= request.user.username ) - project_instance=projects.objects.get(project_id=projectid) - - obj= requests.objects.all() - if len(obj)==0 : - requestid=1 - - else : - requestid= obj[0].request_id+1 - - requests.objects.create( - request_id=requestid, - project_id=project_instance, - request_type="funds", - project_investigator_id=pi_id_instance, - status=stats, description=desc, amount= amt - ) - rspc_inventory.objects.create( - inventory_id=requestid, - project_id=project_instance, - project_investigator_id=pi_id_instance, - status=stats, - description=desc, amount= amt - ) - messages.success(request,"Request added successfully") - return render(request,"rs/add_fund_requests.html") - - if(id=='1'): - projectid = obj.get('project_id') - pi_id = obj.get('project_investigator_id') - stats = obj.get('status') - desc= obj.get('description') - - obj= requests.objects.all() - if len(obj)==0 : - requestid=1 - - else : - requestid= obj[0].request_id+1 - - - check= projects.objects.filter(project_id=projectid) - if not check.exists(): - messages.error(request,"Request not added, no such project exists") - return render(request,"rs/add_fund_requests.html") - - check= projects.objects.filter(project_id= projectid, project_investigator_id__username=pi_id) - if not check.exists(): - messages.error(request,"Request not added, no such project investigator exists") - return render(request,"rs/add_fund_requests.html") - - pi_id_instance=User.objects.get(username=pi_id) - project_instance=projects.objects.get(project_id=projectid) - - requests.objects.create( - request_id=requestid, - project_id=project_instance, - request_type="staff", - project_investigator_id=pi_id_instance, - description=desc - ) - messages.success(request,"Request added successfully") - return redirect("/research_procedures") - return render(request, "rs/add_requests.html") - - -@login_required -def view_projects(request): - queryset= projects.objects.all() - projects_per_page = 6 - page_number = request.GET.get('page') - - start_index = (int(page_number) - 1) * projects_per_page if page_number else 0 - end_index = start_index + projects_per_page - - paginated_projects = queryset[start_index:end_index] - - # Calculate total pages - total_pages = (queryset.count() + projects_per_page - 1) // projects_per_page - - rspc_admin = HoldsDesignation.objects.get(designation__name="rspc_admin") - rspc_admin =rspc_admin.user.username - if request.user.username == rspc_admin: - notifs = request.user.notifications.all() - total_pages_list = [i for i in range(1, total_pages + 1)] - data = { - 'notifications': notifs, - 'projects': paginated_projects, - 'total_pages': total_pages_list, - 'current_page': int(page_number) if page_number else 1, - "username": request.user.username, - } - return render(request,"rs/view_projects_rspc.html", context= data) - - queryset= projects.objects.filter(project_investigator_id__username= request.user.username) - start_index = (int(page_number) - 1) * projects_per_page if page_number else 0 - end_index = start_index + projects_per_page - - paginated_projects = queryset[start_index:end_index] - - # Calculate total pages - total_pages = (queryset.count() + projects_per_page - 1) // projects_per_page - total_pages_list = [i for i in range(1, total_pages + 1)] - data= { - 'projects': paginated_projects, - 'total_pages': total_pages_list, - 'current_page': int(page_number) if page_number else 1, - "username": request.user.username, - } - # print(data) - # print(request.user.username) - - return render(request,"rs/view_projects_rspc.html", context= data) - -@login_required -def view_requests(request,id): - - if id== '1': - queryset= requests.objects.filter(request_type= "staff") - elif id== '0': - rspc_admin = HoldsDesignation.objects.get(designation__name="rspc_admin") - rspc_admin =rspc_admin.user.username - if request.user.username == rspc_admin : - queryset= rspc_inventory.objects.all() - notifs = request.user.notifications.all() - data = { - 'notifications': notifs, - "requests": queryset, - "username": request.user.username - } - return render(request,"rs/view_requests.html", context= data) - - - queryset= rspc_inventory.objects.filter(project_investigator_id = request.user.username ) - else: - render(request,"/404.html") - - data= { - "requests": queryset, - "username": request.user.username, - "id":id, - } - - # print(data) - # print(request.user.username) - - return render(request,"rs/view_requests.html", context= data) - -@login_required -def view_financial_outlay(request,pid): - - table_data=financial_outlay.objects.filter(project_id=pid).order_by('category', 'sub_category') - project= projects.objects.get(project_id=pid); - - years = set(table_data.values_list('year', flat=True)) - - category_data = {} - for category in table_data.values_list('category', flat=True).distinct(): - category_data[category] = table_data.filter(category=category) - - - data = { - 'table_title': 'Total Budget Outlay', - 'table_caption': '...', # Add caption if needed - 'project_name':project.project_name, - 'years': list(years), - 'category_data': category_data, - } - - # print(data) - return render(request,"rs/view_financial_outlay.html", context= data) - - - - -@login_required -def submit_closure_report(request,id): - id= int(id) - obj= projects.objects.get(project_id=id) - obj.status= 1; - obj.save() - - queryset= projects.objects.filter(project_investigator_id = request.user.username) - - # print(queryset) - - data= { - "projects": queryset, - "username": request.user.username - } - messages.success(request,"Closure report submitted successfully") - return render(request,"rs/view_projects_rspc.html",context=data) - -@login_required -def view_project_inventory(request,pj_id): - pj_id=int(pj_id) - queryset= requests.objects.filter(project_id=pj_id,request_type="funds") - - - # print(queryset) - - data= { - "requests": queryset, - "username": request.user.username - } - return render(request,"rs/view_project_inventory.html",context=data) - -@login_required -def view_project_staff(request,pj_id): - pj_id=int(pj_id) - queryset= requests.objects.filter(project_id=pj_id,request_type="staff") - - - # print(queryset) - - data= { - "requests": queryset, - "username": request.user.username - } - return render(request,"rs/view_project_staff.html",context=data) - - - -def view_project_info(request,id): - id= int(id) - obj= projects.objects.get(project_id=id) - - - copis= co_pis.objects.filter(project_id__project_id=id) - data = { - "project": obj, - "copis": copis - } - - return render(request,"rs/view_project_info.html", context= data) - -@login_required -def financial_outlay_form(request,pid): - pid= int(pid) - project= projects.objects.get(project_id=pid); - categories = category.objects.all().distinct(); - - categories_with_subcategories = category.objects.values('category_name', 'sub_category_name') - - # Organize the data into a dictionary - category_subcategory_map = {} - for item in categories_with_subcategories: - category_name = item['category_name'] - subcategory = item['sub_category_name'] - if category_name in category_subcategory_map: - category_subcategory_map[category_name].append(subcategory) - else: - category_subcategory_map[category_name] = [subcategory] - - # Pass the organized data to the template - - data = { - "project_id": project.project_id, - "project_name":project.project_name, - "years": list(range(1, int(project.years) + 1)), - "category_subcategory_map": category_subcategory_map - - } - - return render(request,"rs/add_financial_outlay.html", context= data) -# return render(request,"rs/add_financial_outlay.html", context= data) - - -@login_required -def add_staff_details(request, pid): - if request.session.get('currentDesignationSelected') != 'rspc_admin': - messages.error(request, 'Only RSPC Admin can add staff details') - return redirect("/research_procedures") - if request.method == 'POST': - obj = request.POST - for key, value in obj.items(): - if key.startswith('staff_id'): - year_count = key.split('-')[-2] - staff_count = key.split('-')[-1] - staff_id_key = f'staff_id-{year_count}-{staff_count}' - staff_name_key = f'staff_name-{year_count}-{staff_count}' - qualification_key = f'qualification-{year_count}-{staff_count}' - stipend_key = f'stipend-{year_count}-{staff_count}' - year = year_count - staff_id = obj.get(staff_id_key, '').strip() - staff_name = obj.get(staff_name_key, '').strip() - qualification = obj.get(qualification_key, '').strip() - stipend = obj.get(stipend_key, '').strip() - project_instance = projects.objects.get(project_id=pid) - # print(type(staff_id)) - ob = staff_allocations.objects.all() - - if len(ob) == 0: - fid = 1 - else: - fid = ob[0].staff_allocation_id + 1 - - if not User.objects.filter(username=staff_id).exists(): - - messages.error(request, "Staff with ID " + staff_id + " does not exist") - return redirect("/research_procedures/add_staff_details/"+str(pid)) - - staff_id_instance = User.objects.get(username=staff_id) - - - staff_allocations.objects.create( - staff_allocation_id=fid, - project_id=project_instance, - staff_id=staff_id_instance, - staff_name=staff_name, - qualification=qualification, - year=year, - stipend=stipend - ) - - return redirect("/research_procedures/view_staff_details/"+str(pid)) - - project = projects.objects.get(project_id=pid) - - years_passed = int((datetime.datetime.now().date() - project.start_date).days / 365.25) - - data = { - "project_id": project.project_id, - "project_name" : project.project_name, - "years": list(range(1, int(project.years) + 1)), - "year": int(years_passed) + 1, - } - - return render(request, "rs/add_staff_details.html", context=data) - -@login_required -def view_staff_details(request,pid): - - staff_records = staff_allocations.objects.filter(project_id=pid) - - # Initialize a dictionary to hold data year-wise - data_by_year = {} - project = projects.objects.get(project_id=pid) - - # Iterate through each staff record - for record in staff_records: - year = record.year - if year not in data_by_year: - data_by_year[year] = [] - data_by_year[year].append({ - 'staff_allocation_id': record.staff_allocation_id, - 'staff_id' : record.staff_id, - 'staff_name': record.staff_name, - 'qualification': record.qualification, - 'stipend': record.stipend, - 'start_date': record.start_date, - 'end_date': record.end_date, - }) -# Pass the organized data to the template - context = { - 'data_by_year': data_by_year, - 'project_name':project.project_name - } - rspc_admin = HoldsDesignation.objects.get(designation__name="rspc_admin") - - - return render(request, "rs/view_staff_details.html", context) - - -@login_required -def add_financial_outlay(request,pid): - if request.method == 'POST': - - project = projects.objects.get(project_id=pid) - project.financial_outlay_status = 1 - project.save() - - obj = request.POST - for key, value in obj.items(): - if key.startswith('category-select'): - year_count = key.split('-')[-2] - category_count = key.split('-')[-1] - subcategory_key = f'subcategory-select-{year_count}-{category_count}' - amount_key = f'amount-{year_count}-{category_count}' - - category = value - subcategory = obj.get(subcategory_key, ['']) - amount = obj.get(amount_key, ['']) - year = int(year_count) - - # print(year) - # print(amount) - # print(subcategory) - # print(category) - project_instance=projects.objects.get(project_id=pid) - - - ob= financial_outlay.objects.all() - if len(ob)==0 : - fid=1 - - else : - fid= ob[0].financial_outlay_id+1 - financial_outlay.objects.create( - financial_outlay_id=fid, - project_id=project_instance, - category=category, - sub_category=subcategory, - amount=amount, - year=year, - status=0, - staff_limit=0 - ) - - - return redirect("/research_procedures/view_financial_outlay/"+str(pid)) - -@login_required -def inbox(request): - - projects_per_page = 6 - page_number = request.GET.get('page') - - start_index = (int(page_number) - 1) * projects_per_page if page_number else 0 - end_index = start_index + projects_per_page - - - - user_designation= request.session.get('currentDesignationSelected') - - print(user_designation) - user_designation= get_designation_instance(user_designation) - - - user_obj = get_user_by_username(request.user.username) - - # There was some issue using view_inbox function, so I had to write the code here - - data= Tracking.objects.filter(receiver_id=user_obj, receive_design=user_designation, file_id__src_module="research_procedures").order_by('-receive_date') - print(data) - files= [] - count =0 - for file in data: - count+=1 - files.append( File.objects.get(id=file.file_id.id) ) - - print(files) - paginated_projects = data[start_index:end_index] - - # Calculate total pages - total_pages = (data.count() + projects_per_page - 1) // projects_per_page - total_pages_list = [i for i in range(1, total_pages + 1)] - data1={ - 'inbox': paginated_projects, - 'total_pages': total_pages_list, - 'current_page': int(page_number) if page_number else 1, - "files": paginated_projects, - } - # print(data) - return render(request, "rs/inbox.html",context= data1) - -def view_file(request, id): - file1= File.objects.get(id=id) - tracks= Tracking.objects.filter(file_id=file1) - current_user = Tracking.objects.filter(file_id=file1).order_by('-receive_date')[0].current_id - - return render(request, "rs/view_file.html", context= {"file": file1, "tracks": tracks, "current_user": current_user}) - -@login_required -def add_staff_request(request,id): - if request.method == 'POST': - obj= request.POST - projectid = int(id) - receiver_designation = obj.get('receiver') - - receiver_designation= get_designation_instance(receiver_designation) - receiver = get_user_by_designation(receiver_designation).username - - subject= obj.get('subject') - sender = request.user.username - file_to_forward= request.FILES.get('file_to_forward') - project_instance=projects.objects.get(project_id=projectid) - receiver_instance=User.objects.get(username=receiver) - sender_designation= HoldsDesignation.objects.get(user= request.user).designation - receiver_designation = receiver_designation - - file_x= create_file( - uploader=sender, - uploader_designation=sender_designation, - receiver= receiver_instance.username, - receiver_designation=receiver_designation, - src_module="research_procedures", - src_object_id= projectid, - subject= subject, - file_extra_JSON= { "message": "Request Added." }, - attached_file= file_to_forward, - ) - - tracking_obj = Tracking.objects.get(file_id__id=file_x) - file_obj= File.objects.get(id=file_x) - - tracking_obj.upload_file= file_obj.upload_file - tracking_obj.remarks= "Request Added by " + request.user.username - tracking_obj.save() - - - messages.success(request,"request added successfully") - - return redirect("/research_procedures/view_project_info/"+ str(projectid)) - -@login_required -def view_request_inbox(request): - user_designation= getDesignation(request.user.username) - print(user_designation) - data = view_inbox(request.user.username,user_designation, "research_procedures") - print(data) - files= [] - count =0 - for i in data: - count+=1 - file1= File.objects.get(id=i['id']) - files.append((count, file1)) - - - data={ - - "inbox": data, - "files": files - } - # print(data) - return render(request, "rs/view_request_inbox.html",context= data) - -@login_required -def forward_request(request,id): - # forward_file( - # file_id: int, - # receiver: str, - # receiver_designation: str, - # file_extra_JSON: dict, - # remarks: str = "", - # file_attachment: Any = None) -> int: - if request.method == 'POST': - obj= request.POST - - fileid = int(id) - filez= File.objects.get(id=fileid) - - remarks = obj.get('remarks') - receiver_designation =obj.get('receiver_designation') - if receiver_designation == 'project_investigator': - project= projects.objects.get(project_id= filez.src_object_id ) - receiver_instance= project.project_investigator_id - receiver_designation= getDesignation(receiver_instance.username) - else: - receiver_instance= HoldsDesignation.objects.get(designation__name=receiver_designation).user - attachment= request.FILES.get('attachment') - receiver= receiver_instance.username - - filex= forward_file( - file_id= fileid, - receiver= receiver, - receiver_designation=receiver_designation, - file_extra_JSON= { "message": "Request forwarded."}, - remarks= remarks, - file_attachment= attachment, - ) - if(receiver_designation == 'Professor' or receiver_designation == 'Assistant Professor' or receiver_designation == 'rspc_admin'): - research_procedures_notif(request.user, receiver_instance, "Request update") - messages.success(request,"Request forwarded successfully") - - - return redirect("/research_procedures/inbox") - - - return redirect("/research_procedures/view_request_inbox") - - - - -def update_time_period(request,id): - if request.method== "POST": - - obj = request.POST - up_year= obj.get('updated_year') - - project = get_object_or_404(projects, project_id=id) - - project.years=up_year - - project.save() - - return redirect("/research_procedures/financial_outlay/"+str(id)) - - -@login_required -def update_financial_outlay(request,pid): - - - #post method - if(request.session.get('currentDesignationSelected') != 'rspc_admin'): - messages.error(request, 'Only RSPC Admin can update financial outlay') - return redirect("/research_procedures") - - if request.method=="POST" : - - obj = request.POST - financial_outlay_id = obj.get('financial_outlay_id') - # print("sdfkjsdfd") - used_amount = obj.get('used_amount') - if used_amount is None or used_amount == '': - messages.error(request,"Enter amount") - return redirect("/research_procedures/update_financial_outlay/" + str(pid)) - financial_outlay_instance = financial_outlay.objects.get(financial_outlay_id=financial_outlay_id) - # financial_outlay_instance.status = 1 - financial_outlay_instance.utilized_amount += int(used_amount) - financial_outlay_instance.save() - messages.success(request,"Financial Outlay updated successfully") - return redirect("/research_procedures/update_financial_outlay/"+str(pid)) - - - - #get method - table_data=financial_outlay.objects.filter(project_id=pid).order_by('category', 'sub_category') - project= projects.objects.get(project_id=pid); - - years = set(table_data.values_list('year', flat=True)) - - category_data = {} - for category in table_data.values_list('category', flat=True).distinct(): - category_data[category] = table_data.filter(category=category) - - - data = { - 'table_title': 'Total Budget Outlay', - 'table_caption': '...', # Add caption if needed - 'project_name':project.project_name, - 'years': list(years), - 'category_data': category_data, - 'project_id': pid, - } - - # print(data) - return render(request,"rs/update_financial_outlay.html", context= data) - -@login_required -def approve_request(request,id): - - - - if request.method == 'POST': - obj= request.POST - fileid = id - message= "Request approved by " + request.user.username - designation = get_designation_instance("rspc_admin") - receiver_instance= get_user_by_designation(designation) - receiver_designation= designation - receiver= receiver_instance.username - sender = request.user.username - filex= get_file_by_id(fileid) - file2=create_file( - uploader=sender, - uploader_designation= getDesignation(sender), - receiver= receiver, - receiver_designation=receiver_designation, - src_module="research_procedures", - src_object_id= filex.src_object_id, - file_extra_JSON= { "message": message + " by "+ sender}, - attached_file= filex.upload_file, - ) - delete_file(fileid) - messages.success(request,"Request approved successfully") - return redirect("/research_procedures/inbox") - - - -def download_project_pdf(request, project_id): - # Retrieve project and financial outlay information - project = projects.objects.get(project_id=project_id) - financial_outlays = financial_outlay.objects.filter(project_id=project_id) - - # Create a buffer for the PDF - buffer = BytesIO() - - # Create a PDF document - pdf = SimpleDocTemplate(buffer, pagesize=letter) - elements = [] - - # Construct project info table data - project_data = [['Project ID', 'Project Name', 'Project Type', 'Sponsored Agency', 'Start Date', 'Finish Date'], - [project.project_id, project.project_name, project.project_type, project.sponsored_agency, - project.start_date, project.finish_date]] - - # Construct financial outlay table data - financial_outlay_data = [['Financial Outlay ID', 'Category', 'Subcategory', 'Amount', 'Year']] - for outlay in financial_outlays: - financial_outlay_data.append([outlay.financial_outlay_id, outlay.category, outlay.sub_category, outlay.amount, outlay.year]) - - # Create project info table - project_table = Table(project_data) - project_table.setStyle(TableStyle([('BACKGROUND', (0, 0), (-1, 0), colors.grey), - ('TEXTCOLOR', (0, 0), (-1, 0), colors.whitesmoke), - ('ALIGN', (0, 0), (-1, -1), 'CENTER'), - ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'), - ('BOTTOMPADDING', (0, 0), (-1, 0), 12), - ('BACKGROUND', (0, 1), (-1, -1), colors.beige), - ('GRID', (0, 0), (-1, -1), 1, colors.black)])) - - # Create financial outlay table - financial_outlay_table = Table(financial_outlay_data) - financial_outlay_table.setStyle(TableStyle([('BACKGROUND', (0, 0), (-1, 0), colors.grey), - ('TEXTCOLOR', (0, 0), (-1, 0), colors.whitesmoke), - ('ALIGN', (0, 0), (-1, -1), 'CENTER'), - ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'), - ('BOTTOMPADDING', (0, 0), (-1, 0), 12), - ('BACKGROUND', (0, 1), (-1, -1), colors.beige), - ('GRID', (0, 0), (-1, -1), 1, colors.black)])) - - # Add tables to elements - elements.append(project_table) - elements.append(Spacer(1, 20)) - elements.append(financial_outlay_table) - - # Build the PDF - pdf.build(elements) - - # Close the PDF buffer and return the response with PDF content for download - buffer.seek(0) - response = HttpResponse(buffer.getvalue(), content_type='application/pdf') - response['Content-Disposition'] = f'attachment; filename=project_{project_id}.pdf' - return response - - -def change_year(request,id): - if request.method == 'POST': - obj= request.POST - projectid = int(id) - year = obj.get('year') - project_instance=projects.objects.get(project_id=projectid) - project_instance.years= year - project_instance.save() - messages.success(request,"Year changed successfully") - return redirect("/research_procedures/view_financial_outlay/"+str(projectid)) - -def change_end_date(request,id): - if request.method == "POST": - obj= request.POST - staff_allocations_id = int(id) - end_date = obj.get('end_date') - staff_allocation_instance = staff_allocations.objects.get(staff_allocation_id=staff_allocations_id) - staff_allocation_instance.end_date = end_date - staff_allocation_instance.save() - messages.success(request,"End date changed successfully") - return redirect("/research_procedures/view_staff_details/"+str(staff_allocation_instance.project_id.project_id)) - -def AjaxDropdown(request): - - if request.method == 'POST': - value = request.POST.get('value') - users = User.objects.filter(username__startswith=value) - users = serializers.serialize('json', list(users)) - - context = { - 'users': users - } - return HttpResponse(JsonResponse(context), content_type='application/json') - - -def getDesignation(us): - user_inst = User.objects.get(username= us) - - user_designation= HoldsDesignation.objects.filter(user= user_inst) - if user_designation.exists(): - user_designation= user_designation.first().designation.name - - return user_designation - -def get_file_by_id(id): - file1= File.objects.get(id=id) - print(file1) - return file1 - -def delete_file(id): - file1= File.objects.get(id=id) - tracking= Tracking.objects.get(file_id=file1) - tracking.delete() - file1.delete() - return - -def get_user_by_username(username): - return User.objects.get(username=username) - -def get_user_by_designation(designation): - return HoldsDesignation.objects.get(designation=designation).user - -def get_designation_instance(designation): - return Designation.objects.get(name=designation) - -def get_obj_by_username_and_designation(username,designation): - user_instance = get_user_by_username(username) - designation_instance = get_designation_instance(designation) - - return HoldsDesignation.objects.filter(user=user_instance, designation=designation_instance) - - - - - +def transfer_insert(request): + user = get_object_or_404(ExtraInfo, user=request.user) + pf = user.id + tech_transfer = TechTransfer() + tech_transfer.pf_no = pf + tech_transfer.details = request.POST.get('details') + tech_transfer.save() + messages.success(request,"Successfully created Technology Transfer") + return redirect(reverse("research_procedures:patent_registration")) \ No newline at end of file diff --git a/FusionIIIT/applications/scholarships/helpers.py b/FusionIIIT/applications/scholarships/helpers.py deleted file mode 100644 index 489f340a4..000000000 --- a/FusionIIIT/applications/scholarships/helpers.py +++ /dev/null @@ -1,14 +0,0 @@ -import subprocess -from datetime import datetime - -from django.conf import settings - - -def getBatch(roll): - batch = "20" - if(str(roll)[2].isdigit()): - return str(roll)[0:4] - - else: - batch+=str(roll)[0:2] - return batch \ No newline at end of file diff --git a/FusionIIIT/applications/scholarships/migrations/0001_initial.py b/FusionIIIT/applications/scholarships/migrations/0001_initial.py index 7e1962c88..176422fd9 100644 --- a/FusionIIIT/applications/scholarships/migrations/0001_initial.py +++ b/FusionIIIT/applications/scholarships/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 3.1.5 on 2024-07-16 15:44 +# Generated by Django 3.1.5 on 2023-03-15 18:53 import datetime from django.db import migrations, models @@ -85,7 +85,7 @@ class Migration(migrations.Migration): fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('programme', models.CharField(default='B.Tech', max_length=10)), - ('year', models.IntegerField(default=2024)), + ('year', models.IntegerField(default=2023)), ('award_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='scholarships.award_and_scholarship')), ('student', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='academic_information.student')), ], diff --git a/FusionIIIT/applications/scholarships/migrations/0002_auto_20250201_2228.py b/FusionIIIT/applications/scholarships/migrations/0002_auto_20250201_2228.py deleted file mode 100644 index b42373ea9..000000000 --- a/FusionIIIT/applications/scholarships/migrations/0002_auto_20250201_2228.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.1.5 on 2025-02-01 22:28 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('scholarships', '0001_initial'), - ] - - operations = [ - migrations.AlterField( - model_name='previous_winner', - name='year', - field=models.IntegerField(default=2025), - ), - ] diff --git a/FusionIIIT/applications/scholarships/urls.py b/FusionIIIT/applications/scholarships/urls.py index 121cdd116..fd5a3516f 100755 --- a/FusionIIIT/applications/scholarships/urls.py +++ b/FusionIIIT/applications/scholarships/urls.py @@ -18,4 +18,4 @@ url(r'^getContent/$', views.getContent, name='getContent'), url(r'^updateEndDate/$', views.updateEndDate, name='updateEndDate'), -] \ No newline at end of file +] diff --git a/FusionIIIT/applications/scholarships/views.py b/FusionIIIT/applications/scholarships/views.py index bbcbffb16..5f547149f 100755 --- a/FusionIIIT/applications/scholarships/views.py +++ b/FusionIIIT/applications/scholarships/views.py @@ -100,17 +100,16 @@ def convener_view(request): # It updates the student Notification table on the spacs head sending the mcm invitation if batch == 'all': active_batches = range(datetime.datetime.now().year - 4 , datetime.datetime.now().year + 1) - query = reduce(or_, (Q(id__id__startswith=int(batch)-2000) for batch in active_batches)) + query = reduce(or_, (Q(id__id__startswith=batch) for batch in active_batches)) recipient = Student.objects.filter(programme=programme).filter(query) else: - recipient = Student.objects.filter(programme=programme, id__id__startswith=int(batch)-2000) + recipient = Student.objects.filter(programme=programme, id__id__startswith=batch) # Notification starts convenor = request.user for student in recipient: scholarship_portal_notif(convenor, student.id.user, 'award_' + award) # Notification if award == 'Merit-cum-Means Scholarship': - print("MCM") rel = Release.objects.get(date_time=d_time) Notification.objects.select_related('student_id','release_id').bulk_create([Notification( release_id=rel, @@ -118,7 +117,6 @@ def convener_view(request): notification_mcm_flag=True, invite_mcm_accept_flag=False) for student in recipient]) else: - print("convo") rel = Release.objects.get(date_time=d_time) Notification.objects.select_related('student_id','release_id').bulk_create([Notification( release_id=rel, @@ -148,10 +146,8 @@ def convener_view(request): year = datetime.datetime.now().year Mcm.objects.select_related('award_id','student').filter(id=pk).update(status='Accept') request.session['last_clicked'] = 'Accept_MCM' - zest=Previous_winner.objects.filter(student = student_id, year=year, award_id=award) - print(zest) - if(len(zest)==0): - Previous_winner.objects.create(student=student_id, year=year, award_id=award) + Previous_winner.objects.create( + student=student_id, year=year, award_id=award) convenor = request.user recipient = student_id scholarship_portal_notif(convenor, recipient.id.user, 'Accept_MCM') @@ -175,10 +171,8 @@ def convener_view(request): student_id = Director_gold.objects.select_related('student','award_id').get(id=pk).student year = datetime.datetime.now().year Director_gold.objects.select_related('student','award_id').filter(id=pk).update(status='Accept') - zest=Previous_winner.objects.filter(student = student_id, year=year, award_id=award) - print(zest) - if(len(zest)==0): - Previous_winner.objects.create(student=student_id, year=year, award_id=award) + Previous_winner.objects.create( + student=student_id, year=year, award_id=award) convenor = request.user recipient = student_id scholarship_portal_notif( @@ -205,13 +199,12 @@ def convener_view(request): student_id = Director_silver.objects.select_related('student','award_id').get(id=pk).student year = datetime.datetime.now().year Director_silver.objects.select_related('student','award_id').filter(id=pk).update(status='Accept') - zest=Previous_winner.objects.filter(student = student_id, year=year, award_id=award) - print(zest) - if(len(zest)==0): - Previous_winner.objects.create(student=student_id, year=year, award_id=award) + Previous_winner.objects.create( + student=student_id, year=year, award_id=award) convenor = request.user recipient = student_id - scholarship_portal_notif(convenor, recipient.id.user, 'Accept_Silver') + scholarship_portal_notif( + convenor, recipient.id.user, 'Accept_Silver') request.session['last_clicked'] = 'Accept_Silver' messages.success(request, 'Application is accepted') return HttpResponseRedirect('/spacs/convener_view') @@ -222,7 +215,8 @@ def convener_view(request): Director_silver.objects.select_related('student','award_id').filter(id=pk).update(status='Reject') convenor = request.user recipient = student_id - scholarship_portal_notif(convenor, recipient.id.user, 'Reject_Silver') + scholarship_portal_notif( + convenor, recipient.id.user, 'Reject_Silver') request.session['last_clicked'] = 'Reject_Silver' messages.success(request, 'Application is rejected') return HttpResponseRedirect('/spacs/convener_view') @@ -233,10 +227,8 @@ def convener_view(request): student_id = Proficiency_dm.objects.select_related('student','award_id').get(id=pk).student year = datetime.datetime.now().year Proficiency_dm.objects.select_related('student','award_id').filter(id=pk).update(status='Accept') - zest=Previous_winner.objects.filter(student = student_id, year=year, award_id=award) - print(zest) - if(len(zest)==0): - Previous_winner.objects.create(student=student_id, year=year, award_id=award) + Previous_winner.objects.create( + student=student_id, year=year, award_id=award) convenor = request.user recipient = student_id scholarship_portal_notif(convenor, recipient.id.user, 'Accept_DM') @@ -423,7 +415,6 @@ def getWinners(request): return HttpResponse(json.dumps(context), content_type='getWinners/json') def get_MCM_Flag(request): # Here we are extracting mcm_flag - print("get mcm_flags here") x = Notification.objects.select_related('student_id','release_id').filter(student_id=request.user.extrainfo.id) for i in x: i.invite_mcm_accept_flag = True @@ -440,7 +431,6 @@ def get_MCM_Flag(request): # Here we are extracting mcm_flag # return HttpResponseRedirect('/spacs/student_view') def getConvocationFlag(request): # Here we are extracting convocation_flag - print("get convo_flags here") x = Notification.objects.filter(student_id=request.user.extrainfo.id) for i in x: i.invite_convocation_accept_flag = True @@ -1051,27 +1041,22 @@ def sendStudentRenderRequest(request, additionalParams={}): x_notif_con_flag = False for dates in release: if checkDate(dates.startdate, dates.enddate): - print("sudheer's test --->") - print(request.user.extrainfo.student) - print(str(request.user.extrainfo.student)[0:2]) - if dates.award == 'Merit-cum-Means Scholarship' and dates.batch == "20"+str(request.user.extrainfo.student)[0:2]and dates.programme == request.user.extrainfo.student.programme: + if dates.award == 'Merit-cum-Means Scholarship' and dates.batch == str(request.user.extrainfo.student)[0:4] and dates.programme == request.user.extrainfo.student.programme: x_notif_mcm_flag = True if no_of_mcm_filled > 0: update_mcm_flag = True - elif dates.award == 'Convocation Medals' and dates.batch == "20"+str(request.user.extrainfo.student)[0:2]and dates.programme == request.user.extrainfo.student.programme: - + elif dates.award == 'Convocation Medals' and dates.batch == str(request.user.extrainfo.student)[0:4] and dates.programme == request.user.extrainfo.student.programme: x_notif_con_flag = True if no_of_con_filled > 0: update_con_flag = True else: - - if dates.award == "Merit-cum-Means Scholarship" and dates.batch =="20"+ str(request.user.extrainfo.student)[0:2]: + if dates.award == "Merit-cum-Means Scholarship" and dates.batch == str(request.user.extrainfo.student)[0:4]: try: x = Notification.objects.select_related('student_id','release_id').get( student_id=request.user.extrainfo.id, release_id=dates.id).delete() except: pass - elif dates.award == 'Convocation Medals' and dates.batch == "20"+str(request.user.extrainfo.student)[0:2]: + elif dates.award == 'Convocation Medals' and dates.batch == str(request.user.extrainfo.student)[0:4]: try: x = Notification.objects.select_related('student_id','release_id').get( student_id=request.user.extrainfo.id, release_id=dates.id).delete() @@ -1079,11 +1064,9 @@ def sendStudentRenderRequest(request, additionalParams={}): pass x = Notification.objects.select_related('student_id','release_id').filter(student_id=request.user.extrainfo.id).order_by('-release_id__date_time') - print(x) show_mcm_flag = False show_convocation_flag = False for i in x: - print(i.invite_convocation_accept_flag) if i.invite_mcm_accept_flag == True: show_mcm_flag = True break @@ -1129,4 +1112,4 @@ def getCommonParams(request): context = {'mcm': mcm, 'awards': awards, 'student': student, 'gold': gold, 'silver': silver, 'dandm': dandm, 'con': con, 'assis': assis, 'hd': hd, 'hd1': hd1, 'last_clicked': last_clicked, 'year_range': year_range, 'active_batches': active_batches} - return context \ No newline at end of file + return context diff --git a/FusionIIIT/applications/visitor_hostel/forms.py b/FusionIIIT/applications/visitor_hostel/forms.py index c506bc932..32b915407 100644 --- a/FusionIIIT/applications/visitor_hostel/forms.py +++ b/FusionIIIT/applications/visitor_hostel/forms.py @@ -5,37 +5,41 @@ from applications.visitor_hostel.models import * +from .models import Inventory + #class booking_request(forms.Form): CHOICES = (('A', 'A',), ('B', 'B',), ('C', 'C',), ('D', 'D',)) class ViewBooking(forms.Form): - date_from = forms.DateField(initial=datetime.date.today) - date_to = forms.DateField(initial=datetime.date.today) + date_from = forms.DateField(initial=datetime.date.today) + date_to = forms.DateField(initial=datetime.date.today) +class MealBooking(ModelForm): + date = forms.DateField(initial=datetime.date.today) + class Meta: + model = MealRecord + exclude = ['meal_date'] class RoomAvailability(forms.Form): - date_from = forms.DateField(initial=datetime.date.today) - date_to = forms.DateField(initial=datetime.date.today) + date_from = forms.DateField(initial=datetime.date.today) + date_to = forms.DateField(initial=datetime.date.today) -# class InventoryForm(forms.Form): -# # class Meta: -# # model = Inventory -# # fields = ["item_name", "quantity"] -# item_name = forms.CharField(max_length=20) -# quantity = forms.IntegerField(min_value=0) -# consumable = forms.BooleanField(initial=False) -# # checkbox = ["consumable"] +class InventoryForm(forms.ModelForm): + class Meta: + model = Inventory + fields = ["item_name", "quantity", "consumable"] class Room_booking(forms.Form): - name = forms.CharField(max_length=100) - mob = forms.CharField(max_length=12) - email = forms.CharField(max_length=40) - address = forms.CharField(max_length=200) - country = forms.CharField(max_length=25) - category = forms.ChoiceField(widget=forms.RadioSelect, choices=CHOICES) - total_persons = forms.IntegerField() - purpose = forms.CharField(widget=forms.Textarea) - date_from = forms.DateField(initial=datetime.date.today) - date_to = forms.DateField(initial=datetime.date.today) + name = forms.CharField(max_length=100) + mob = forms.CharField(max_length=12) + email = forms.CharField(max_length=40) + address = forms.CharField(max_length=200) + country = forms.CharField(max_length=25) + category = forms.ChoiceField(widget=forms.RadioSelect, choices=CHOICES) + total_persons = forms.IntegerField() + purpose = forms.CharField(widget=forms.Textarea) + date_from = forms.DateField(initial=datetime.date.today) + date_to = forms.DateField(initial=datetime.date.today) + \ No newline at end of file diff --git a/FusionIIIT/applications/visitor_hostel/migrations/0001_initial.py b/FusionIIIT/applications/visitor_hostel/migrations/0001_initial.py index fca0cab67..f4229feb2 100644 --- a/FusionIIIT/applications/visitor_hostel/migrations/0001_initial.py +++ b/FusionIIIT/applications/visitor_hostel/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 3.1.5 on 2024-07-16 15:44 +# Generated by Django 3.1.5 on 2023-03-15 18:53 from django.conf import settings from django.db import migrations, models @@ -89,13 +89,14 @@ class Migration(migrations.Migration): fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('meal_date', models.DateField()), - ('morning_tea', models.IntegerField(default=0)), - ('eve_tea', models.IntegerField(default=0)), - ('breakfast', models.IntegerField(default=0)), - ('lunch', models.IntegerField(default=0)), - ('dinner', models.IntegerField(default=0)), + ('morning_tea', models.BooleanField(default=False)), + ('eve_tea', models.BooleanField(default=False)), + ('breakfast', models.BooleanField(default=False)), + ('lunch', models.BooleanField(default=False)), + ('dinner', models.BooleanField(default=False)), ('persons', models.IntegerField(default=0)), ('booking', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='visitor_hostel.bookingdetail')), + ('room', models.ForeignKey(default=0, on_delete=django.db.models.deletion.CASCADE, to='visitor_hostel.roomdetail')), ('visitor', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='visitor_hostel.visitordetail')), ], ), @@ -125,7 +126,6 @@ class Migration(migrations.Migration): ('meal_bill', models.IntegerField(default=0)), ('room_bill', models.IntegerField(default=0)), ('payment_status', models.BooleanField(default=False)), - ('bill_date', models.DateField(blank=True, default=django.utils.timezone.now)), ('booking', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='visitor_hostel.bookingdetail')), ('caretaker', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), ('room', models.ManyToManyField(to='visitor_hostel.RoomDetail')), diff --git a/FusionIIIT/applications/visitor_hostel/migrations/0002_data_integrity_fixes.py b/FusionIIIT/applications/visitor_hostel/migrations/0002_data_integrity_fixes.py new file mode 100644 index 000000000..e2e00d6fb --- /dev/null +++ b/FusionIIIT/applications/visitor_hostel/migrations/0002_data_integrity_fixes.py @@ -0,0 +1,118 @@ +from django.conf import settings +from django.core.validators import MinValueValidator +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('visitor_hostel', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='bill', + name='meal_bill', + field=models.IntegerField(default=0, validators=[MinValueValidator(0)]), + ), + migrations.AlterField( + model_name='bill', + name='room_bill', + field=models.IntegerField(default=0, validators=[MinValueValidator(0)]), + ), + migrations.AlterField( + model_name='bookingdetail', + name='caretaker', + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name='caretaker', + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AlterField( + model_name='bookingdetail', + name='number_of_rooms', + field=models.IntegerField( + blank=True, + default=1, + null=True, + validators=[MinValueValidator(1)], + ), + ), + migrations.AlterField( + model_name='bookingdetail', + name='number_of_rooms_alloted', + field=models.IntegerField( + blank=True, + default=1, + null=True, + validators=[MinValueValidator(0)], + ), + ), + migrations.AlterField( + model_name='bookingdetail', + name='person_count', + field=models.IntegerField(default=1, validators=[MinValueValidator(1)]), + ), + migrations.AlterField( + model_name='inventory', + name='addition_stock', + field=models.IntegerField(default=0, validators=[MinValueValidator(0)]), + ), + migrations.AlterField( + model_name='inventory', + name='inuse', + field=models.IntegerField(default=0, validators=[MinValueValidator(0)]), + ), + migrations.AlterField( + model_name='inventory', + name='non_serviceable', + field=models.IntegerField(default=0, validators=[MinValueValidator(0)]), + ), + migrations.AlterField( + model_name='inventory', + name='opening_stock', + field=models.IntegerField(default=0, validators=[MinValueValidator(0)]), + ), + migrations.AlterField( + model_name='inventory', + name='quantity', + field=models.IntegerField(default=0, validators=[MinValueValidator(0)]), + ), + migrations.AlterField( + model_name='inventory', + name='serviceable', + field=models.IntegerField(default=0, validators=[MinValueValidator(0)]), + ), + migrations.AlterField( + model_name='inventory', + name='total_stock', + field=models.IntegerField(default=0, validators=[MinValueValidator(0)]), + ), + migrations.AlterField( + model_name='inventory', + name='total_usable', + field=models.IntegerField(default=0, validators=[MinValueValidator(0)]), + ), + migrations.AlterField( + model_name='inventorybill', + name='cost', + field=models.IntegerField(default=0, validators=[MinValueValidator(0)]), + ), + migrations.AlterField( + model_name='mealrecord', + name='persons', + field=models.IntegerField(default=0, validators=[MinValueValidator(0)]), + ), + migrations.AlterField( + model_name='mealrecord', + name='room', + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to='visitor_hostel.roomdetail', + ), + ), + ] diff --git a/FusionIIIT/applications/visitor_hostel/models.py b/FusionIIIT/applications/visitor_hostel/models.py index ffd49a1be..61bbab1dc 100644 --- a/FusionIIIT/applications/visitor_hostel/models.py +++ b/FusionIIIT/applications/visitor_hostel/models.py @@ -1,6 +1,7 @@ -from django.contrib.auth.models import User -from django.db import models -from django.utils import timezone +from django.contrib.auth.models import User +from django.core.validators import MinValueValidator +from django.db import models +from django.utils import timezone from applications.globals.models import ExtraInfo @@ -58,6 +59,7 @@ class VisitorDetail(models.Model): visitor_address = models.TextField(blank=True) nationality = models.CharField(max_length=20, blank=True) + def __str__(self): return '{} - {}'.format(self.id, self.visitor_name, self.visitor_email, self.visitor_organization, self.visitor_address, self.visitor_phone) @@ -70,15 +72,15 @@ class RoomDetail(models.Model): room_status = models.CharField(max_length=20, choices=ROOM_STATUS, default='Available') def __str__(self): - return '{} - {}'.format(self.id, self.room_number , self.room_type, self.room_status, self.room_floor) + return self.room_number -class BookingDetail(models.Model): - intender = models.ForeignKey(User, related_name='intender', on_delete=models.CASCADE) - caretaker = models.ForeignKey(User, related_name='caretaker', default=1, on_delete=models.CASCADE) +class BookingDetail(models.Model): + intender = models.ForeignKey(User, related_name='intender', on_delete=models.CASCADE) + caretaker = models.ForeignKey(User, related_name='caretaker', on_delete=models.CASCADE) visitor_category = models.CharField(max_length=1, choices=VISITOR_CATEGORY, default='C') modified_visitor_category = models.CharField(max_length=1, choices=VISITOR_CATEGORY, default='C') - person_count = models.IntegerField(default=1) + person_count = models.IntegerField(default=1, validators=[MinValueValidator(1)]) purpose = models.TextField(default="Hi!") booking_from = models.DateField() booking_to = models.DateField() @@ -95,61 +97,73 @@ class BookingDetail(models.Model): visitor = models.ManyToManyField(VisitorDetail) image = models.FileField(null=True, blank=True, upload_to='VhImage/') rooms = models.ManyToManyField(RoomDetail) - number_of_rooms = models.IntegerField(default=1,null=True,blank=True) - number_of_rooms_alloted = models.IntegerField(default=1,null=True,blank=True) + number_of_rooms = models.IntegerField( + default=1, + null=True, + blank=True, + validators=[MinValueValidator(1)], + ) + number_of_rooms_alloted = models.IntegerField( + default=1, + null=True, + blank=True, + validators=[MinValueValidator(0)], + ) booking_date = models.DateField(auto_now_add=False, auto_now=False, default=timezone.now) bill_to_be_settled_by = models.CharField(max_length=15, choices=BILL_TO_BE_SETTLED_BY ,default ="Intender") - + def __str__(self): return '%s ----> %s - %s id is %s and category is %s' % (self.id, self.visitor, self.status, self.id, self.visitor_category) -class MealRecord(models.Model): - booking = models.ForeignKey(BookingDetail, on_delete=models.CASCADE) - visitor = models.ForeignKey(VisitorDetail, on_delete=models.CASCADE) +class MealRecord(models.Model): + booking = models.ForeignKey(BookingDetail, on_delete=models.CASCADE) + visitor = models.ForeignKey(VisitorDetail, on_delete=models.CASCADE) + room = models.ForeignKey(RoomDetail, on_delete=models.CASCADE) meal_date = models.DateField() - morning_tea = models.IntegerField(default=0) - eve_tea = models.IntegerField(default=0) - breakfast = models.IntegerField(default=0) - lunch = models.IntegerField(default=0) - dinner = models.IntegerField(default=0) - persons=models.IntegerField(default=0) + morning_tea = models.BooleanField(default=False) + eve_tea = models.BooleanField(default=False) + breakfast = models.BooleanField(default=False) + lunch = models.BooleanField(default=False) + dinner = models.BooleanField(default=False) + persons = models.IntegerField(default=0, validators=[MinValueValidator(0)]) -class Bill(models.Model): +class Bill(models.Model): booking = models.OneToOneField(BookingDetail, on_delete=models.CASCADE) room = models.ManyToManyField(RoomDetail) caretaker = models.ForeignKey(User, on_delete=models.CASCADE) - meal_bill = models.IntegerField(default=0) - room_bill = models.IntegerField(default=0) + meal_bill = models.IntegerField(default=0, validators=[MinValueValidator(0)]) + room_bill = models.IntegerField(default=0, validators=[MinValueValidator(0)]) payment_status = models.BooleanField(default=False) - bill_date = models.DateField(default=timezone.now, blank=True) def __str__(self): return '%s ----> %s - %s id is %s' % (self.booking.id, self.meal_bill, self.room_bill, self.payment_status) -class Inventory(models.Model): - item_name = models.CharField(max_length=20) - quantity = models.IntegerField(default=0) - consumable = models.BooleanField(default=False) - opening_stock = models.IntegerField(default=0) - addition_stock = models.IntegerField(default=0) - total_stock = models.IntegerField(default=0) - serviceable = models.IntegerField(default=0) - non_serviceable = models.IntegerField(default=0) - inuse = models.IntegerField(default=0) - total_usable = models.IntegerField(default=0) +class Inventory(models.Model): + item_name = models.CharField(max_length=20) + quantity = models.IntegerField(default=0, validators=[MinValueValidator(0)]) + consumable = models.BooleanField(default=False) + opening_stock = models.IntegerField(default=0, validators=[MinValueValidator(0)]) + addition_stock = models.IntegerField(default=0, validators=[MinValueValidator(0)]) + total_stock = models.IntegerField(default=0, validators=[MinValueValidator(0)]) + serviceable = models.IntegerField(default=0, validators=[MinValueValidator(0)]) + non_serviceable = models.IntegerField(default=0, validators=[MinValueValidator(0)]) + inuse = models.IntegerField(default=0, validators=[MinValueValidator(0)]) + total_usable = models.IntegerField(default=0, validators=[MinValueValidator(0)]) remark = models.TextField(blank=True) def __str__(self): return '{} - {}'.format(self.id, self.item_name) -class InventoryBill(models.Model): - item_name = models.ForeignKey(Inventory, on_delete=models.CASCADE) - bill_number = models.CharField(max_length=40) - cost = models.IntegerField(default=0) +class InventoryBill(models.Model): + item_name = models.ForeignKey(Inventory, on_delete=models.CASCADE) + bill_number = models.CharField(max_length=40) + cost = models.IntegerField(default=0, validators=[MinValueValidator(0)]) def __str__(self): return str(self.bill_number) + + diff --git a/FusionIIIT/applications/visitor_hostel/static/visitor_hostel/visitor-hostel.js b/FusionIIIT/applications/visitor_hostel/static/visitor_hostel/visitor-hostel.js index 5338efcbc..3ecb7a431 100644 --- a/FusionIIIT/applications/visitor_hostel/static/visitor_hostel/visitor-hostel.js +++ b/FusionIIIT/applications/visitor_hostel/static/visitor_hostel/visitor-hostel.js @@ -294,41 +294,31 @@ function request_booking (event) { // Meal Record -function bookameal_submit(event, htmlElement){ - console.log('hii') +$('.bookameal-submit').click(function(event){ event.preventDefault(); - var pk = $(htmlElement).attr('data-pk'); - var booking_id = $(htmlElement).attr('booking_id') - // 'numberofpeople': $('input[name="numberofpeople-meal"]').val(), - console.log(pk); - var jsondata = { - 'pk' : pk, - 'booking' : booking_id, - 'm_tea': $(`input[name="m_tea${booking_id}"]`).val(), - 'breakfast': $(`input[name="breakfast${booking_id}"]`).val(), - 'lunch': $(`input[name="lunch${booking_id}"]`).val(), - 'eve_tea': $(`input[name="eve_tea${booking_id}"]`).val(), - 'dinner': $(`input[name="dinner${booking_id}"]`).val(), - - 'csrfmiddlewaretoken': $('input[name="csrf"]').val(), - } - console.log(jsondata) + var pk = $(this).attr('data-pk'); + var food = [] + $('input[name=food'+pk+']:checked').each(function(){ + food.push($(this).val()); + }); $.ajax({ type: 'POST', url: '/visitorhostel/record-meal/', - data: jsondata, + data: { + 'pk' : pk, + 'booking' : $('input[name="meal-booking-id"]').val(), + 'numberofpeople': $('input[name="numberofpeople-meal"]').val(), + 'food':food, + 'csrfmiddlewaretoken': $('input[name="csrf"]').val(), + }, success: function(data) { alertModal('Great! Meals recorded successfully'); - setTimeout(function() { - location.reload(); - }, 1500); - }, - error: function (data, err) { - alertModal('Something missing! Please refill the form '); + error: function(data, err) { + alertModal('Something missing! Please refill the form'); } }); -}; +}); // Update Inventory @@ -366,75 +356,49 @@ function submit_inventory_form(id){ // Adding more items to inventory -// $('#add-more-items-inventory').click(function(event){ +$('#add-more-items-inventory').click(function(event){ -// $.ajax({ -// type: 'POST', -// url: '/visitorhostel/add-to-inventory/', -// data: { -// 'item' : $('.reset-this-form')[0].children[2].children[0].children[1].children[0].value, -// 'quantity' : $('.reset-this-form')[0].children[2].children[1].children[1].children[0].value, -// 'amount' : $('.reset-this-form')[0].children[2].children[2].children[1].children[0].value, -// 'consumable' : $('.reset-this-form')[0].children[2].children[3].children[1].children[0].value, -// 'csrfmiddlewaretoken': '{{csrf_token}}' -// }, -// success: function(data) { -// $('.reset-this-form')[0].children[2].children[0].children[1].children[0].value = ""; -// $('.reset-this-form')[0].children[2].children[1].children[1].children[0].value = ""; -// $('.reset-this-form')[0].children[2].children[2].children[1].children[0].value = ""; -// }, -// error: function(data, err) { -// alertModal('Something missing! Please refill the form'); -// } -// }); - -// }); - -function add_inventory_item(event) { - event.preventDefault(); - jsondata = { - 'csrfmiddlewaretoken': $('input[name="csrf"]').val(), - 'item_name' : $('input[name="item_name"]').val(), - 'quantity' : $('input[name="quantity_add"]').val(), - 'cost' : $('input[name="cost"]').val(), - 'consumable': ($('input[name="consumable"]:checked').val())?true:false, - 'bill_number' : $('input[name="bill_number"]').val() - } - console.log(jsondata) $.ajax({ type: 'POST', url: '/visitorhostel/add-to-inventory/', - data: jsondata, + data: { + 'item' : $('.reset-this-form')[0].children[2].children[0].children[1].children[0].value, + 'quantity' : $('.reset-this-form')[0].children[2].children[1].children[1].children[0].value, + 'amount' : $('.reset-this-form')[0].children[2].children[2].children[1].children[0].value, + 'consumable' : $('.reset-this-form')[0].children[2].children[3].children[1].children[0].value, + 'csrfmiddlewaretoken': '{{csrf_token}}' + }, success: function(data) { - // $('.reset-this-form')[0].children[2].children[0].children[1].children[0].value = ""; - // $('.reset-this-form')[0].children[2].children[1].children[1].children[0].value = ""; - // $('.reset-this-form')[0].children[2].children[2].children[1].children[0].value = ""; - alertModal('Great! Item added successfully'); - setTimeout(function() { - location.reload(); - }, 1500); + $('.reset-this-form')[0].children[2].children[0].children[1].children[0].value = ""; + $('.reset-this-form')[0].children[2].children[1].children[1].children[0].value = ""; + $('.reset-this-form')[0].children[2].children[2].children[1].children[0].value = ""; }, - error: function(xhr, data, err) { + error: function(data, err) { alertModal('Something missing! Please refill the form'); } - }); -} -$('.add-item-form-submit').click(function(event){ + +}); + +$('#add-item-form-submit').click(function(event){ event.preventDefault(); - jsondata = { - 'csrfmiddlewaretoken': $('input[name="csrf"]').val(), - 'item_name' : $('input[name="item_name"]').val(), - 'quantity' : $('input[name="quantity_add"]').val(), - 'cost' : $('input[name="cost"]').val(), - 'consumable': ($('input[name="consumable"]:checked').val())?true:false, - 'bill_number' : $('input[name="bill_number"]').val() - } - console.log(jsondata) + if($('input[name="consumable"]:checked')){ + consumable = 'True'; + } + else{ + consumable = 'False'; + } $.ajax({ type: 'POST', url: '/visitorhostel/add-to-inventory/', - data: jsondata, + data: { + 'csrfmiddlewaretoken': $('input[name="csrf"]').val(), + 'item_name' : $('input[name="item-name"]').val(), + 'quantity' : $('input[name="quantity_add"]').val(), + 'cost' : $('input[name="cost"]').val(), + 'consumable' : consumable, + 'bill_number' : $('input[name="bill_number"]').val() + }, success: function(data) { $('.reset-this-form')[0].children[2].children[0].children[1].children[0].value = ""; $('.reset-this-form')[0].children[2].children[1].children[1].children[0].value = ""; @@ -650,20 +614,12 @@ function forward_booking (id) { previous_category = $('input[name=category-'+id+']').val(); modified_category = $('input[name=modified-category-'+id+']').val(); rooms = $('select[name=alloted-rooms-'+id+']').val(); - rem = $('input[name=cancellation-remarks-' + id + ']').val(); - console.log("TExt rem: ", rem); + // if (previous_category == 0) { // alertModal("Please fill the category to confirm."); // return; // } - console.log("Rooms: ", rooms); - remark = ""; - for (x in rooms) { - remark += rooms[x] + " "; - } - remark += "can be alloted"; - console.log("remarks: ", remark); if (modified_category == 0) { modified_category = previous_category; } @@ -681,8 +637,7 @@ function forward_booking (id) { 'csrfmiddlewaretoken': csrfmiddlewaretoken, 'previous_category' : previous_category, 'modified_category' : modified_category, - 'rooms': rooms, - 'remark': remark, + 'rooms' : rooms, }, success: function(data) { alertModal("This booking has been forwarded"); @@ -729,28 +684,28 @@ function cancel_active_booking (id, booking_from) { // Edit Inventory -// $("#edit-inventory").click(function(e){ -// $(".inventory-item").slideToggle(); -// $(".inventory-form").slideToggle(); -// $("#update-inventory-submit").slideToggle(); -// }); -// $("#update-inventory-submit").click(function(e){ -// event.preventDefault(); -// $.ajax({ -// type: 'POST', -// url: '/visitorhostel/bookaroom/', -// data: { -// 'csrfmiddlewaretoken' : '{{csrf_token}}', -// 'data' : data -// }, -// success: function(data) { -// alertModal("Congratulations! Inventory is updated successfully"); -// }, -// error: function(data, err) { -// alertModal('Something missing! PLease refill the form'); -// } -// }); -// }); +$("#edit-inventory").click(function(e){ + $(".inventory-item").slideToggle(); + $(".inventory-form").slideToggle(); + $("#update-inventory-submit").slideToggle(); +}); +$("#update-inventory-submit").click(function(e){ + event.preventDefault(); + $.ajax({ + type: 'POST', + url: '/visitorhostel/bookaroom/', + data: { + 'csrfmiddlewaretoken' : '{{csrf_token}}', + 'data' : data + }, + success: function(data) { + alertModal("Congratulations! Inventory is updated successfully"); + }, + error: function(data, err) { + alertModal('Something missing! PLease refill the form'); + } + }); +}); // Submit Visitor Details @@ -832,7 +787,7 @@ function submit_visitor_details (id) { // Check Out -function check_out(id, mess_bill, room_bill) { +function check_out (id , mess_bill , room_bill) { $.ajax({ type: 'POST', url: '/visitorhostel/check-out/', @@ -858,10 +813,10 @@ function check_out(id, mess_bill, room_bill) { function bill_between_date_range() { - start = $('input[name=start]').val(); - end = $('input[name=end]').val(); - - if(new Date(start)>new Date(end)) + start_date = $('input[name=start').val(); + end_date = $('input[name=end]').val(); + + if(new Date(start_date)>new Date(end_date)) { alertModal('Please check start date and end date.') return; @@ -869,21 +824,24 @@ function bill_between_date_range() { $.ajax({ type: 'POST', - url: '/visitorhostel/bill_between_dates/', + url: '/visitorhostel/bill_between_date_range/', data: { 'csrfmiddlewaretoken' : $('input[name="csrf"]').val(), - 'start_date' : start, - 'end_date' : end, + 'start_date' : start_date, + 'end_date' : end_date, }, success: function(data) { $('#replace-this-div-booking-bw-dates').html(data); console.log("winning"); + console.log(start_date); // alert('Bookings Between range are ..'); }, error: function(data, err) { - alertModal ('Error !', data, err); + alertModal ('Error !'); + console.log(start_date); + console.log(end_date); // alertModal('Something missing! Please refill the form'); } }); @@ -892,12 +850,14 @@ function bill_between_date_range() { // finding available room's list between date range function find_available_rooms ( available_rooms ) { - start_date = $('input[name=start-date]').val(); + start_date = $('input[name=start-date').val(); end_date = $('input[name=end-date]').val(); if (new Date(start_date) > new Date(end_date)) { alertModal ('Please check start date and end date!'); return; } + console.log(start_date); + console.log(end_date); $.ajax({ type: 'POST', diff --git a/FusionIIIT/applications/visitor_hostel/urls.py b/FusionIIIT/applications/visitor_hostel/urls.py index 1153e0d88..fc352d646 100644 --- a/FusionIIIT/applications/visitor_hostel/urls.py +++ b/FusionIIIT/applications/visitor_hostel/urls.py @@ -12,9 +12,9 @@ url(r'^get-booking-form/', views.get_booking_form, name='get_booking_form'), url(r'^request-booking/' , views.request_booking , name ='request_booking'), url(r'^confirm-booking/' , views.confirm_booking , name ='confirm_booking'), - url(r'^cancel-booking/', views.cancel_booking, name='cancel_booking'), - url(r'^cancel-booking-request/', views.cancel_booking_request,name='cancel_booking_request'), - url(r'^reject-booking/', views.reject_booking, name='reject_booking'), + url(r'^cancel-booking/', views.cancel_booking, name = 'cancel_booking'), + url(r'^cancel-booking-request/', views.cancel_booking_request, name = 'cancel_booking_request'), + url(r'^reject-booking/', views.reject_booking, name = 'reject_booking'), url(r'^check-in/', views.check_in, name = 'check_in'), url(r'^check-out/', views.check_out, name = 'check_out'), url(r'^record-meal/', views.record_meal, name = 'record_meal'), @@ -29,3 +29,4 @@ url(r'^booking-details/', views.booking_details, name = 'booking_details'), url(r'^forward-booking/', views.forward_booking, name = 'forward_booking'), ] + \ No newline at end of file diff --git a/FusionIIIT/applications/visitor_hostel/views.py b/FusionIIIT/applications/visitor_hostel/views.py index 3419a7d14..ab0711153 100644 --- a/FusionIIIT/applications/visitor_hostel/views.py +++ b/FusionIIIT/applications/visitor_hostel/views.py @@ -25,7 +25,7 @@ import numpy as np from django.contrib.auth.models import User -# from .forms import InventoryForm +from .forms import InventoryForm # for notifications from notification.views import visitors_hostel_notif @@ -61,14 +61,10 @@ def visitorhostel(request): # bookings for intender view if (user_designation == "Intender"): - all_bookings = BookingDetail.objects.select_related( - 'intender', 'caretaker').all().order_by('booking_from') - pending_bookings = BookingDetail.objects.select_related('intender', 'caretaker').filter(Q(status="Pending") | Q( - status="Forward"), booking_to__gte=datetime.datetime.today(), intender=user).order_by('booking_from') - active_bookings = BookingDetail.objects.select_related('intender', 'caretaker').filter( - status="CheckedIn", booking_to__gte=datetime.datetime.today(), intender=user).order_by('booking_from') - dashboard_bookings = BookingDetail.objects.select_related('intender', 'caretaker').filter(Q(status="Pending") | Q(status="Forward") | Q( - status="Confirmed") | Q(status='Rejected'), booking_to__gte=datetime.datetime.today(), intender=user).order_by('booking_from') + all_bookings = BookingDetail.objects.select_related('intender','caretaker').all().order_by('booking_from') + pending_bookings = BookingDetail.objects.select_related('intender','caretaker').filter(Q(status="Pending") | Q(status="Forward"), booking_to__gte=datetime.datetime.today(), intender=user).order_by('booking_from') + active_bookings = BookingDetail.objects.select_related('intender','caretaker').filter(status="CheckedIn", booking_to__gte=datetime.datetime.today(), intender=user).order_by('booking_from') + dashboard_bookings = BookingDetail.objects.select_related('intender','caretaker').filter(Q(status = "Pending") | Q(status="Forward") | Q(status = "Confirmed") | Q(status = 'Rejected'), booking_to__gte=datetime.datetime.today(), intender=user).order_by('booking_from') # print(dashboard_bookings.booking_from) visitors = {} @@ -82,55 +78,43 @@ def visitorhostel(request): temp2 = range(1, booking.number_of_rooms_alloted) rooms[booking.id] = temp2 - complete_bookings = BookingDetail.objects.select_related('intender', 'caretaker').filter( - check_out__lt=datetime.datetime.today(), intender=user).order_by('booking_from').reverse() - canceled_bookings = BookingDetail.objects.select_related('intender', 'caretaker').filter( - status="Canceled", intender=user).order_by('booking_from') - rejected_bookings = BookingDetail.objects.select_related('intender', 'caretaker').filter( - status='Rejected', intender=user).order_by('booking_from') - cancel_booking_requested = BookingDetail.objects.select_related('intender', 'caretaker').filter( - status='CancelRequested', intender=user).order_by('booking_from') + complete_bookings = BookingDetail.objects.select_related('intender','caretaker').filter(booking_to__lt=datetime.datetime.today(), intender=user).order_by('booking_from') + canceled_bookings = BookingDetail.objects.select_related('intender','caretaker').filter(status="Canceled", intender=user).order_by('booking_from') + rejected_bookings = BookingDetail.objects.select_related('intender','caretaker').filter(status='Rejected', intender=user).order_by('booking_from') + cancel_booking_requested = BookingDetail.objects.select_related('intender','caretaker').filter(status='CancelRequested', intender=user).order_by('booking_from') + else: # booking for caretaker and incharge view - all_bookings = BookingDetail.objects.select_related( - 'intender', 'caretaker').all().order_by('booking_from') - pending_bookings = BookingDetail.objects.select_related('intender', 'caretaker').filter( - Q(status="Pending") | Q(status="Forward"), booking_to__gte=datetime.datetime.today()).order_by('booking_from') - active_bookings = BookingDetail.objects.filter(Q(status="Confirmed") | Q( - status="CheckedIn"), booking_to__gte=datetime.datetime.today()).select_related('intender', 'caretaker').order_by('booking_from') - cancel_booking_request = BookingDetail.objects.select_related('intender', 'caretaker').filter( - status="CancelRequested", booking_to__gte=datetime.datetime.today()).order_by('booking_from') - dashboard_bookings = BookingDetail.objects.select_related('intender', 'caretaker').filter(Q(status="Pending") | Q( - status="Forward") | Q(status="Confirmed"), booking_to__gte=datetime.datetime.today()).order_by('booking_from') - print(dashboard_bookings) + all_bookings = BookingDetail.objects.select_related('intender','caretaker').all().order_by('booking_from') + pending_bookings = BookingDetail.objects.select_related('intender','caretaker').filter(Q(status="Pending") | Q(status="Forward"), booking_to__gte=datetime.datetime.today()).order_by('booking_from') + active_bookings = BookingDetail.objects.filter(Q(status="Confirmed") | Q(status="CheckedIn"), booking_to__gte=datetime.datetime.today()).select_related('intender','caretaker').order_by('booking_from') + cancel_booking_request = BookingDetail.objects.select_related('intender','caretaker').filter(status="CancelRequested", booking_to__gte=datetime.datetime.today()).order_by('booking_from') + dashboard_bookings = BookingDetail.objects.select_related('intender','caretaker').filter(Q(status = "Pending") | Q(status="Forward") | Q(status = "Confirmed"), booking_to__gte=datetime.datetime.today()).order_by('booking_from') visitors = {} rooms = {} # x = BookingDetail.objects.all().annotate(rooms_count=Count('rooms')) - c_bookings = BookingDetail.objects.select_related('intender', 'caretaker').filter( - Q(status="Forward"), booking_to__gte=datetime.datetime.today()).order_by('booking_from') - + c_bookings = BookingDetail.objects.select_related('intender','caretaker').filter(Q(status="Forward"), booking_to__gte=datetime.datetime.today()).order_by('booking_from') + # number of visitors for booking in active_bookings: temp = range(2, booking.person_count + 1) visitors[booking.id] = temp + # rooms alloted to booking for booking in active_bookings: for room_no in booking.rooms.all(): temp2 = range(2, booking.number_of_rooms_alloted + 1) rooms[booking.id] = temp2 - # print(booking.rooms.all()) + #print(booking.rooms.all()) + - complete_bookings = BookingDetail.objects.select_related('intender', 'caretaker').filter(Q(status="Canceled") | Q( - status="Complete"), check_out__lt=datetime.datetime.today()).select_related().order_by('booking_from').reverse() - canceled_bookings = BookingDetail.objects.filter(status="Canceled").select_related( - 'intender', 'caretaker').order_by('booking_from') - cancel_booking_requested = BookingDetail.objects.select_related('intender', 'caretaker').filter( - status='CancelRequested', booking_to__gte=datetime.datetime.today(), intender=user).order_by('booking_from') - rejected_bookings = BookingDetail.objects.select_related( - 'intender', 'caretaker').filter(status='Rejected').order_by('booking_from') + complete_bookings = BookingDetail.objects.select_related('intender','caretaker').filter(Q(status="Canceled") | Q(status="Complete"), booking_to__lt=datetime.datetime.today()).select_related().order_by('booking_from') + canceled_bookings = BookingDetail.objects.filter(status="Canceled").select_related('intender','caretaker').order_by('booking_from') + cancel_booking_requested = BookingDetail.objects.select_related('intender','caretaker').filter(status='CancelRequested', booking_to__gte=datetime.datetime.today(), intender=user).order_by('booking_from') + rejected_bookings = BookingDetail.objects.select_related('intender','caretaker').filter(status='Rejected').order_by('booking_from') # finding available room list for alloting rooms for booking in pending_bookings: @@ -138,18 +122,17 @@ def visitorhostel(request): booking_to = booking.booking_to temp1 = booking_details(booking_from, booking_to) available_rooms[booking.id] = temp1 - + # forwarded rooms details for booking in c_bookings: booking_from = booking.booking_from booking_to = booking.booking_to temp2 = forwarded_booking_details(booking_from, booking_to) forwarded_rooms[booking.id] = temp2 - # print(available_rooms) - # print(forwarded_rooms) # inventory data inventory = Inventory.objects.all() inventory_bill = InventoryBill.objects.select_related('item_name').all() + # completed booking bills completed_booking_bills = {} @@ -157,8 +140,7 @@ def visitorhostel(request): current_balance = 0 for bill in all_bills: - completed_booking_bills[bill.id] = {'intender': str(bill.booking.intender), 'booking_from': str( - bill.booking.booking_from), 'booking_to': str(bill.booking.booking_to), 'total_bill': str(bill.meal_bill + bill.room_bill), 'bill_date': str(bill.bill_date)} + completed_booking_bills[bill.id] = {'intender': str(bill.booking.intender), 'booking_from': str(bill.booking.booking_from), 'booking_to': str(bill.booking.booking_to), 'total_bill': str(bill.meal_bill + bill.room_bill)} current_balance = current_balance+bill.meal_bill + bill.room_bill for inv_bill in inventory_bill: @@ -184,7 +166,7 @@ def visitorhostel(request): category = booking.visitor_category person = booking.person_count - room_bill = 100 + room_bill = 0 if days == 0: days = 1 @@ -211,23 +193,25 @@ def visitorhostel(request): mess_bill = 0 for visitor in booking.visitor.all(): - meal = MealRecord.objects.select_related( - 'booking__intender', 'booking__caretaker', 'visitor').filter(booking_id=booking.id) + meal = MealRecord.objects.select_related('booking__intender','booking__caretaker','visitor','room').filter(visitor=visitor) mess_bill1 = 0 for m in meal: - if m.morning_tea != 0: - mess_bill1 = mess_bill1+m.morning_tea*10 - if m.eve_tea != 0: - mess_bill1 = mess_bill1+m.eve_tea*10 - if m.breakfast != 0: - mess_bill1 = mess_bill1+m.breakfast*50 - if m.lunch != 0: - mess_bill1 = mess_bill1+m.lunch*100 - if m.dinner != 0: - mess_bill1 = mess_bill1+m.dinner*100 - - mess_bill = mess_bill + mess_bill1 + if m.morning_tea == True: + mess_bill1 = mess_bill1+10 + if m.eve_tea == True: + mess_bill1 = mess_bill1+10 + if m.breakfast == True: + mess_bill1 = mess_bill1+50 + if m.lunch == True: + mess_bill1 = mess_bill1+100 + if m.dinner == True: + mess_bill1 = mess_bill1+100 + + if mess_bill1 == 270: + mess_bill = mess_bill+225 + else: + mess_bill = mess_bill + mess_bill1 total_bill = mess_bill + room_bill @@ -239,7 +223,7 @@ def visitorhostel(request): visitor_list = [] for b in dashboard_bookings: - count = 1 + count=1 b_visitor_list = b.visitor.all() for v in b_visitor_list: if count == 1: @@ -252,7 +236,7 @@ def visitorhostel(request): 'pending_bookings': pending_bookings, 'active_bookings': active_bookings, 'canceled_bookings': canceled_bookings, - 'dashboard_bookings': dashboard_bookings, + 'dashboard_bookings' : dashboard_bookings, 'bills': bills, # 'all_rooms_status' : all_rooms_status, @@ -267,7 +251,7 @@ def visitorhostel(request): 'intenders': intenders, 'user': user, 'visitors': visitors, - 'rooms': rooms, + 'rooms' : rooms, # 'num_rooms' : list(range(1, booking.number_of_rooms_alloted+1)), # 'num_rooms' :list(range(1, booking.number_of_rooms_alloted+1)), 'previous_visitors': previous_visitors, @@ -275,17 +259,15 @@ def visitorhostel(request): 'current_balance': current_balance, 'rejected_bookings': rejected_bookings, 'cancel_booking_request': cancel_booking_request, - 'cancel_booking_requested': cancel_booking_requested, + 'cancel_booking_requested' : cancel_booking_requested, 'user_designation': user_designation}) # Get methods for bookings - @login_required(login_url='/accounts/login/') def get_booking_requests(request): if request.method == 'POST': - pending_bookings = BookingDetail.objects.select_related( - 'intender', 'caretaker').filter(status="Pending") + pending_bookings = BookingDetail.objects.select_related('intender','caretaker').filter(status="Pending") return render(request, "vhModule/visitorhostel.html", {'pending_bookings': pending_bookings}) else: @@ -293,12 +275,10 @@ def get_booking_requests(request): # getting active bookings - @login_required(login_url='/accounts/login/') def get_active_bookings(request): if request.method == 'POST': - active_bookings = BookingDetail.objects.select_related( - 'intender', 'caretaker').filter(status="Confirmed") + active_bookings = BookingDetail.objects.select_related('intender','caretaker').filter(status="Confirmed") return render(request, "vhModule/visitorhostel.html", {'active_bookings': active_bookings}) else: @@ -307,8 +287,8 @@ def get_active_bookings(request): @login_required(login_url='/accounts/login/') def get_inactive_bookings(request): - if request.method == 'POST': - inactive_bookings = BookingDetail.objects.select_related('intender', 'caretaker').filter( + if request.method == 'POST' : + inactive_bookings = BookingDetail.objects.select_related('intender','caretaker').filter( Q(status="Cancelled") | Q(status="Rejected") | Q(status="Complete")) return render(request, "vhModule/visitorhostel.html", {'inactive_bookings': inactive_bookings}) @@ -328,27 +308,26 @@ def get_booking_form(request): # request booking form action view starts here - @login_required(login_url='/accounts/login/') def request_booking(request): if request.method == 'POST': - flag = 0 + flag=0 # getting details from request form intender = request.POST.get('intender') user = User.objects.get(id=intender) print("jiihuhhih") print(user) - booking_id = request.POST.get('booking-id') + booking_id = request.POST.get('booking-id') category = request.POST.get('category') person_count = request.POST.get('number-of-people') bookingObject = [] - # if person_count and (int(person_count)<20): - # person_count = person_count + #if person_count and (int(person_count)<20): + # person_count = person_count - # else: - # flag = 1 # for error + #else: + # flag = 1 # for error # person_count = 1 purpose_of_visit = request.POST.get('purpose-of-visit') @@ -356,8 +335,7 @@ def request_booking(request): booking_to = request.POST.get('booking_to') booking_from_time = request.POST.get('booking_from_time') booking_to_time = request.POST.get('booking_to_time') - remarks_during_booking_request = request.POST.get( - 'remarks_during_booking_request') + remarks_during_booking_request = request.POST.get('remarks_during_booking_request') bill_to_be_settled_by = request.POST.get('bill_settlement') number_of_rooms = request.POST.get('number-of-rooms') caretaker = 'shailesh' @@ -366,28 +344,27 @@ def request_booking(request): # flag=1 # if flag ==0: - # print(sys.getsizeof(booking_from_time)) - # print(sys.getsizeof(booking_from)) - # print(sys.getsizeof(purpose_of_visit)) - # print(sys.getsizeof(bill_to_be_settled_by)) - # print("gfcfhcghv") + print(sys.getsizeof(booking_from_time)) + print(sys.getsizeof(booking_from)) + print(sys.getsizeof(purpose_of_visit)) + print(sys.getsizeof(bill_to_be_settled_by)) + care_taker = HoldsDesignation.objects.select_related('user','working','designation').filter(designation__name = "VhCaretaker") - care_taker = care_taker[0] - # print(care_taker,"care_taker") + care_taker = care_taker[1] care_taker = care_taker.user bookingObject = BookingDetail.objects.create( - caretaker=care_taker, - purpose=purpose_of_visit, - intender=user, - booking_from=booking_from, - booking_to=booking_to, - visitor_category=category, - person_count=person_count, - arrival_time=booking_from_time, - departure_time=booking_to_time, - # remark=remarks_during_booking_request, - number_of_rooms=number_of_rooms, - bill_to_be_settled_by=bill_to_be_settled_by) + caretaker = care_taker, + purpose=purpose_of_visit, + intender=user, + booking_from=booking_from, + booking_to=booking_to, + visitor_category=category, + person_count=person_count, + arrival_time=booking_from_time, + departure_time=booking_to_time, + #remark=remarks_during_booking_request, + number_of_rooms=number_of_rooms, + bill_to_be_settled_by=bill_to_be_settled_by) # visitor_hostel_caretaker_notif(request.user,care_taker,"Submitted") # print (bookingObject) # print("Hello") @@ -405,11 +382,10 @@ def request_booking(request): # in case of any attachment doc = request.FILES.get('files-during-booking-request') - remark = remarks_during_booking_request, + remark=remarks_during_booking_request, if doc: print("hello") - filename, file_extenstion = os.path.splitext( - request.FILES.get('files-during-booking-request').booking_id) + filename, file_extenstion = os.path.splitext(request.FILES.get('files-during-booking-request').booking_id) filename = booking_id full_path = settings.MEDIA_ROOT + "/VhImage/" url = settings.MEDIA_URL + filename + file_extenstion @@ -423,7 +399,7 @@ def request_booking(request): bookingObject.image = uploaded_file_url bookingObject.save() - # visitor datails from place request form + # visitor datails from place request form visitor_name = request.POST.get('name') visitor_phone = request.POST.get('phone') @@ -436,8 +412,9 @@ def request_booking(request): visitor_organization = ' ' visitor = VisitorDetail.objects.create( - visitor_phone=visitor_phone, visitor_name=visitor_name, visitor_email=visitor_email, visitor_address=visitor_address, visitor_organization=visitor_organization, nationality=visitor_nationality - ) + visitor_phone=visitor_phone, visitor_name=visitor_name, visitor_email=visitor_email, visitor_address=visitor_address, visitor_organization=visitor_organization + , nationality=visitor_nationality + ) # try: # bd = BookingDetail.objects.get(id=booking_id) @@ -446,13 +423,13 @@ def request_booking(request): bookingObject.save() # except: - # print("exception occured") - # return HttpResponse('/visitorhostel/') + #print("exception occured") + # return HttpResponse('/visitorhostel/') # for sending notification of booking request to caretaker - # caretaker_name = HoldsDesignation.objects.select_related('user','working','designation').get(designation__name = "VhCaretaker") - # visitors_hostel_notif(request.user, care_taker.user, 'booking_request') + #caretaker_name = HoldsDesignation.objects.select_related('user','working','designation').get(designation__name = "VhCaretaker") + #visitors_hostel_notif(request.user, care_taker.user, 'booking_request') return HttpResponseRedirect('/visitorhostel/') else: @@ -482,33 +459,30 @@ def update_booking(request): number_of_rooms = request.POST.get('number-of-rooms') # remark = request.POST.get('remark') - booking = BookingDetail.objects.select_related( - 'intender', 'caretaker').get(id=booking_id) + booking = BookingDetail.objects.select_related('intender','caretaker').get(id=booking_id) booking.person_count = person_count booking.number_of_rooms = number_of_rooms booking.booking_from = booking_from booking.booking_to = booking_to booking.purpose = purpose_of_visit booking.save() - forwarded_rooms = {} + # BookingDetail.objects.filter(id=booking_id).update(person_count=person_count, # purpose=purpose_of_visit, # booking_from=booking_from, # booking_to=booking_to, # number_of_rooms=number_of_rooms) - booking = BookingDetail.objects.select_related( - 'intender', 'caretaker').get(id=booking_id) - c_bookings = BookingDetail.objects.select_related('intender', 'caretaker').filter( - Q(status="Forward"), booking_to__gte=datetime.datetime.today()).order_by('booking_from') + booking = BookingDetail.objects.select_related('intender','caretaker').get(id=booking_id) + c_bookings = BookingDetail.objects.select_related('intender','caretaker').filter(Q(status="Forward"), booking_to__gte=datetime.datetime.today()).order_by('booking_from') for booking in c_bookings: booking_from = booking.booking_from booking_to = booking.booking_to temp2 = forwarded_booking_details(booking_from, booking_to) forwarded_rooms[booking.id] = temp2 return render(request, "visitorhostel/", - { - 'forwarded_rooms': forwarded_rooms}) - + { + 'forwarded_rooms': forwarded_rooms}) + else: return HttpResponseRedirect('/visitorhostel/') @@ -528,11 +502,9 @@ def confirm_booking(request): # rooms list rooms = request.POST.getlist('rooms[]') - # print(rooms) - booking = BookingDetail.objects.select_related( - 'intender', 'caretaker').get(id=booking_id) - bd = BookingDetail.objects.select_related( - 'intender', 'caretaker').get(id=booking_id) + #print(rooms) + booking = BookingDetail.objects.select_related('intender','caretaker').get(id=booking_id) + bd = BookingDetail.objects.select_related('intender','caretaker').get(id=booking_id) bd.status = 'Confirmed' bd.category = category @@ -541,9 +513,8 @@ def confirm_booking(request): bd.rooms.add(room_object) bd.save() - # notification of booking confirmation - visitors_hostel_notif(request.user, bd.intender, - 'booking_confirmation') + # notification of booking confirmation + visitors_hostel_notif(request.user, bd.intender, 'booking_confirmation') return HttpResponseRedirect('/visitorhostel/') else: return HttpResponseRedirect('/visitorhostel/') @@ -557,51 +528,42 @@ def cancel_booking(request): booking_id = request.POST.get('booking-id') remark = request.POST.get('remark') charges = request.POST.get('charges') - BookingDetail.objects.select_related('intender', 'caretaker').filter(id=booking_id).update( + BookingDetail.objects.select_related('intender','caretaker').filter(id=booking_id).update( status='Canceled', remark=remark) - booking = BookingDetail.objects.select_related( - 'intender', 'caretaker').get(id=booking_id) + booking = BookingDetail.objects.select_related('intender','caretaker').get(id=booking_id) # if no applicable charges then set charges to zero x = 0 if charges: - Bill.objects.create(booking=booking, meal_bill=x, room_bill=int( - charges), caretaker=user, payment_status=True) + Bill.objects.create(booking=booking, meal_bill=x, room_bill=int(charges), caretaker=user, payment_status=True) else: - Bill.objects.create(booking=booking, meal_bill=x, - room_bill=x, caretaker=user, payment_status=True) - - complete_bookings = BookingDetail.objects.filter(Q(status="Canceled") | Q( - status="Complete"), booking_to__lt=datetime.datetime.today()).select_related('intender', 'caretaker').order_by('booking_from') + Bill.objects.create(booking=booking, meal_bill=x, room_bill=x, caretaker=user, payment_status=True) + complete_bookings = BookingDetail.objects.filter(Q(status="Canceled") | Q(status="Complete"), booking_to__lt=datetime.datetime.today()).select_related('intender','caretaker').order_by('booking_from') + + # to notify the intender that his cancellation request has been confirmed - visitors_hostel_notif(request.user, booking.intender, - 'booking_cancellation_request_accepted') + visitors_hostel_notif(request.user, booking.intender, 'booking_cancellation_request_accepted') return HttpResponseRedirect('/visitorhostel/') else: return HttpResponseRedirect('/visitorhostel/') # cancel confirmed booing by intender - @login_required(login_url='/accounts/login/') def cancel_booking_request(request): if request.method == 'POST': - intender = request.user.holds_designations.filter( - designation__name='VhIncharge') + intender = request.user.holds_designations.filter(designation__name = 'VhIncharge') booking_id = request.POST.get('booking-id') remark = request.POST.get('remark') - BookingDetail.objects.select_related('intender', 'caretaker').filter( - id=booking_id).update(status='CancelRequested', remark=remark) + BookingDetail.objects.select_related('intender','caretaker').filter(id=booking_id).update(status='CancelRequested', remark=remark) - incharge_name = HoldsDesignation.objects.select_related( - 'user', 'working', 'designation').filter(designation__name="VhIncharge")[1] + incharge_name = HoldsDesignation.objects.select_related('user','working','designation').filter(designation__name = "VhIncharge")[1] # to notify the VhIncharge about a new cancelltaion request - visitors_hostel_notif( - request.user, incharge_name.user, 'cancellation_request_placed') + visitors_hostel_notif(request.user, incharge_name.user, 'cancellation_request_placed') return HttpResponseRedirect('/visitorhostel/') else: return HttpResponseRedirect('/visitorhostel/') @@ -614,19 +576,18 @@ def reject_booking(request): if request.method == 'POST': booking_id = request.POST.get('booking-id') remark = request.POST.get('remark') - BookingDetail.objects.select_related('intender', 'caretaker').filter(id=booking_id).update( + BookingDetail.objects.select_related('intender','caretaker').filter(id=booking_id).update( status="Rejected", remark=remark) # to notify the intender that his request has been rejected - # visitors_hostel_notif(request.user, booking.intender, 'booking_rejected') + #visitors_hostel_notif(request.user, booking.intender, 'booking_rejected') return HttpResponseRedirect('/visitorhostel/') else: return HttpResponseRedirect('/visitorhostel/') # Guest check in view - @login_required(login_url='/accounts/login/') def check_in(request): if request.method == 'POST': @@ -641,8 +602,7 @@ def check_in(request): visitor = VisitorDetail.objects.create( visitor_phone=visitor_phone, visitor_name=visitor_name, visitor_email=visitor_email, visitor_address=visitor_address) try: - bd = BookingDetail.objects.select_related( - 'intender', 'caretaker').get(id=booking_id) + bd = BookingDetail.objects.select_related('intender','caretaker').get(id=booking_id) bd.status = "CheckedIn" bd.check_in = check_in_date bd.visitor.add(visitor) @@ -656,7 +616,6 @@ def check_in(request): # guest check out view - @login_required(login_url='/accounts/login/') def check_out(request): user = get_object_or_404(User, username=request.user.username) @@ -667,14 +626,11 @@ def check_out(request): id = request.POST.get('id') meal_bill = request.POST.get('mess_bill') room_bill = request.POST.get('room_bill') - checkout_date = datetime.date.today() - total_bill = int(meal_bill)+int(room_bill) - BookingDetail.objects.select_related('intender', 'caretaker').filter(id=id).update( + BookingDetail.objects.select_related('intender','caretaker').filter(id=id).update( check_out=datetime.datetime.today(), status="Complete") - booking = BookingDetail.objects.select_related( - 'intender', 'caretaker').get(id=id) + booking = BookingDetail.objects.select_related('intender','caretaker').get(id=id) Bill.objects.create(booking=booking, meal_bill=int(meal_bill), room_bill=int( - room_bill), caretaker=user, payment_status=True, bill_date=checkout_date) + room_bill), caretaker=user, payment_status=True) # for visitors in visitor_info: @@ -706,6 +662,7 @@ def check_out(request): return HttpResponseRedirect('/visitorhostel/') + @login_required(login_url='/accounts/login/') def record_meal(request): user = get_object_or_404(User, username=request.user.username) @@ -716,40 +673,58 @@ def record_meal(request): id = request.POST.get('pk') booking_id = request.POST.get('booking') - booking = BookingDetail.objects.select_related( - 'intender', 'caretaker').get(id=booking_id) + booking = BookingDetail.objects.select_related('intender','caretaker').get(id=booking_id) visitor = VisitorDetail.objects.get(id=id) date_1 = datetime.datetime.today() - print(id, booking_id, booking, visitor, date_1) - m_tea = request.POST.get("m_tea") - breakfast = request.POST.get("breakfast") - lunch = request.POST.get("lunch") - eve_tea = request.POST.get("eve_tea") - dinner = request.POST.get("dinner") - - person = 1 - print("bid: ", id) - + food = request.POST.getlist('food[]') + if '1' in food: + m_tea = True + else: + m_tea = False + + if '4' in food: + e_tea = True + else: + e_tea = False + + if '2' in food: + breakfast = True + else: + breakfast = False + + if '3' in food: + lunch = True + else: + lunch = False + + if '5' in food: + dinner = True + else: + dinner = False + + if request.POST.get('numberofpeople'): + person = request.POST.get('numberofpeople') + else: + person = 1 + try: - meal = MealRecord.objects.select_related('booking__intender', 'booking__caretaker', 'visitor').get( + meal = MealRecord.objects.select_related('booking__intender','booking__caretaker','visitor','room').get( visitor=visitor, booking=booking, meal_date=date_1) except: meal = False if meal: - meal.morning_tea += int(m_tea) - meal.eve_tea += int(eve_tea) - meal.breakfast += int(breakfast) - meal.lunch += int(lunch) - meal.dinner += int(dinner) + meal.morning_tea = m_tea + meal.eve_tea = e_tea + meal.breakfast = breakfast + meal.lunch = lunch + meal.dinner = dinner meal.save() - return HttpResponseRedirect('/visitorhostel/') - else: MealRecord.objects.create(visitor=visitor, booking=booking, morning_tea=m_tea, - eve_tea=eve_tea, + eve_tea=e_tea, meal_date=date_1, breakfast=breakfast, lunch=lunch, @@ -762,7 +737,6 @@ def record_meal(request): # generate bill records between date range - @login_required(login_url='/accounts/login/') def bill_generation(request): user = get_object_or_404(User, username=request.user.username) @@ -781,8 +755,7 @@ def bill_generation(request): st = False user = get_object_or_404(User, username=request.user.username) - c = ExtraInfo.objects.select_related( - 'department').filter(user=user) + c = ExtraInfo.objects.select_related('department').filter(user=user) visitor = Visitor.objects.filter(visitor_phone=v_id) visitor = visitor[0] visitor_bill = Visitor_bill.objects.create( @@ -795,7 +768,6 @@ def bill_generation(request): # get available rooms list between date range - @login_required(login_url='/accounts/login/') def room_availabity(request): if request.method == 'POST': @@ -803,7 +775,7 @@ def room_availabity(request): date_2 = request.POST.get('end_date') available_rooms_list = [] available_rooms_bw_dates = booking_details(date_1, date_2) - # print("Available rooms are ") + #print("Available rooms are ") for room in available_rooms_bw_dates: available_rooms_list.append(room.room_number) @@ -819,21 +791,18 @@ def add_to_inventory(request): if request.method == 'POST': item_name = request.POST.get('item_name') bill_number = request.POST.get('bill_number') - quantity = int((request.POST.get('quantity'))) - cost = int(request.POST.get('cost')) + quantity = (request.POST.get('quantity')) + cost = request.POST.get('cost') consumable = request.POST.get('consumable') - if consumable == 'false': - isConsumable = False - else: - isConsumable = True - print(isConsumable) - x = Inventory.objects.create( - item_name=item_name, quantity=quantity, consumable=isConsumable) - print(x.pk) - item = Inventory.objects.get(pk=x.pk) - item_id = item.pk + # if(Inventory.objects.get(item_name = item_name)): + # Inventory.objects.filter(item_name=item_name).update(quantity=quantity,consumable=consumable) + # else: + Inventory.objects.create( + item_name=item_name, quantity=quantity, consumable=consumable) + + item_name_key = Inventory.objects.get(item_name=item_name) InventoryBill.objects.create( - bill_number=bill_number, cost=cost, item_name_id=item_id) + item_name=item_name_key, bill_number=bill_number, cost=cost) return HttpResponseRedirect('/visitorhostel/') else: return HttpResponseRedirect('/visitorhostel/') @@ -843,13 +812,9 @@ def add_to_inventory(request): def update_inventory(request): if request.method == 'POST': id = request.POST.get('id') - quantity = int(request.POST.get('quantity')) - if quantity < 0: - quantity = 1 - if quantity == 0: - Inventory.objects.filter(id=id).delete() - else: - Inventory.objects.filter(id=id).update(quantity=quantity) + quantity = request.POST.get('quantity') + + Inventory.objects.filter(id=id).update(quantity=quantity) return HttpResponseRedirect('/visitorhostel/') else: return HttpResponseRedirect('/visitorhostel/') @@ -867,6 +832,7 @@ def edit_room_status(request): return HttpResponseRedirect('/visitorhostel/') + @login_required(login_url='/accounts/login/') def bill_between_dates(request): if request.method == 'POST': @@ -875,7 +841,7 @@ def bill_between_dates(request): bill_range_bw_dates = bill_range(date_1, date_2) meal_total = 0 room_total = 0 - individual_total = [] + individual_total =[] # calculating room and mess bill booking wise for i in bill_range_bw_dates: @@ -885,22 +851,24 @@ def bill_between_dates(request): total_bill = meal_total + room_total # zip(bill_range_bw_dates, individual_total) return render(request, "vhModule/booking_bw_dates.html", { - # 'booking_bw_dates': bill_range_bw_dates, - 'booking_bw_dates_length': bill_range_bw_dates, - 'meal_total': meal_total, - 'room_total': room_total, - 'total_bill': total_bill, - 'individual_total': individual_total, - 'booking_bw_dates': zip(bill_range_bw_dates, individual_total) - }) + # 'booking_bw_dates': bill_range_bw_dates, + 'booking_bw_dates_length': bill_range_bw_dates, + 'meal_total' : meal_total, + 'room_total' :room_total, + 'total_bill' : total_bill, + 'individual_total' : individual_total, + 'booking_bw_dates': zip(bill_range_bw_dates, individual_total) + }) else: return HttpResponseRedirect('/visitorhostel/') -def bill_range(date1, date2): - bookings = BookingDetail.objects.select_related('intender', 'caretaker').filter(Q(booking_from__lte=date1, booking_to__gte=date1) | Q(booking_from__gte=date1, - booking_to__lte=date2) | Q(booking_from__lte=date2, booking_to__gte=date2) | Q(booking_from__lte=date1, booking_to__gte=date1) | Q(booking_from__gte=date1, booking_to__lte=date2) | Q(booking_from__lte=date2, booking_to__gte=date2)) +def bill_range(date1,date2): + + + bookings = BookingDetail.objects.select_related('intender','caretaker').filter(Q(booking_from__lte=date1, booking_to__gte=date1) | Q(booking_from__gte=date1, + booking_to__lte=date2) | Q(booking_from__lte=date2, booking_to__gte=date2) | Q(booking_from__lte=date1, booking_to__gte=date1) | Q(booking_from__gte=date1, booking_to__lte=date2) | Q(booking_from__lte=date2, booking_to__gte=date2)) # bill_details = Bill.objects.filter(Q(booking__booking_from__lte=date1, booking__booking_to__gte=date1, booking__status="Confirmed") | Q(booking__booking_from__gte=date1, # booking__booking_to__lte=date2, booking__status="Confirmed") | Q(booking__booking_from__lte=date2, booking__booking_to__gte=date2, status="Confirmed") | Q(booking_from__lte=date1, booking__booking_to__gte=date1, status="CheckedIn") | Q(booking__booking_from__gte=date1, booking__booking_to__lte=date2, booking__status="CheckedIn") | Q(booking__booking_from__lte=date2, booking__booking_to__gte=date2, booking__status="CheckedIn")) bookings_bw_dates = [] @@ -911,9 +879,8 @@ def bill_range(date1, date2): all_bill = Bill.objects.select_related('caretaker').all().order_by('-id') for b_id in booking_ids: - if Bill.objects.select_related('caretaker').filter(booking__pk=b_id).exists(): - bill_id = Bill.objects.select_related( - 'caretaker').get(booking__pk=b_id) + if Bill.objects.select_related('caretaker').filter(booking__pk=b_id).exists() : + bill_id = Bill.objects.select_related('caretaker').get(booking__pk=b_id) bookings_bw_dates.append(bill_id) return bookings_bw_dates @@ -921,9 +888,9 @@ def bill_range(date1, date2): def booking_details(date1, date2): - bookings = BookingDetail.objects.select_related('intender', 'caretaker').filter(Q(booking_from__lte=date1, booking_to__gte=date1, status="Confirmed") | Q(booking_from__gte=date1, - booking_to__lte=date2, status="Confirmed") | Q(booking_from__lte=date2, booking_to__gte=date2, status="Confirmed") | Q(booking_from__lte=date1, booking_to__gte=date1, status="Forward") | Q(booking_from__gte=date1, - booking_to__lte=date2, status="Forward") | Q(booking_from__lte=date2, booking_to__gte=date2, status="Forward") | Q(booking_from__lte=date1, booking_to__gte=date1, status="CheckedIn") | Q(booking_from__gte=date1, booking_to__lte=date2, status="CheckedIn") | Q(booking_from__lte=date2, booking_to__gte=date2, status="CheckedIn")) + bookings = BookingDetail.objects.select_related('intender','caretaker').filter(Q(booking_from__lte=date1, booking_to__gte=date1, status="Confirmed") | Q(booking_from__gte=date1, + booking_to__lte=date2, status="Confirmed") | Q(booking_from__lte=date2, booking_to__gte=date2, status="Confirmed") | Q(booking_from__lte=date1, booking_to__gte=date1, status="Forward") | Q(booking_from__gte=date1, + booking_to__lte=date2, status="Forward") | Q(booking_from__lte=date2, booking_to__gte=date2, status="Forward") | Q(booking_from__lte=date1, booking_to__gte=date1, status="CheckedIn") | Q(booking_from__gte=date1, booking_to__lte=date2, status="CheckedIn") | Q(booking_from__lte=date2, booking_to__gte=date2, status="CheckedIn")) booked_rooms = [] for booking in bookings: @@ -940,13 +907,12 @@ def booking_details(date1, date2): # function for finding forwarded booking rooms - def forwarded_booking_details(date1, date2): - bookings = BookingDetail.objects.select_related('intender', 'caretaker').filter(Q(booking_from__lte=date1, booking_to__gte=date1, status="Confirmed") | Q(booking_from__gte=date1, - booking_to__lte=date2, status="Confirmed") | Q(booking_from__lte=date2, booking_to__gte=date2, status="Confirmed") | Q(booking_from__lte=date1, booking_to__gte=date1, status="CheckedIn") | Q(booking_from__gte=date1, booking_to__lte=date2, status="CheckedIn") | Q(booking_from__lte=date2, booking_to__gte=date2, status="CheckedIn")) - forwarded_bookings = BookingDetail.objects.select_related('intender', 'caretaker').filter(Q(booking_from__lte=date1, booking_to__gte=date1, status="Forward") | Q(booking_from__gte=date1, - booking_to__lte=date2, status="Forward") | Q(booking_from__lte=date2, booking_to__gte=date2, status="Forward")) + bookings = BookingDetail.objects.select_related('intender','caretaker').filter(Q(booking_from__lte=date1, booking_to__gte=date1, status="Confirmed") | Q(booking_from__gte=date1, + booking_to__lte=date2, status="Confirmed") | Q(booking_from__lte=date2, booking_to__gte=date2, status="Confirmed") | Q(booking_from__lte=date1, booking_to__gte=date1, status="CheckedIn") | Q(booking_from__gte=date1, booking_to__lte=date2, status="CheckedIn") | Q(booking_from__lte=date2, booking_to__gte=date2, status="CheckedIn")) + forwarded_bookings = BookingDetail.objects.select_related('intender','caretaker').filter(Q(booking_from__lte=date1, booking_to__gte=date1, status="Forward") | Q(booking_from__gte=date1, + booking_to__lte=date2, status="Forward") | Q(booking_from__lte=date2, booking_to__gte=date2, status="Forward") ) booked_rooms = [] # Bookings for rooms which are forwarded but not yet approved @@ -969,14 +935,10 @@ def forward_booking(request): previous_category = request.POST.get('previous_category') modified_category = request.POST.get('modified_category') rooms = request.POST.getlist('rooms[]') - remark = request.POST.get('remark') print(rooms) - BookingDetail.objects.select_related('intender', 'caretaker').filter( - id=booking_id).update(status="Forward", remark=remark) - booking = BookingDetail.objects.select_related( - 'intender', 'caretaker').get(id=booking_id) - bd = BookingDetail.objects.select_related( - 'intender', 'caretaker').get(id=booking_id) + BookingDetail.objects.select_related('intender','caretaker').filter(id=booking_id).update(status="Forward") + booking = BookingDetail.objects.select_related('intender','caretaker').get(id=booking_id) + bd = BookingDetail.objects.select_related('intender','caretaker').get(id=booking_id) bd.modified_visitor_category = modified_category count_rooms = 0 @@ -987,17 +949,14 @@ def forward_booking(request): bd.number_of_rooms_alloted = count_rooms bd.save() - dashboard_bookings = BookingDetail.objects.select_related('intender', 'caretaker').filter(Q(status="Pending") | Q(status="Forward") | Q( - status="Confirmed") | Q(status='Rejected'), booking_to__gte=datetime.datetime.today(), intender=user).order_by('booking_from') + dashboard_bookings = BookingDetail.objects.select_related('intender','caretaker').filter(Q(status = "Pending") | Q(status="Forward") | Q(status = "Confirmed") | Q(status = 'Rejected'), booking_to__gte=datetime.datetime.today(), intender=user).order_by('booking_from') # return render(request, "vhModule/visitorhostel.html", # {'dashboard_bookings' : dashboard_bookings}) - incharge_name = HoldsDesignation.objects.select_related( - 'user', 'working', 'designation').filter(designation__name="VhIncharge")[1] + incharge_name = HoldsDesignation.objects.select_related('user','working','designation').filter(designation__name = "VhIncharge")[1] # notify incharge about forwarded booking - visitors_hostel_notif( - request.user, incharge_name.user, 'booking_forwarded') + visitors_hostel_notif(request.user, incharge_name.user, 'booking_forwarded') return HttpResponseRedirect('/visitorhostel/') else: - return HttpResponseRedirect('/visitorhostel/') + return HttpResponseRedirect('/visitorhostel/') \ No newline at end of file diff --git a/FusionIIIT/manage.py b/FusionIIIT/manage.py index c4bcdcbec..dc64c28ce 100755 --- a/FusionIIIT/manage.py +++ b/FusionIIIT/manage.py @@ -3,7 +3,10 @@ import sys if __name__ == "__main__": - os.environ.setdefault("DJANGO_SETTINGS_MODULE", "Fusion.settings.development") + default_settings = "Fusion.settings.development" + if len(sys.argv) > 1 and sys.argv[1] == "test": + default_settings = "Fusion.settings.test" + os.environ.setdefault("DJANGO_SETTINGS_MODULE", default_settings) from django.core.management import execute_from_command_line diff --git a/FusionIIIT/media/Administrator/academic_information/sheet.xlsx b/FusionIIIT/media/Administrator/academic_information/sheet.xlsx deleted file mode 100644 index 274ab022f..000000000 Binary files a/FusionIIIT/media/Administrator/academic_information/sheet.xlsx and /dev/null differ diff --git a/FusionIIIT/media/Insti_logo.svg b/FusionIIIT/media/Insti_logo.svg deleted file mode 100644 index 8be5c8ca6..000000000 --- a/FusionIIIT/media/Insti_logo.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/FusionIIIT/notification/views.py b/FusionIIIT/notification/views.py index ca732a7b7..0480575b8 100644 --- a/FusionIIIT/notification/views.py +++ b/FusionIIIT/notification/views.py @@ -1,41 +1,39 @@ from django.shortcuts import render -from requests import Response from notifications.signals import notify -# Create your views here. +# Create your views here. def leave_module_notif(sender, recipient, type, date=None): url = 'leave:leave' module = 'Leave Module' - sender = sender - recipient = recipient - verb = '' - if type == 'leave_applied': - verb = "Your leave has been successfully submitted." - elif type == 'request_accepted': + sender=sender + recipient=recipient + verb='' + if type=='leave_applied': + verb="Your leave has been successfully submitted." + elif type=='request_accepted': verb = "Your responsibility has been accepted " - elif type == 'request_declined': + elif type=='request_declined': verb = "Your responsibility has been declined " - elif type == 'leave_accepted': + elif type=='leave_accepted': verb = "You leave request has been accepted " elif type == 'leave_forwarded': verb = "You leave request has been forwarded " - elif type == 'leave_rejected': + elif type=='leave_rejected': verb = "You leave request has been rejected " - elif type == 'offline_leave': + elif type=='offline_leave': verb = "Your offline leave has been updated " - elif type == 'replacement_request': + elif type=='replacement_request': verb = "You have a replacement request " - elif type == 'leave_request': + elif type=='leave_request': verb = "You have a leave request from " - elif type == 'leave_withdrawn': + elif type=='leave_withdrawn': verb = "The leave has been withdrawn for " + date - elif type == 'replacement_cancel': + elif type=='replacement_cancel': verb = "Your replacement has been cancelled for "+date - notify.send(sender=sender, recipient=recipient, - url=url, module=module, verb=verb) + notify.send(sender=sender, recipient=recipient, url=url, module=module, verb=verb) def placement_cell_notif(sender, recipient, type): url = 'placement:placement' @@ -44,30 +42,25 @@ def placement_cell_notif(sender, recipient, type): recipient = recipient verb = '' - notify.send(sender=sender, recipient=recipient, - url=url, module=module, verb=verb) - + notify.send(sender=sender, recipient=recipient, url=url, module=module, verb=verb) def academics_module_notif(sender, recipient, type): - url = '' - module = "Academic's Module" + url='' + module="Academic's Module" sender = sender recipient = recipient verb = type - notify.send(sender=sender, recipient=recipient, - url=url, module=module, verb=verb) - + notify.send(sender=sender, recipient=recipient, url=url, module=module, verb=verb) def office_module_notif(sender, recipient): - url = 'office_module:officeOfRegistrar' - module = "Academic's Module" + url='office_module:officeOfRegistrar' + module="Academic's Module" sender = sender recipient = recipient verb = "New file received" - notify.send(sender=sender, recipient=recipient, - url=url, module=module, verb=verb) + notify.send(sender=sender, recipient=recipient, url=url, module=module, verb=verb) def central_mess_notif(sender, recipient, type, message=None): @@ -76,7 +69,6 @@ def central_mess_notif(sender, recipient, type, message=None): sender = sender recipient = recipient verb = '' - if type == 'feedback_submitted': verb = 'Your feedback has been successfully submitted.' elif type == 'menu_change_accepted': @@ -87,21 +79,13 @@ def central_mess_notif(sender, recipient, type, message=None): verb = 'Your vacation request has been' + message elif type == 'meeting_invitation': verb = message - elif type == 'special_request': + elif type =='special_request': verb = "Your special food request has been " + message elif type == 'added_committee': verb = "You have been added to the mess committee. " notify.send(sender=sender, recipient=recipient, url=url, module=module, verb=verb) - -def placement_cellNotif(sender, recipient, type): - url = 'placement:placement' - module = 'Placement Cell' - sender = sender - recipient = recipient - verb = '' - notify.send(sender=sender, recipient=recipient, url=url, module=module, verb=verb) def visitors_hostel_notif(sender, recipient, type): url='visitorhostel:visitorhostel' @@ -124,60 +108,46 @@ def visitors_hostel_notif(sender, recipient, type): notify.send(sender=sender, recipient=recipient, url=url, module=module, verb=verb) -def healthcare_center_notif(sender, recipient, type, message): +def healthcare_center_notif(sender, recipient, type): url='healthcenter:healthcenter' module='Healthcare Center' sender = sender recipient = recipient verb = '' - flag='' if type == 'appoint': verb = "Your Appointment has been booked" - elif type == 'amb_request': + if type == 'amb_request': verb = "Your Ambulance request has been placed" - elif type == 'presc': + if type == 'Presc': verb = "You have been prescribed some medicine" - elif type == 'appoint_req': + if type == 'appoint_req': verb = "You have a new appointment request" - elif type == 'feedback_submitted': - verb = "Your feedback has been submitted" - elif type == 'feedback_res': - verb = "You have a new feedback" - elif type == 'amb_req': + if type == 'amb_req': verb = "You have a new ambulance request" - elif type == 'new_announce': - verb = message - flag='announcement' - elif type == 'rel_forward': - verb = "You have a new medical relief forward request" - elif type == 'rel_approve': - verb = "You have a new medical relief approval request" - elif type == 'rel_approved': - verb = 'Your medical relief request has been approved' - notify.send(sender=sender, recipient=recipient, url=url, module=module, verb=verb, flag=flag) - -def file_tracking_notif(sender, recipient, title): - url = 'filetracking:inward' - module = 'File Tracking' + + + + notify.send(sender=sender, recipient=recipient, url=url, module=module, verb=verb) + + +def file_tracking_notif(sender, recipient,title): + url='filetracking:inward' + module='File Tracking' sender = sender recipient = recipient verb = title - notify.send(sender=sender, recipient=recipient, - url=url, module=module, verb=verb) - + notify.send(sender=sender, recipient=recipient, url=url, module=module, verb=verb) def scholarship_portal_notif(sender, recipient, type): - url = 'spacs:spacs' - module = 'Scholarship Portal' + url='spacs:spacs' + module='Scholarship Portal' sender = sender recipient = recipient verb = '' if type.startswith('award'): s = type.split('_') - # print("psss") - # print(type, s) verb = "Invitation to apply for " + s[1] elif type == 'Accept_MCM': verb = "Your Mcm form has been accepted " @@ -195,54 +165,51 @@ def scholarship_portal_notif(sender, recipient, type): verb = "Your Convocation form for D&M Proficiency Gold Medal has been accepted " elif type == 'Reject_Silver': verb = "Your Convocation form for D&M Proficiency Gold Medal has been rejected " - notify.send(sender=sender, recipient=recipient, - url=url, module=module, verb=verb) + notify.send(sender=sender, recipient=recipient, url=url, module=module, verb=verb) -def complaint_system_notif(sender, recipient, type, complaint_id, student, message): - if (student == 0): + + +def complaint_system_notif(sender, recipient, type, complaint_id,student,message): + if(student==0): url = ('complaint:detail') else: - url = ('complaint:detail2') - module = 'Complaint System' + url=('complaint:detail2') + module='Complaint System' sender = sender recipient = recipient verb = message description = complaint_id - notify.send(sender=sender, recipient=recipient, url=url, - module=module, verb=verb, description=description) - + notify.send(sender=sender, recipient=recipient, url=url, module=module, verb=verb,description=description) def office_dean_PnD_notif(sender, recipient, type): url = 'office_module:officeOfDeanPnD' module = 'Office of Dean PnD Module' - sender = sender - recipient = recipient - verb = '' - if type == 'requisition_filed': + sender=sender + recipient=recipient + verb='' + if type=='requisition_filed': verb = "Your requisition has been successfully submitted." - elif type == 'request_accepted': + elif type=='request_accepted': verb = "Your requisition has been accepted." - elif type == 'request_rejected': + elif type=='request_rejected': verb = "Your requisition has been rejected." - elif type == 'assignment_created': + elif type=='assignment_created': verb = "Assignment has been created." - elif type == 'assignment_received': + elif type=='assignment_received': verb = "You have received an assignment." - elif type == 'assignment_reverted': + elif type=='assignment_reverted': verb = "Assignment has been reverted." - elif type == 'assignment_approved': + elif type=='assignment_approved': verb = "Assignment has been approved." - elif type == 'assignment_rejected': + elif type=='assignment_rejected': verb = "Assignment has been rejected." - notify.send(sender=sender, recipient=recipient, - url=url, module=module, verb=verb) - + notify.send(sender=sender, recipient=recipient, url=url, module=module, verb=verb) def office_module_DeanS_notif(sender, recipient, type): - url = 'office_module:officeOfDeanStudents' - module = 'Office Module' + url='office_module:officeOfDeanStudents' + module='Office Module' sender = sender recipient = recipient verb = "" @@ -270,8 +237,7 @@ def office_module_DeanS_notif(sender, recipient, type): elif type == 'budget_alloted': verb = "Budget has been alloted by Junior Superintendent" - notify.send(sender=sender, recipient=recipient, - url=url, module=module, verb=verb) + notify.send(sender=sender, recipient=recipient, url=url, module=module, verb=verb) def gymkhana_voting(sender, recipient, type, title, desc): @@ -304,8 +270,7 @@ def gymkhana_session(sender, recipient, type, club, desc, venue): verb = "" if type == 'new_session': - verb = "A session by {} Club will be organised in {}".format( - club, venue) + verb = "A session by {} Club will be organised in {}".format(club, venue) notify.send(sender=sender, recipient=recipient, @@ -325,8 +290,7 @@ def gymkhana_event(sender, recipient, type, club, event_name, desc, venue): verb = "" if type == 'new_event': - verb = "{} event by {} Club will be organised in {}".format( - event_name, club, venue) + verb = "{} event by {} Club will be organised in {}".format(event_name, club, venue) notify.send(sender=sender, recipient=recipient, @@ -336,77 +300,43 @@ def gymkhana_event(sender, recipient, type, club, event_name, desc, venue): description=desc ) - -def AssistantshipClaim_notify(sender, recipient, month, year): - - message = "Your Assistantshipclaim of {} month year {} is approved ".format( - month, year) +def AssistantshipClaim_notify(sender,recipient,month,year): + + message="Your Assistantshipclaim of {} month year {} is approved ".format(month,year) url = 'academic-procedures:academic_procedures' module = 'Assistantship Request' - notify.send(sender=sender, recipient=recipient, - url=url, module=module, verb=message) + notify.send(sender=sender,recipient= recipient, url=url, module=module, verb=message) -def AssistantshipClaim_faculty_notify(sender, recipient): - message = " Assistantshipclaim is requested " +def AssistantshipClaim_faculty_notify(sender,recipient): + + message=" Assistantshipclaim is requested " url = 'academic-procedures:academic_procedures' module = 'Assistantship Request' - notify.send(sender=sender, recipient=recipient, - url=url, module=module, verb=message) + notify.send(sender=sender,recipient= recipient, url=url, module=module, verb=message) -def AssistantshipClaim_acad_notify(sender, recipient): - message = "AssistantshipClaim is requested " +def AssistantshipClaim_acad_notify(sender,recipient): + message = "AssistantshipClaim is requested " url = 'academic-procedures:academic_procedures' module = 'Assistantship Request' - notify.send(sender=sender, recipient=recipient, - url=url, module=module, verb=message) + notify.send(sender=sender,recipient= recipient, url=url, module=module, verb=message) -def AssistantshipClaim_account_notify(sender, stu, recipient): +def AssistantshipClaim_account_notify(sender,stu, recipient): message = "Assistantship claim of{} is forwaded ".format(stu) url = 'academic-procedures:academic_procedures' module = 'Assistantship Request' - notify.send(sender=sender, recipient=recipient, - url=url, module=module, verb=message) - + notify.send(sender=sender,recipient= recipient, url=url, module=module, verb=message) def department_notif(sender, recipient, type): - url = 'dep:dep' - module = 'department' - sender = sender - recipient = recipient - verb = type - flag = "announcement" - - notify.send(sender=sender, - recipient=recipient, - url=url, - module=module, - verb=verb, - flag=flag) -def examination_notif(sender, recipient, type): - url='examination:examination' - module='examination' - sender = sender - recipient = recipient - verb = type - flag = "announcement" - - notify.send(sender=sender, - recipient=recipient, - url=url, - module=module, - verb=verb, - flag=flag) -def examination_notif(sender, recipient, type,request): - url='examination:examination' - module='examination' + url='dep:dep' + module='department' sender = sender recipient = recipient verb = type - flag = "examination" + flag = "department" notify.send(sender=sender, recipient=recipient, @@ -414,15 +344,13 @@ def examination_notif(sender, recipient, type,request): module=module, verb=verb, flag=flag) - print("test3") - # return render(request, 'examination/announcement_req.html') def office_module_DeanRSPC_notif(sender, recipient, type): - url = 'office:officeOfDeanRSPC' - module = 'Office Module' + url='office:officeOfDeanRSPC' + module='Office Module' sender = sender recipient = recipient verb = "" @@ -434,11 +362,11 @@ def office_module_DeanRSPC_notif(sender, recipient, type): elif type == 'Pending': verb = "Kindly wait for the response" - notify.send(sender=sender, recipient=recipient, - url=url, module=module, verb=verb) + + notify.send(sender=sender, recipient=recipient, url=url, module=module, verb=verb) -def research_procedures_notif(sender, recipient, type): +def research_procedures_notif(sender,recipient,type): url = 'research_procedures:patent_registration' module = 'Research Procedures' sender = sender @@ -456,125 +384,4 @@ def research_procedures_notif(sender, recipient, type): elif type == "created": verb = "A new Patent has been Created" - notify.send(sender=sender,recipient=recipient,url=url,module=module,verb=verb) - -def hostel_notifications(sender, recipient, type): - url = 'hostelmanagement:hostel_view' - module = 'Hostel Management' - - sender = sender - recipient = recipient - verb = "" - if type == "leave_accept": - verb = "Your leave request has been Accepted." - elif type == "leave_reject": - verb = "Your leave request has been Rejected." - elif type == "guestRoom_accept": - verb = "Your Guest Room request has been Accepted." - elif type == "guestRoom_reject": - verb = "Your Guest Room request has been Rejected." - elif type == "leave_request": - verb = "You have a new Leave Request." - elif type == "guestRoom_request": - verb = "You have a new Guest Room Request." - elif type == "fine_imposed": - verb = "A fine has been imposed on you." - - notify.send(sender=sender,recipient=recipient,url=url,module=module,verb=verb) - -def prog_and_curr_notif(sender, recipient,title): - url='programme_curriculum:inward_files' - module='Programme and Curriculum' - sender = sender - recipient = recipient - verb = title - notify.send(sender=sender, recipient=recipient, url=url, module=module, verb=verb) - -def course_management_notif(sender, recipient, type, course, course_name, course_code, flag): - url = 'online_cms:course' - module="Course Management" - sender = sender - recipient = recipient - verb = type - - notify.send(sender=sender, recipient=recipient, url=url, module=module, verb=verb, flag=flag, course_code=course_code, course=course, cname = course_name) - - -def otheracademic_notif(sender, recipient, type, otheracademic_id,student,message): - if(type=='ug_leave_hod'): - url = ('otheracademic:otheracademic') - elif type=='pg_leave_ta' : - url = ('otheracademic:leaveApproveTA') - elif type=='pg_leave_hod' : - url = ('otheracademic:otheracademic') - elif type=='ast_ta' : - url = ('otheracademic:assistantship_form_approval') - elif type=='ast_thesis' : - url = ('otheracademic:assistantship_thesis') - - elif type=='ast_acadadmin' : - url = ('otheracademic:assistantship_acad_approveform') - elif type=='ast_hod' : - url = ('otheracademic:assistantship_hod') - elif type=='hostel_nodues' : - url = ('otheracademic:hostel_nodues') - elif type=='bank_nodues' : - url = ('otheracademic:Bank_nodues') - elif type=='btp_nodues' : - url = ('otheracademic:BTP_nodues') - elif type=='cse_nodues' : - url = ('otheracademic:CSE_nodues') - elif type=='design_nodues' : - url = ('otheracademic:Design_nodues') - elif type=='acad_nodues' : - url = ('otheracademic:dsa_nodues') - elif type=='ece_nodues' : - url = ('otheracademic:Ece_nodues') - elif type=='library_nodues' : - url = ('otheracademic:library_nodues') - elif type=='mess_nodues' : - url = ('otheracademic:mess_nodues') - elif type=='physics_nodues' : - url = ('otheracademic:Physics_nodues') - elif type=='discipline_nodues' : - url = ('otheracademic:discipline_nodues') - elif type=='me_nodues' : - url = ('otheracademic:ME_nodues') - elif type=="ug_leave_hod_approve": - url = ('otheracademic:leaveStatus') - elif type=="bonafide_acadadmin": - url = ('otheracademic:bonafideApproveForm') - elif type=="bonafide_accept": - url = ('otheracademic:bonafideStatus') - elif type=="ast_ta_accept": - url = ('otheracademic:assistantship_status') - elif type=="nodues_status": - url = ('otheracademic:nodues_status') - elif type=="pg_leave_ta_approve": - url = ('otheracademic:leaveStatusPG') - elif type=="pg_leave_thesis": - url = ('otheracademic:leaveApproveThesis') - else: - url=('otheracademic:otheracademic') - - - module='otheracademic' - sender = sender - recipient = recipient - verb = message - description = otheracademic_id - - notify.send(sender=sender, recipient=recipient, url=url, module=module, verb=verb,description=description) -def iwd_notif(sender,recipient,type): - module= 'iwdModuleV2' - url= 'iwdModuleV2:iwdModuleV2' - verb="" - if type == "file_forward": - verb= "file forwarded from " + sender.username+"." - if type == "Request_added": - verb= "Request added by "+ sender.username + "." - if type == "Request_approved": - verb = "Request approved by " + sender.username + "." - if type == "Request_rejected": - verb = "Request rejected by " + sender.username + "." notify.send(sender=sender,recipient=recipient,url=url,module=module,verb=verb) \ No newline at end of file diff --git a/FusionIIIT/templates/academic_procedures/academic.html b/FusionIIIT/templates/academic_procedures/academic.html index 8b289a460..6b04a54fb 100644 --- a/FusionIIIT/templates/academic_procedures/academic.html +++ b/FusionIIIT/templates/academic_procedures/academic.html @@ -22,39 +22,6 @@ } - - {% endblock css %} @@ -63,12 +30,13 @@ {% include 'dashboard/navbar.html' %} {% endblock %} -
    +
    - + {% comment %}The left-margin segment!{% endcomment %} +
    {% comment %}The left-rail segment starts here!{% endcomment %} -
    +
    {% comment %}The user image card starts here!{% endcomment %} {% block usercard %} {% include 'globals/usercard.html' %} @@ -78,8 +46,8 @@
    {% comment %}The Tab-Menu starts here!{% endcomment %} -
    - - - - - - - - - - - - - - - - - - {% for course in assigned_courses %} - - - - - - - - - - - - - {%endfor%} - -

    Course Name

    Course Code

    Course Version

    Working Year

    Semester No

    Download Roll List

    {{course.course_id.name }} {{course.course_id.code }} {{course.course_id.version }} {{course.year }} {{course.semester_no }} -
    - {% csrf_token %} - - - -
    -
    - - - - - +
    {% comment %}The right-rail segment ends here!{% endcomment %} @@ -243,4 +181,4 @@ -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/FusionIIIT/templates/academic_procedures/addCourse.html b/FusionIIIT/templates/academic_procedures/addCourse.html index 42f38a05d..c58b1ac45 100755 --- a/FusionIIIT/templates/academic_procedures/addCourse.html +++ b/FusionIIIT/templates/academic_procedures/addCourse.html @@ -11,7 +11,7 @@
    {% if adc_date_flag %} - {% if details.cpi >= 0.0 %} + {% if details.cpi >= 6.0 %} {% if add_courses_options %}
    {% csrf_token %} diff --git a/FusionIIIT/templates/academic_procedures/allot_courses.html b/FusionIIIT/templates/academic_procedures/allot_courses.html index 62855b783..5d4fc0e45 100644 --- a/FusionIIIT/templates/academic_procedures/allot_courses.html +++ b/FusionIIIT/templates/academic_procedures/allot_courses.html @@ -27,7 +27,7 @@

    @@ -54,11 +54,6 @@

    8

    -
    - - - -
    - {% for message in messages %} -

    - {{ message }} -

    - {% endfor %} - -{% endif %} - -
    - - - -
    - - - {% csrf_token %} -
    -

    Note : Provide the data in Excel Sheet in following format:

    - RollNo | CourseSlot Name | CourseCode -

    Download - the sample Excel, fill the data accordingly and then upload the same

    -
    -
    - - - -
    - -
    - -
    - -
    - -
    - - -
    -{% endblock %} - -{% block javascript %} - -{% endblock %} - - -{% comment %} -Display course slot codes, and their course as per batch and semester -{% endcomment %} \ No newline at end of file diff --git a/FusionIIIT/templates/academic_procedures/auto_finalregister.html b/FusionIIIT/templates/academic_procedures/auto_finalregister.html deleted file mode 100644 index 95788c24d..000000000 --- a/FusionIIIT/templates/academic_procedures/auto_finalregister.html +++ /dev/null @@ -1,251 +0,0 @@ -{% load static %} -{% block finalregister %} - - - - -
    Final Registration
    - -
    - {% if curr_sem.semester_no != 8 %} - {% if frd %} - {% if final_registration_flag == True %} -

    -

    You have already registered for this semester!!!
    -

    - - - {% else %} -
    -
    - {% csrf_token %} -
    - - - - - -
    - -
    -
    - - -
    -
    - -
    -
    -
    - - -
    - -
    - -
    -
    - -
    -
    -
    - - -
    -
    - - -
    -
    -
    - - -
    - -
    -
    - - -
    -
    -
    -
    -
    -
    -
    - - - -
    -
    - -
    -
    - -
    -
    -
    - - - {% comment %}
    - {% csrf_token %} - -

    Fee Payment

    - - - - - -
    {% endcomment %} - {% endif %} - {% else %} -
    -

    Final Registration Date is yet to come

    -
    - {% endif %} - {% else %} -
    -

    You are not Eligible for Registration !!

    -
    - {% endif %} -
    -{% endblock %} - - - - - - - - \ No newline at end of file diff --git a/FusionIIIT/templates/academic_procedures/auto_pre_registration.html b/FusionIIIT/templates/academic_procedures/auto_pre_registration.html deleted file mode 100644 index 614feddd7..000000000 --- a/FusionIIIT/templates/academic_procedures/auto_pre_registration.html +++ /dev/null @@ -1,504 +0,0 @@ - - - - -{% load static %} -{% block resiterstudent %} - - -
    Pre-Registration for Next Semester Courses
    -
    - {% if curr_sem.semester_no != 8 %} - {% if pre_registration_flag %} - -
    You have already registered at - {% if pre_registration_timestamp is not None %} - {{pre_registration_timestamp}} - {% endif %} - !!! -
    -
    View Courses
    -
    Hide courses
    -
    -
    -
    - - - - - - - - - - - - - {% for course_slot,courses in pre_registered_course_show.items %} - - - - - - - - - - - {% endfor %} - - -
    -
    -
    - {% else %} - {% if prd %} -
    - {% csrf_token %} - - - - - - - - - - - - - - - - - {% for course_slot in next_sem_registration_courses %} - {% if course_slot.type == "Swayam" %} - - - - - - - - - - - {% elif course_slot.type == "Open Elective" %} - {% for course in course_slot.courses.all %} - - - - - - - - - - - {% endfor %} - {% else %} - - - - - - - - - - {% if course_slot.type == "Backlog" %} - - {% endif %} - - - - {% endif %} - {% endfor %} - - - - -
    #Slot nameSlot typeSemesterCreditsPrioritySelectRegistration Type
    {{ forloop.counter }}.{{course_slot.name}}{{course_slot.type}}{{next_sem.semester_no}} - - {{course_slot.courses.all.0.credit}} -
    - -
    -
    {{ forloop.parentloop.counter }}.{{ course_slot.name }}{{ course_slot.type }}{{ next_sem.semester_no }} - - {{ course.credit }} {{ forloop.counter }} -
    - -
    -
    {{forloop.counter}}.{{course_slot.name}}{{course_slot.type}}{{next_sem.semester_no}} - - {{course_slot.courses.all.0.credit}} -
    - -
    -
    -
    - -
    -
    -
    -
    - -
    -
    -
    - -
    -
    - {% else %} - {% if prd_start_date%} - -
    Pre Registration will start on {{prd_start_date}}. -
    - {% else %} - -
    Pre Registration date hasn't come yet -
    - {% endif %} - - {% endif %} - {% endif %} - {% else %} -
    -

    You are not Eligible for Registration !!

    -
    - {% endif %} - -
    - - - -{% endblock %} - - - - diff --git a/FusionIIIT/templates/academic_procedures/backlog.html b/FusionIIIT/templates/academic_procedures/backlog.html deleted file mode 100644 index 013310330..000000000 --- a/FusionIIIT/templates/academic_procedures/backlog.html +++ /dev/null @@ -1,77 +0,0 @@ -{% load static %} -
    Backlog Courses
    - diff --git a/FusionIIIT/templates/academic_procedures/course_registration_receipt.html b/FusionIIIT/templates/academic_procedures/course_registration_receipt.html deleted file mode 100644 index 7535c717e..000000000 --- a/FusionIIIT/templates/academic_procedures/course_registration_receipt.html +++ /dev/null @@ -1,204 +0,0 @@ - - - - - - - - - -
    - -
    -
    Student Copy
    -
    -

    Name: {{ name }}

    -

    Roll no.: {{ roll_no }}

    -

    Branch: {{ branch }}

    -

    Batch: {{ batch }}

    -
    - - - - - - - - - - - - - {% for items in current_courseregistrations %} - - - - - - - - {% endfor %} - -
    #Course IDCourse NameCreditsRegistration Type
    {{ forloop.counter }}{{ items.course_id.code }}{{ items.course_id.name }}{{ items.course_id.credit }}{{ items.registration_type }}
    - -
    -

    Additional Notes:

    -

    _____________________________________________________________________________________________________________

    -

    _____________________________________________________________________________________________________________

    -
    - -
    -
    Student's Signature
    -
    Admin's Signature
    -
    -
    - - -
    - - -
    -
    Academic Copy
    -
    -

    Name: {{ name }}

    -

    Roll no.: {{ roll_no }}

    -

    Branch: {{ branch }}

    -

    Batch: {{ batch }}

    -
    - - - - - - - - - - - - - {% for items in current_courseregistrations %} - - - - - - - - {% endfor %} - -
    #Course IDCourse NameCreditsRegistration Type
    {{ forloop.counter }}{{ items.course_id.code }}{{ items.course_id.name }}{{ items.course_id.credit }}{{ items.registration_type }}
    - -
    -

    Additional Notes:

    -

    _____________________________________________________________________________________________________________

    -

    _____________________________________________________________________________________________________________

    -
    - -
    -
    Student's Signature
    -
    Admin's Signature
    -
    -
    -
    - - - diff --git a/FusionIIIT/templates/academic_procedures/current_sem.html b/FusionIIIT/templates/academic_procedures/current_sem.html index 02a18f580..59ed5ab02 100755 --- a/FusionIIIT/templates/academic_procedures/current_sem.html +++ b/FusionIIIT/templates/academic_procedures/current_sem.html @@ -74,39 +74,26 @@
    Course ID
    Course Name
    Credits
    -
    Registration Type
    Semester
    - {% for items in current_courseregistrations %} + {% for items in currently_registered %}
    {{ forloop.counter }} .
    - {{ items.course_id.code }} + {{ items.1.code }} - {{ items.course_id.name}} + {{ items.1.name}} -
    {{ items.course_id.credit }}
    - - -
    {{ items.registration_type }}
    +
    {{ items.1.credit }}
    {{curr_sem.semester_no}}
    - {% endfor %} diff --git a/FusionIIIT/templates/academic_procedures/dropCourse.html b/FusionIIIT/templates/academic_procedures/dropCourse.html index b2ea43e49..a44f365a6 100755 --- a/FusionIIIT/templates/academic_procedures/dropCourse.html +++ b/FusionIIIT/templates/academic_procedures/dropCourse.html @@ -1,9 +1,9 @@ {% load static %} {% block complainthistory %} -
    Drop Course(s)
    +
    Drop Couse(s)
    - - - - -
    -
    - {% if drop_date_flag == True %} - - - - - - - - - - - - - - - - - {% for items in current_courseregistrations %} - - - - - - - - - - {% endfor %} - -
    #
    Course ID
    Course Name
    Credits
    Registration Type
    Semester
    {{ forloop.counter }}.
    - {{ items.course_id.code }} -
    - {{ items.course_id.name}} - -
    {{ items.course_id.credit }}
    -
    -
    {{ items.registration_type }}
    -
    -
    {{curr_sem.semester_no}}
    -
    - - DROP -
    - {% else %} -
    You have to wait for the date
    - {% endif %} -
    -
    - - - - - - - - diff --git a/FusionIIIT/templates/academic_procedures/finalregister.html b/FusionIIIT/templates/academic_procedures/finalregister.html index f6f906da1..2349c5f01 100755 --- a/FusionIIIT/templates/academic_procedures/finalregister.html +++ b/FusionIIIT/templates/academic_procedures/finalregister.html @@ -76,7 +76,6 @@ Course Name Credits Semester - Registration Type @@ -93,7 +92,6 @@ {{ items.course_credit }} {{next_sem.semester_no}} - {{ items.registration_type }} @@ -145,9 +143,6 @@ - - -

    @@ -217,6 +212,10 @@

    Fee Payment

    + + + + - - - diff --git a/FusionIIIT/templates/academic_procedures/studentCourses.html b/FusionIIIT/templates/academic_procedures/studentCourses.html index df5549b01..7c1a9152c 100644 --- a/FusionIIIT/templates/academic_procedures/studentCourses.html +++ b/FusionIIIT/templates/academic_procedures/studentCourses.html @@ -1,51 +1,13 @@ {% load static %} @@ -90,12 +52,6 @@ Semester - - Registration Type - - - Replaced By - Drop Course @@ -124,16 +80,8 @@ {{ items.sem }} - {{ items.registration_type }} - - - {% if items.replaced_by %} - {{ items.replaced_by.new_course_registration.course_id.code }} - Sem - {{ items.replaced_by.new_course_registration.semester_id.semester_no }} - {% endif %} - - - + DROP @@ -154,37 +102,20 @@ {% endif %}
    - + {% comment %} {% endcomment %} - - - - \ No newline at end of file diff --git a/FusionIIIT/templates/academic_procedures/studentSwayam.html b/FusionIIIT/templates/academic_procedures/studentSwayam.html deleted file mode 100644 index 6a912f3e9..000000000 --- a/FusionIIIT/templates/academic_procedures/studentSwayam.html +++ /dev/null @@ -1,322 +0,0 @@ -{% load static %} - - - - -
    -
    - {% if dict2 %} - -           -           -           -           - Name = {{ dict2.firstname }} {{ dict2.lastname }} -           -           -           - Roll No = {{ dict2.roll_no }} - - {% endif %} - {% if details %} - - - - - - - - - - - - - - - - - - - - - {% for items in details %} - - - - - - - - - - - - - - {% endfor %} - - -
    - Reg ID# - - Course ID - - Course Name - - Credits - - Semester - - Replace Course -
    - {{ items.rid }} - - {{ items.course_id }} - - {{ items.course_name }} - - {{ items.credits }} - - {{ items.sem }} - - -
    - - - - - - -
    - -
    - {% else %} -
    -


    -
    - NO RECORDS FOUND -
    - {% endif %} - -
    - {% comment %} {% endcomment %} - - - - -
    - - - - \ No newline at end of file diff --git a/FusionIIIT/templates/academic_procedures/student_table.html b/FusionIIIT/templates/academic_procedures/student_table.html index 696f06cc7..cf74029d7 100755 --- a/FusionIIIT/templates/academic_procedures/student_table.html +++ b/FusionIIIT/templates/academic_procedures/student_table.html @@ -101,8 +101,7 @@ var student_id = $(this).attr('data'); $.ajax({ type: 'post', - // url: '/academic-procedures/process_verification_request/', - url: '/academic-procedures/auto_process_verification_request/', + url: '/academic-procedures/process_verification_request/', data: { student_id: student_id, status_req: 'accept', @@ -130,12 +129,10 @@ $('.reg-disapprove').on('click', function(e){ var id = $(this).attr('data-two'); var student_id = $(this).attr('data'); - // var reason = $('#fname').val(); - var reason = $(this).closest('.verify').find('.reason').val(); + var reason = $('#fname').val(); $.ajax({ type: 'post', - // url: '/academic-procedures/process_verification_request/', - url: '/academic-procedures/auto_process_verification_request/', + url: '/academic-procedures/process_verification_request/', data: { student_id: student_id, status_req: 'reject', diff --git a/FusionIIIT/templates/academic_procedures/studentlist.html b/FusionIIIT/templates/academic_procedures/studentlist.html index b3c3ea209..8f43ce3d5 100755 --- a/FusionIIIT/templates/academic_procedures/studentlist.html +++ b/FusionIIIT/templates/academic_procedures/studentlist.html @@ -61,7 +61,7 @@ Verify Student Registration for Semester {{ date.semflag }} : {{ date.year }} - +
    {% if course_verification_date %}
    diff --git a/FusionIIIT/templates/academic_procedures/swayam_list.html b/FusionIIIT/templates/academic_procedures/swayam_list.html deleted file mode 100644 index 12c50c1de..000000000 --- a/FusionIIIT/templates/academic_procedures/swayam_list.html +++ /dev/null @@ -1,57 +0,0 @@ -{% comment %} ACAD ADMIN WILL REPLACE COURSES WITH SWAYAM COURSES HERE {% endcomment%} -{% load static %} -{% block allot_swayam %} - - - - - -{% comment %}The tab menu starts here!{% endcomment %} - -
    -
    - - {% csrf_token %} -
    - - -
    -
    - -
    - - -
    -
    - -
    -
    - -{% endblock %} \ No newline at end of file diff --git a/FusionIIIT/templates/academic_procedures/swayamregister.html b/FusionIIIT/templates/academic_procedures/swayamregister.html deleted file mode 100644 index 1b1902824..000000000 --- a/FusionIIIT/templates/academic_procedures/swayamregister.html +++ /dev/null @@ -1,87 +0,0 @@ -{% load static %} -{% block finalregister %} - - - - -
    Swayam Registration
    - -
    - {% if swayam_registration_flag == True %} -
    - {% csrf_token %} -
    - - -
    -
    - - -
    -
    - - -
    - - - - -
    -
    -
    - {% else %} -
    -

    Swayam Registration Date is yet to come

    -
    - {% endif %} -
    -{% endblock %} - - - \ No newline at end of file diff --git a/FusionIIIT/templates/account/base.html b/FusionIIIT/templates/account/base.html index 13b22224e..986600600 100755 --- a/FusionIIIT/templates/account/base.html +++ b/FusionIIIT/templates/account/base.html @@ -43,7 +43,7 @@ - +
    @@ -70,12 +70,11 @@
    {% else %} -
    + min-width: 360px;"> {% endif %} {% block content %} @@ -84,8 +83,6 @@ {% if request.user.is_authenticated %}
    -
    -
    {% comment %}The central-rail segment ends here!{% endcomment %} diff --git a/FusionIIIT/templates/account/login.html b/FusionIIIT/templates/account/login.html index 8fd2720dd..9bbff7e94 100755 --- a/FusionIIIT/templates/account/login.html +++ b/FusionIIIT/templates/account/login.html @@ -1,17 +1,16 @@ {% extends "account/base.html" %} -{% block content %} +{%block content%} {% load i18n %} {% load account socialaccount %} {% load static semanticui %} -
    User Image + src="{% static 'globals/img/user.png' %}" + style="margin-top: -27.5%; margin-left: 27.5%;" + alt="User Image" >
    - Login -
    + Login +
    @@ -19,61 +18,31 @@ {% csrf_token %} {% render_form form %} {% if redirect_field_value %} - + {% endif %}
    - + - - +
    {% get_providers as socialaccount_providers %}
    -
    - {% if socialaccount_providers %} -
    -
    - {% include "socialaccount/snippets/provider_list.html" with process="login" %} +
    + {% if socialaccount_providers %} +
    +
    + {% include "socialaccount/snippets/provider_list.html" with process="login" %} +
    -
    - {% include "socialaccount/snippets/login_extra.html" %} - {% endif %} -
    + {% include "socialaccount/snippets/login_extra.html" %} + {% endif %} +
    -{% if form.errors %} - -{% endif %} - - - {% endblock %} - \ No newline at end of file + diff --git a/FusionIIIT/templates/ais/account.html b/FusionIIIT/templates/ais/account.html index 15e92bad6..9f64e467f 100644 --- a/FusionIIIT/templates/ais/account.html +++ b/FusionIIIT/templates/ais/account.html @@ -1,6 +1,7 @@ {% extends 'globals/base.html' %} {% load static %} + {% block title %} Academic Information System {% endblock %} diff --git a/FusionIIIT/templates/ais/ais.html b/FusionIIIT/templates/ais/ais.html index ab5fecb5c..e9d3fea7d 100755 --- a/FusionIIIT/templates/ais/ais.html +++ b/FusionIIIT/templates/ais/ais.html @@ -46,12 +46,8 @@


    @@ -166,11 +134,8 @@
    - {% if delete_preregistration %} -
    - {% else %} -
    - {% endif %} + +
    {% block generateSheet %} {% include 'ais/generateSheet.html' %} {% endblock %} @@ -205,26 +170,18 @@ {% endblock %}
    - - - - - - - -
    - {% block config_pre_registration %} - {% include 'ais/pre_registration_courses.html' %} +
    + {% block min_credfloat_courses %} + {% include 'ais/new.html' %} {% endblock %}
    - {% block config_pre_reg_courses %} - {% include 'ais/config_courseslot_courses.html' %} + {% block curriculum %} + {% include 'ais/curriculum.html' %} {% endblock %}
    -
    {% block branch_change %} {% include 'academic_procedures/academicadminforbranch.html' %} @@ -244,59 +201,22 @@ {% endblock %}
    -
    - {% block allot_courses_after_add_and_drop %} - {% include 'academic_procedures/allot_courses_after_add_and_drop.html' %} - {% endblock %} -
    -
    {% block student_dashboard %} {% include 'ais/student_dashboard.html' %} {% endblock %}
    - -
    - {% block start_elective_allocation %} - {% include 'ais/start_elective_allocation.html' %} - {% endblock %} -
    - {% if delete_preregistration %} -
    - {% else %} -
    - {% endif %} - {% block delete_preregistration %} - {% include 'ais/delete_preregistration.html' %} - {% endblock %} -
    - -
    - {% block allot_swayam %} - {% include 'academic_procedures/swayam_list.html' %} - {% endblock %} -
    - - -
    - {% block curriculum %} - {% include 'ais/curriculum.html' %} - {% endblock %} -
    -
    - - {% block sidepanel%} - {% include 'notifications/academic_procedures_sidepanel.html' %} {% endblock %}
    -
    +
    -->
    {% endblock %} diff --git a/FusionIIIT/templates/ais/config_courseslot_courses.html b/FusionIIIT/templates/ais/config_courseslot_courses.html deleted file mode 100644 index ca3bcba67..000000000 --- a/FusionIIIT/templates/ais/config_courseslot_courses.html +++ /dev/null @@ -1,140 +0,0 @@ - - - -{% load static %} -{% block config_courseslot_courses %} - - - -
    - -
    - -
    - -
    - -
    - -
    -
    - -
    - -
    - -
    -
    -
    - - - - -{% endblock %} - - - - - - diff --git a/FusionIIIT/templates/ais/delete_preregistration.html b/FusionIIIT/templates/ais/delete_preregistration.html deleted file mode 100644 index 3203e7953..000000000 --- a/FusionIIIT/templates/ais/delete_preregistration.html +++ /dev/null @@ -1,110 +0,0 @@ -{% load static %} -{% block delete_preregistration %} - - -{% comment %}The tab menu starts here!{% endcomment %} -
    - {% if messages %} -
    - {% for message in messages %} -
    - {{ message }} -
    - {% endfor %} -
    - {% endif %} -

    Search and Manage Registrations

    - - -
    - {% csrf_token %} - -
    - - -
    - - -
    - - -
    - - - - {% if initial_registration or student_registration_check %} -
    -

    Search Results

    - - {% if initial_registrations %} -

    Initial Registration(s)

    -
    - {% for registration in initial_registrations %} -
    - Student Roll No: {{ registration.student_id }} -
    - Semester: {{ registration.semester_id }} -
    - Course: {{ registration.course_id }} -
    - Course Slot: {{ registration.course_slot_id }} -
    - Timestamp: {{ registration.timestamp }} -
    - Priority: {{ registration.priority }} -

    -
    - {% endfor %} -
    - {% else %} -
    -
    No Initial Registration found.
    -
    - {% endif %} - - {% if student_registration_check %} -

    Student Registration Check

    -
    -
    - Student Roll No: {{ student_registration_check.student_id }} -
    -
    - Pre-Registration Flag: {{ student_registration_check.pre_registration_flag }} -
    -
    - Final Registration Flag: {{ student_registration_check.final_registration_flag }} -
    -
    - {% else %} -
    -
    No Student Registration Check found.
    -
    - {% endif %} - - - -
    - {% else %} - {% if delete_preregistration and not messages %} -
    -
    Student's Pre Registration Data not found.
    -
    - {% endif %} - {% endif %} -
    - - - -{% endblock %} \ No newline at end of file diff --git a/FusionIIIT/templates/ais/generateSheet.html b/FusionIIIT/templates/ais/generateSheet.html index 16996f7e2..fe03efbfd 100755 --- a/FusionIIIT/templates/ais/generateSheet.html +++ b/FusionIIIT/templates/ais/generateSheet.html @@ -21,7 +21,7 @@ success: function(jsondata) { $("#loading").hide(); - // console.log(jsondata); + console.log(jsondata); $('#course_list').html(jsondata.html); }, error: function(data) @@ -33,29 +33,29 @@ -
    +
    {% csrf_token %}
    - - - - - +
    +
    diff --git a/FusionIIIT/templates/ais/pre_registration_courses.html b/FusionIIIT/templates/ais/pre_registration_courses.html deleted file mode 100644 index e861ec3c8..000000000 --- a/FusionIIIT/templates/ais/pre_registration_courses.html +++ /dev/null @@ -1,254 +0,0 @@ - - - - -{% load static %} -{% block config_pre_registration %} - - - -
    -
    -
    - - -
    - -
    - - -
    -
    - -
    - - -
    - -
    - - -
    - -
    - -
    -
    - - -
    - -
    - -
    - - - - {% csrf_token %} - - - - - - -
    - - -
    -
    -
    - - - -{% endblock %} - - - - diff --git a/FusionIIIT/templates/ais/profile.html b/FusionIIIT/templates/ais/profile.html index d30c5fa0e..479de29f6 100755 --- a/FusionIIIT/templates/ais/profile.html +++ b/FusionIIIT/templates/ais/profile.html @@ -29,7 +29,7 @@

    Note : Provide the data in Excel Sheet in following format:

    RollNo | First Name | Last Name | Email | Sex | DOB | Father's Name | Mother's Name | Category | Phone No | Address | Department | Specialization | Hall No -

    Download the sample Excel, fill the data accordingly and then upload the same

    +

    Download the sample Excel, fill the data accordingly and then upload the same

    diff --git a/FusionIIIT/templates/ais/start_elective_allocation.html b/FusionIIIT/templates/ais/start_elective_allocation.html deleted file mode 100644 index 8a01c6ca6..000000000 --- a/FusionIIIT/templates/ais/start_elective_allocation.html +++ /dev/null @@ -1,182 +0,0 @@ -{% load static %} -{% block start_elective_allocation %} - - -{% comment %}The tab menu starts here!{% endcomment %} - -
    -
    -
    - {% csrf_token %} -
    - - -
    -
    - - -
    -
    - - -
    -
    - -
    -
    -

    -

    -
    -
    - -
    -
    - -
    - -
    -
    -
    -
    -
    - {% csrf_token %} -
    - - -
    -
    - - -
    -
    - - -
    -
    - - -
    -
    - -
    -
    - -
    -
    -
    - - - - - - - - -
    students in course
    -
    - -
    - -
    -
    - - - -{% endblock %} \ No newline at end of file diff --git a/FusionIIIT/templates/ais/student_dashboard.html b/FusionIIIT/templates/ais/student_dashboard.html index 9d1a4c45a..06ad5ae92 100644 --- a/FusionIIIT/templates/ais/student_dashboard.html +++ b/FusionIIIT/templates/ais/student_dashboard.html @@ -21,7 +21,7 @@
    - +
    diff --git a/FusionIIIT/templates/complaintModule/add_workers.html b/FusionIIIT/templates/complaintModule/add_workers.html index 969fad127..842d4397a 100644 --- a/FusionIIIT/templates/complaintModule/add_workers.html +++ b/FusionIIIT/templates/complaintModule/add_workers.html @@ -2,16 +2,16 @@ {% block lodgecomplaint %} {% comment %}The tab menu starts here!{% endcomment %} - +
    - + - +
    - +
    @@ -112,13 +112,13 @@ - +
    - - - \ No newline at end of file + {%endblock%} \ No newline at end of file diff --git a/FusionIIIT/templates/complaintModule/assignworker.html b/FusionIIIT/templates/complaintModule/assignworker.html index 97076b99a..458d05ff9 100644 --- a/FusionIIIT/templates/complaintModule/assignworker.html +++ b/FusionIIIT/templates/complaintModule/assignworker.html @@ -1,4 +1,4 @@ - + {% extends 'globals/base.html' %} {% load static %} @@ -34,10 +34,11 @@
    {% comment %}The Tab-Menu starts here!{% endcomment %} -