66from decimal import Decimal , ROUND_HALF_UP
77from applications .academic_procedures .models import (course_registration , course_replacement )
88from applications .programme_curriculum .models import Course as Courses , Batch , CourseInstructor
9- from applications .examination .models import (hidden_grades , ResultAnnouncement )
9+ from applications .examination .models import (hidden_grades , ResultAnnouncement , authentication )
1010from applications .academic_information .models import (Student )
1111from applications .online_cms .models import (Student_grades )
1212from rest_framework import status
@@ -1163,25 +1163,19 @@ def post(self, request):
11631163 )
11641164
11651165
1166- # Setup header rows: S. No, Roll No, and Name in columns A, B, and C .
1166+ # Setup header rows: S. No and Roll No in columns A and B .
11671167 ws ["A1" ] = "S. No"
11681168 ws ["B1" ] = "Roll No"
1169- ws ["C1" ] = "Name"
11701169 for col in ("A" , "B" ):
11711170 cell = ws [col + "1" ]
11721171 cell .alignment = Alignment (horizontal = "center" , vertical = "center" )
11731172 cell .font = Font (bold = True )
11741173 cell .fill = header_fill
1175- cell = ws ["C1" ]
1176- cell .alignment = Alignment (horizontal = "center" , vertical = "center" )
1177- cell .font = Font (bold = True )
1178- cell .fill = header_fill
11791174 ws .column_dimensions [get_column_letter (1 )].width = 12
11801175 ws .column_dimensions [get_column_letter (2 )].width = 18
1181- ws .column_dimensions [get_column_letter (3 )].width = 30
11821176
1183- # Starting from column 4 , add headers for each course (each course uses 2 columns for Grade and Remarks).
1184- col_idx = 4
1177+ # Starting from column 3 , add headers for each course (each course uses 2 columns for Grade and Remarks).
1178+ col_idx = 3
11851179 for course in courses :
11861180 # Merge cells for the course code header.
11871181 ws .merge_cells (start_row = 1 , start_column = col_idx , end_row = 1 , end_column = col_idx + 1 )
@@ -1268,19 +1262,11 @@ def post(self, request):
12681262
12691263 # Fill in student rows, starting from row 5.
12701264 row_idx = 5
1271- User = get_user_model ()
12721265 for idx , student in enumerate (students , start = 1 ):
12731266 ws .cell (row = row_idx , column = 1 ).value = idx
12741267 ws .cell (row = row_idx , column = 2 ).value = student .id_id
1275-
1276- try :
1277- student_user = User .objects .get (username = student .id_id )
1278- student_name = f"{ student_user .first_name } { student_user .last_name } " .strip () or student_user .username
1279- except :
1280- student_name = student .id_id
1281-
1282- ws .cell (row = row_idx , column = 3 ).value = student_name
1283- ws .cell (row = row_idx , column = 3 ).alignment = Alignment (horizontal = "left" , vertical = "center" )
1268+ for c in [1 , 2 ]:
1269+ ws .cell (row = row_idx , column = c ).alignment = Alignment (horizontal = "center" , vertical = "center" )
12841270
12851271 # Get the student’s grade records for the current semester.
12861272 student_grades = Student_grades .objects .filter (
@@ -1290,7 +1276,7 @@ def post(self, request):
12901276 semester = semester
12911277 )
12921278 grades_map = {g .course_id_id : g for g in student_grades }
1293- col_ptr = 4
1279+ col_ptr = 3
12941280 for course in courses :
12951281 grade_entry = grades_map .get (course .id )
12961282 grade_val = grade_entry .grade if grade_entry else '-'
@@ -2857,6 +2843,162 @@ def get(self, request, *args, **kwargs):
28572843 return JsonResponse ({"success" : True , "semesters" : semesters })
28582844
28592845
2846+ class GradeStatusAPI (APIView ):
2847+ """
2848+ API to get grade status for all courses in a given academic year and semester type.
2849+ Shows course information, professor name, and submission/verification status.
2850+
2851+ Expected Request:
2852+ POST /api/examination/grade_status/
2853+ Headers:
2854+ Authorization: Token <your_auth_token>
2855+ Body (JSON):
2856+ {
2857+ "Role": "acadadmin",
2858+ "academic_year": "2024-25",
2859+ "semester_type": "Odd Semester"
2860+ }
2861+
2862+ Response:
2863+ 200 OK - List of courses with status information
2864+ 403 Forbidden - Access denied
2865+ 400 Bad Request - Missing required fields
2866+ """
2867+ permission_classes = [IsAuthenticated ]
2868+
2869+ def post (self , request ):
2870+ role = request .data .get ("Role" )
2871+ academic_year = request .data .get ("academic_year" )
2872+ semester_type = request .data .get ("semester_type" )
2873+
2874+ # Role-based access control
2875+ if role not in ["acadadmin" , "Dean Academic" ]:
2876+ return Response (
2877+ {"success" : False , "error" : "Access denied." },
2878+ status = status .HTTP_403_FORBIDDEN
2879+ )
2880+
2881+ # Validate required parameters
2882+ if not academic_year or not semester_type :
2883+ return Response (
2884+ {"error" : "Academic year and semester type are required." },
2885+ status = status .HTTP_400_BAD_REQUEST
2886+ )
2887+
2888+ try :
2889+ # Parse academic year to get working year
2890+ working_year , session = parse_academic_year (academic_year , semester_type )
2891+
2892+ # Get all courses that have registrations for this academic year and semester type
2893+ # Use values_list with flat=True for better performance
2894+ course_ids = course_registration .objects .filter (
2895+ session = academic_year ,
2896+ semester_type = semester_type
2897+ ).values_list ('course_id' , flat = True ).distinct ()
2898+
2899+ # Fetch courses with select_related for better performance
2900+ courses = Courses .objects .filter (id__in = course_ids ).order_by ('code' )
2901+
2902+ # Bulk fetch all instructors to avoid N+1 queries
2903+ instructors_map = {}
2904+ instructors = CourseInstructor .objects .filter (
2905+ course_id__in = course_ids ,
2906+ year = working_year ,
2907+ semester_type = semester_type
2908+ ).select_related ()
2909+
2910+ for instructor in instructors :
2911+ instructors_map [instructor .course_id_id ] = instructor
2912+
2913+ # Bulk fetch professor names to avoid individual User queries
2914+ instructor_ids = [inst .instructor_id_id for inst in instructors ]
2915+ users_map = {}
2916+ if instructor_ids :
2917+ users = get_user_model ().objects .filter (username__in = instructor_ids )
2918+ users_map = {
2919+ user .username : f"{ user .first_name } { user .last_name } " .strip ()
2920+ for user in users
2921+ }
2922+
2923+ # Bulk fetch grade submission and verification status
2924+ submitted_courses = set (
2925+ Student_grades .objects .filter (
2926+ course_id__in = course_ids ,
2927+ academic_year = academic_year ,
2928+ semester_type = semester_type
2929+ ).values_list ('course_id' , flat = True ).distinct ()
2930+ )
2931+
2932+ verified_courses = set (
2933+ Student_grades .objects .filter (
2934+ course_id__in = course_ids ,
2935+ academic_year = academic_year ,
2936+ semester_type = semester_type ,
2937+ verified = True
2938+ ).values_list ('course_id' , flat = True ).distinct ()
2939+ )
2940+
2941+ # Bulk fetch authentication records
2942+ auth_records_map = {}
2943+ auth_records = authentication .objects .filter (
2944+ course_id__in = course_ids ,
2945+ course_year = working_year
2946+ )
2947+
2948+ for auth in auth_records :
2949+ auth_records_map [auth .course_id_id ] = auth
2950+
2951+ # Build response data efficiently
2952+ grade_status_list = []
2953+
2954+ for course in courses :
2955+ # Get instructor information from pre-fetched data
2956+ instructor = instructors_map .get (course .id )
2957+ professor_name = "Not Assigned"
2958+
2959+ if instructor :
2960+ professor_name = users_map .get (
2961+ instructor .instructor_id_id ,
2962+ instructor .instructor_id_id
2963+ )
2964+
2965+ # Determine status from pre-fetched sets
2966+ submitted = "Submitted" if course .id in submitted_courses else "Not Submitted"
2967+ verified = "Verified" if course .id in verified_courses else "Not Verified"
2968+
2969+ # Check validation status
2970+ validated = "Not Validated"
2971+ if course .id in verified_courses : # Only check if verified
2972+ auth_record = auth_records_map .get (course .id )
2973+ if (auth_record and auth_record .authenticator_1 and
2974+ auth_record .authenticator_2 and auth_record .authenticator_3 ):
2975+ validated = "Validated"
2976+
2977+ grade_status_list .append ({
2978+ "course_code" : course .code ,
2979+ "course_name" : course .name ,
2980+ "course_id" : course .id ,
2981+ "professor_name" : professor_name ,
2982+ "submitted" : submitted ,
2983+ "verified" : verified ,
2984+ "validated" : validated ,
2985+ "credits" : course .credit ,
2986+ "version" : course .version
2987+ })
2988+
2989+ return Response ({
2990+ "success" : True ,
2991+ "grade_status" : grade_status_list ,
2992+ "academic_year" : academic_year ,
2993+ "semester_type" : semester_type
2994+ }, status = status .HTTP_200_OK )
2995+
2996+ except Exception as e :
2997+ return Response (
2998+ {"error" : f"An error occurred: { str (e )} " },
2999+ status = status .HTTP_500_INTERNAL_SERVER_ERROR
3000+ )
3001+
28603002class GenerateStudentResultPDFAPI (APIView ):
28613003 """
28623004 API endpoint to generate PDF report for student examination results
0 commit comments