From 26333f54b41b1b50848906229c957e84e3caef08 Mon Sep 17 00:00:00 2001 From: vikrantwiz02 Date: Tue, 18 Nov 2025 15:15:58 +0530 Subject: [PATCH 1/4] Add programme type filtering to available courses API --- .../academic_information/api/views.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/FusionIIIT/applications/academic_information/api/views.py b/FusionIIIT/applications/academic_information/api/views.py index 37801e49d..bfb99c55d 100644 --- a/FusionIIIT/applications/academic_information/api/views.py +++ b/FusionIIIT/applications/academic_information/api/views.py @@ -929,15 +929,32 @@ def import_calendar(request): @role_required(['acadadmin']) def available_courses(request): """ - GET /api/available-courses/?academic_year=2024-25&semester_type=Odd+Semester + 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) From 5387e58f94228f3c485eeff8db883a1b8ba3077c Mon Sep 17 00:00:00 2001 From: vikrantwiz02 Date: Tue, 18 Nov 2025 15:56:29 +0530 Subject: [PATCH 2/4] Add programme type filtering to academic years and course details retrieval --- .../applications/examination/api/views.py | 81 +++++++++++++++++-- 1 file changed, 75 insertions(+), 6 deletions(-) diff --git a/FusionIIIT/applications/examination/api/views.py b/FusionIIIT/applications/examination/api/views.py index fdfbf896b..740d6001b 100644 --- a/FusionIIIT/applications/examination/api/views.py +++ b/FusionIIIT/applications/examination/api/views.py @@ -317,11 +317,32 @@ class UniqueRegistrationYearsView(APIView): 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 = ( - course_registration.objects + years_query .values_list('session', flat=True) .distinct() - .order_by('session').exclude(session__isnull = True) + .order_by('session') ) return Response({'academic_years': list(years)}, status=200) @@ -342,6 +363,7 @@ def download_template(request): 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) @@ -363,11 +385,32 @@ def download_template(request): User = get_user_model() # Filter course_registration records using course, session (academic year), and semester_type. - course_info = course_registration.objects.filter( + course_info_query = course_registration.objects.filter( course_id_id=course, session=session_year, semester_type=semester_type - ).order_by("student_id_id") + ) + + # 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: + 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_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(): return Response( @@ -1468,6 +1511,7 @@ 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( @@ -1492,10 +1536,35 @@ def post(self, request): .annotate(course_id_int=Cast("course_id_id", IntegerField())) ) - # Retrieve course details - courses_info = Courses.objects.filter( + # Retrieve course details with programme type filtering + courses_query = Courses.objects.filter( id__in=unique_course_ids.values_list("course_id_int", flat=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) + + 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 return Response( { From 5e6b0696a4a0359925c2702f6aa2d9b8d5a0bff0 Mon Sep 17 00:00:00 2001 From: vikrantwiz02 Date: Wed, 19 Nov 2025 04:07:27 +0530 Subject: [PATCH 3/4] Add check_course_students API to verify student enrollment by programme type --- .../applications/examination/api/urls.py | 1 + .../applications/examination/api/views.py | 306 +++++++++++++++--- 2 files changed, 256 insertions(+), 51 deletions(-) diff --git a/FusionIIIT/applications/examination/api/urls.py b/FusionIIIT/applications/examination/api/urls.py index 85b9ddf9f..fc36a42e8 100644 --- a/FusionIIIT/applications/examination/api/urls.py +++ b/FusionIIIT/applications/examination/api/urls.py @@ -7,6 +7,7 @@ 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'), diff --git a/FusionIIIT/applications/examination/api/views.py b/FusionIIIT/applications/examination/api/views.py index 740d6001b..8232843d4 100644 --- a/FusionIIIT/applications/examination/api/views.py +++ b/FusionIIIT/applications/examination/api/views.py @@ -3,6 +3,7 @@ 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 @@ -359,29 +360,28 @@ def download_template(request): - 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") """ - 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') + 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 - ) + 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 + ) - # Check access for allowed roles. - 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) + 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) - try: User = get_user_model() # Filter course_registration records using course, session (academic year), and semester_type. @@ -398,25 +398,34 @@ def download_template(request): 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_info_query = course_info_query.filter( - student_id__in=student_ids_with_programme + 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(): - return Response( - {"error": "No registration data found for the provided course, academic year, and semester type."}, - status=status.HTTP_404_NOT_FOUND - ) + 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 @@ -443,7 +452,76 @@ def download_template(request): return response except Exception as e: - return Response({'error': 'An unexpected error occurred'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + 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): @@ -1541,6 +1619,7 @@ def post(self, request): 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'] @@ -1566,9 +1645,28 @@ def post(self, request): 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": list(courses_info.values()), + "courses_info": courses_data, }, status=status.HTTP_200_OK, ) @@ -1611,6 +1709,7 @@ def post(self, request): 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."}, @@ -1633,29 +1732,108 @@ def post(self, request): return Response({"error": "Invalid course ID."}, status=status.HTTP_400_BAD_REQUEST) - # 6) CHECK STUDENT REGISTRATIONS + # 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 ) - # 7) DUPLICATE‐SUBMIT CHECK - existing = Student_grades.objects.filter( + 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 existing.exists() and not existing.first().reSubmit: - return Response( - {"error": "THIS COURSE HAS ALREADY BEEN SUBMITTED."}, - status=status.HTTP_400_BAD_REQUEST - ) + + 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( @@ -1681,12 +1859,22 @@ def post(self, request): # 10) ATOMIC PROCESSING errors = [] with transaction.atomic(): - # ─── Reset ALL reSubmit flags for this course/year/semester ─── - Student_grades.objects.filter( + # ─── 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 - ).update(reSubmit=False) + ) + + 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): @@ -1702,17 +1890,32 @@ def post(self, request): errors.append(f"Row {idx}: Student with roll_no {roll_no} does not exist.") continue - # b) STUDENT REGISTERED FOR COURSE? - if not course_registration.objects.filter( + student_reg = course_registration.objects.filter( student_id=stud, course_id=course, semester_type=semester_type, session=academic_year - ).exists(): + ) + + 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: @@ -1755,8 +1958,9 @@ def post(self, request): 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": "Grades uploaded successfully."}, + {"message": f"Grades uploaded successfully{programme_msg}."}, status=status.HTTP_200_OK ) From 4620bfb4376f17ff13dd8ccadeadbe07003a877d Mon Sep 17 00:00:00 2001 From: vikrantwiz02 Date: Wed, 19 Nov 2025 04:39:56 +0530 Subject: [PATCH 4/4] Add programme type filtering to grade download and preview APIs --- .../applications/examination/api/views.py | 60 ++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/FusionIIIT/applications/examination/api/views.py b/FusionIIIT/applications/examination/api/views.py index 8232843d4..030ffaf8d 100644 --- a/FusionIIIT/applications/examination/api/views.py +++ b/FusionIIIT/applications/examination/api/views.py @@ -1979,6 +1979,7 @@ 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({"error": "Access denied."}, status=status.HTTP_403_FORBIDDEN) @@ -2005,6 +2006,24 @@ def post(self, request): 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) @@ -2037,6 +2056,7 @@ def post(self, request): 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) @@ -2045,7 +2065,26 @@ def post(self, request): course_id_id=course_id, academic_year=academic_year, semester_type=semester_type - ).order_by("roll_no") + ) + + 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, @@ -2882,6 +2921,7 @@ def post(self, 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."}, @@ -2903,6 +2943,24 @@ def post(self, request): 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"):