Skip to content

Latest commit

 

History

History
662 lines (581 loc) · 31 KB

File metadata and controls

662 lines (581 loc) · 31 KB

i will create a seperate files for all these actions so that i can better understand them. after creating the files i will call each within the grade_sheets/views which will serve as my main base to interact with my react frontend but for now let them remain here in this markdow. @action(detail=False, methods=['GET'], url_path='gradesheet/pdf/generate') def generate_gradesheet_pdf(self, request): """GET /api/grade_sheets/gradesheet/pdf/generate/ - Generate PDFs.""" level_id = request.query_params.get('level_id') student_id = request.query_params.get('student_id') academic_year = request.query_params.get('academic_year') try: if not level_id: return Response({"error": "level_id is required"}, status=status.HTTP_400_BAD_REQUEST)

        level = get_level_by_id(level_id)
        if not level:
            return Response({"error": f"Invalid level_id: {level_id}"}, status=status.HTTP_400_BAD_REQUEST)

        student = None
        if student_id:
            student = get_students_by_level(level_id).filter(id=student_id).first()
            if not student:
                return Response({"error": f"Invalid student_id: {student_id}"}, status=status.HTTP_400_BAD_REQUEST)

        academic_year_obj = AcademicYear.objects.get(name=academic_year) if academic_year else None
        model = StudentGradeSheetPDF if student_id else LevelGradeSheetPDF
        pdf_query = model.objects.filter(
            level_id=level_id,
            student_id=student_id if student_id else None,
            academic_year=academic_year_obj
        )
        if pdf_query.exists():
            pdf_record = pdf_query.first()
            latest_grade = Grade.objects.filter(
                enrollment__level_id=level_id,
                enrollment__student_id=student_id if student_id else None,
                enrollment__academic_year=academic_year_obj
            ).order_by('-updated_at').first()
            if latest_grade and latest_grade.updated_at > pdf_record.updated_at:
                logger.info(f"New grades detected since PDF update at {pdf_record.updated_at}. Regenerating PDF.")
                pdf_query.delete()
            elif os.path.exists(pdf_record.pdf_path):
                return Response({
                    "message": "PDF retrieved successfully",
                    "view_url": pdf_record.view_url,
                    "pdf_path": pdf_record.pdf_path
                })

        # Batch process for level-wide PDFs
        batch_size = 10
        pdf_paths = []
        if not student_id:
            enrollments = Enrollment.objects.filter(level_id=level_id, academic_year=academic_year_obj)
            for i in range(0, enrollments.count(), batch_size):
                batch = enrollments[i:i + batch_size]
                for enrollment in batch:
                    batch_paths = generate_gradesheet_pdf(
                        level_id=int(level_id),
                        student_id=enrollment.student.id,
                        academic_year=academic_year
                    )
                    pdf_paths.extend(batch_paths)
            if pdf_paths:
                # Generate merged level-wide PDF
                batch_paths = generate_gradesheet_pdf(
                    level_id=int(level_id),
                    student_id=None,
                    academic_year=academic_year
                )
                pdf_paths.extend(batch_paths)
        else:
            pdf_paths = generate_gradesheet_pdf(
                level_id=int(level_id),
                student_id=int(student_id),
                academic_year=academic_year
            )

        if not pdf_paths:
            logger.warning(f"No PDFs generated for level_id={level_id}, student_id={student_id}, academic_year={academic_year}")
            return Response({"error": "No PDFs generated"}, status=status.HTTP_404_NOT_FOUND)

        for pdf_path in pdf_paths:
            pdf_filename = os.path.basename(pdf_path)
            model.objects.update_or_create(
                level_id=level_id,
                student_id=student_id if student_id else None,
                academic_year=academic_year_obj,
                defaults={'pdf_path': pdf_path, 'filename': pdf_filename}
            )

        pdf_path = pdf_paths[0]
        view_url = f"{settings.MEDIA_URL}output_gradesheets/{pdf_filename}"
        return Response({
            "message": "PDF generated successfully",
            "view_url": view_url,
            "pdf_path": pdf_path
        })

    except Exception as e:
        logger.error(f"Error generating PDF: {str(e)}")
        return Response({"error": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

view gradesheets pdf: @action(detail=False, methods=['GET'], url_path='gradesheet/pdf/view') def view_gradesheet_pdf(self, request): """GET /api/grade_sheets/gradesheet/pdf/view/ - Serve PDF.""" level_id = request.query_params.get('level_id') student_id = request.query_params.get('student_id') academic_year = request.query_params.get('academic_year') try: if not level_id: return Response({"error": "level_id is required"}, status=status.HTTP_400_BAD_REQUEST)

        academic_year_obj = AcademicYear.objects.get(name=academic_year) if academic_year else None
        model = StudentGradeSheetPDF if student_id else LevelGradeSheetPDF
        pdf_query = model.objects.filter(
            level_id=level_id,
            student_id=student_id if student_id else None,
            academic_year=academic_year_obj
        )
        if not pdf_query.exists():
            logger.warning(f"No PDF record found for level_id={level_id}, student_id={student_id}, academic_year={academic_year}")
            return Response({"error": "PDF not found"}, status=status.HTTP_404_NOT_FOUND)

        pdf_record = pdf_query.first()
        pdf_path = pdf_record.pdf_path
        latest_grade = Grade.objects.filter(
            enrollment__level_id=level_id,
            enrollment__student_id=student_id if student_id else None,
            enrollment__academic_year=academic_year_obj
        ).order_by('-updated_at').first()

        if latest_grade and latest_grade.updated_at > pdf_record.updated_at:
            logger.info(f"New grades detected since PDF update at {pdf_record.updated_at}. Regenerating PDF.")
            pdf_query.delete()
            pdf_paths = generate_gradesheet_pdf(
                level_id=int(level_id),
                student_id=int(student_id) if student_id else None,
                academic_year=academic_year
            )
            if not pdf_paths:
                return Response({"error": "Failed to re-generate PDF"}, status=status.HTTP_404_NOT_FOUND)
            pdf_path = pdf_paths[0]
            pdf_filename = os.path.basename(pdf_path)
            model.objects.update_or_create(
                level_id=level_id,
                student_id=student_id if student_id else None,
                academic_year=academic_year_obj,
                defaults={'pdf_path': pdf_path, 'filename': pdf_filename}
            )

        if not os.path.exists(pdf_path):
            logger.warning(f"PDF file not found: {pdf_path}")
            pdf_paths = generate_gradesheet_pdf(
                level_id=int(level_id),
                student_id=int(student_id) if student_id else None,
                academic_year=academic_year
            )
            if not pdf_paths:
                return Response({"error": "Failed to re-generate PDF"}, status=status.HTTP_404_NOT_FOUND)
            pdf_path = pdf_paths[0]
            pdf_filename = os.path.basename(pdf_path)
            model.objects.update_or_create(
                level_id=level_id,
                student_id=student_id if student_id else None,
                academic_year=academic_year_obj,
                defaults={'pdf_path': pdf_path, 'filename': pdf_filename}
            )

        with open(pdf_path, 'rb') as f:
            response = FileResponse(f, content_type='application/pdf')
            response['Content-Disposition'] = f'inline; filename="{pdf_filename}"'
            logger.info(f"Serving PDF for viewing: {pdf_path}")
            return response

    except Exception as e:
        logger.error(f"Error serving PDF: {str(e)}")
        return Response({"error": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

reportcardprintview: class ReportCardPrintView(APIView): """POST /api/grade_sheets/report_card_print/ - Generate yearly report card PDFs.""" def post(self, request): level_id = request.data.get("level_id") student_id = request.data.get("student_id") academic_year = request.data.get("academic_year")

    if not level_id:
        return Response({"error": "level_id is required"}, status=status.HTTP_400_BAD_REQUEST)

    try:
        academic_year_obj = AcademicYear.objects.get(name=academic_year) if academic_year else None
        status_obj = PassFailedStatus.objects.filter(
            student_id=student_id, level_id=level_id, academic_year=academic_year_obj
        ).first() if student_id else None
        pass_template = status_obj.status in ['PASS', 'CONDITIONAL'] if status_obj else True
        pdf_paths = generate_yearly_gradesheet_pdf(
            level_id=level_id,
            student_id=student_id,
            pass_template=pass_template,
            academic_year=academic_year
        )

        if not pdf_paths:
            return Response({"error": "No PDFs generated"}, status=status.HTTP_404_NOT_FOUND)

        pdf_path = pdf_paths[0]
        pdf_filename = os.path.basename(pdf_path)
        model = StudentGradeSheetPDF if student_id else LevelGradeSheetPDF
        model.objects.update_or_create(
            level_id=level_id,
            student_id=student_id if student_id else None,
            academic_year=academic_year_obj,
            defaults={'pdf_path': pdf_path, 'filename': pdf_filename}
        )

        view_url = f"{settings.MEDIA_URL}output_gradesheets/{pdf_filename}"
        return Response({
            "message": "PDF generated successfully",
            "view_url": view_url,
            "pdf_path": pdf_path
        })

    except Exception as e:
        logger.error(f"Error generating PDF: {str(e)}")
        return Response({"error": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

this is my grade_sheets/helper.py:from venv import logger from grades.models import Grade from subjects.models import Subject from enrollment.models import Enrollment from .utils import determine_pass_fail from periods.models import Period

def get_grade_sheet_data(student_id, level_id, academic_year_id=None): """Compile grade data for a student, level, and optional academic year.""" try: enrollment = Enrollment.objects.get(student_id=student_id, level_id=level_id, academic_year_id=academic_year_id) grades = Grade.objects.filter(enrollment=enrollment).select_related('subject', 'period') subjects = Subject.objects.filter(level_id=level_id) periods = Period.objects.all() period_map = {p.period: p.period for p in periods} # Use actual period codes

    grade_data = {
        'student_name': f"{enrollment.student.firstName} {enrollment.student.lastName}",
        's': [],  # Subjects
        'status': determine_pass_fail(student_id, level_id, academic_year_id)
    }

    for subject in subjects:
        subject_grades = {'sn': subject.subject}
        for period in periods:
            grade = grades.filter(subject=subject, period=period).first()
            subject_grades[period.period] = grade.score if grade else '-'
        # Calculate averages
        try:
            # First semester average (1a)
            if all(subject_grades.get(p) != '-' for p in ['1st', '2nd', '3rd', '1exam']):
                sem1_period_avg = (int(subject_grades['1st']) + int(subject_grades['2nd']) + int(subject_grades['3rd'])) // 3
                subject_grades['1a'] = (sem1_period_avg + int(subject_grades['1exam'])) // 2
            else:
                subject_grades['1a'] = '-'
            # Second semester average (2a)
            if all(subject_grades.get(p) != '-' for p in ['4th', '5th', '6th', '2exam']):
                sem2_period_avg = (int(subject_grades['4th']) + int(subject_grades['5th']) + int(subject_grades['6th'])) // 3
                subject_grades['2a'] = (sem2_period_avg + int(subject_grades['2exam'])) // 2
            else:
                subject_grades['2a'] = '-'
            # Final average (f)
            if subject_grades['1a'] != '-' and subject_grades['2a'] != '-':
                subject_grades['f'] = (int(subject_grades['1a']) + int(subject_grades['2a'])) // 2
            else:
                subject_grades['f'] = '-'
        except (ValueError, TypeError) as e:
            logger.error(f"Error calculating averages for subject {subject.subject}: {str(e)}")
            subject_grades['1a'] = subject_grades['2a'] = subject_grades['f'] = '-'
        grade_data['s'].append(subject_grades)

    return grade_data
except Enrollment.DoesNotExist:
    return None my gradesheets views:import logging

import os from django.shortcuts import render, redirect from django.contrib import messages from django.conf import settings from django.http import FileResponse from rest_framework import viewsets, status from rest_framework.decorators import action from rest_framework.response import Response from rest_framework.views import APIView from enrollment.models import Enrollment from students.helper import get_students_by_level, format_student_data, format_student_name from levels.helper import get_level_by_id, get_all_levels from grades.helper import get_grade_map, save_grade from subjects.helper import get_subjects_by_level from periods.helpers import get_all_periods from enrollment.helper import get_enrollment_by_student_level from grades.models import Grade from .listLevelHelper import build_gradesheet from .models import StudentGradeSheetPDF, LevelGradeSheetPDF from academic_years.models import AcademicYear from pass_and_failed.models import PassFailedStatus

from .yearly_pdf_utils import generate_yearly_gradesheet_pdf from django.urls import reverse

logger = logging.getLogger(name)

class GradeSheetViewSet(viewsets.ViewSet): @action(detail=False, methods=['POST'], url_path='input', url_name='input-grades') def input_grades(self, request): """POST /api/grade_sheets/input/ - Bulk grade submission.""" level_id = request.data.get('level') subject_id = request.data.get('subject_id') period_id = request.data.get('period_id') grades = request.data.get('grades') academic_year = request.data.get('academic_year')

    logger.debug(f"Received POST data: level_id={level_id}, subject_id={subject_id}, period_id={period_id}, grades={grades}, academic_year={academic_year}")

    if not isinstance(grades, list):
        grades=[]
        for key, value in request.data.items():
            if key.startswith('grades[') and value:
                student_id = key.split('[')[1].split(']')[0]
                grades.append({'student_id': student_id, 'score': value})

    if not all([level_id, subject_id, academic_year]) or not isinstance(grades, list):
        return Response({"error": "Missing or invalid required fields, including academic_year."}, status=status.HTTP_400_BAD_REQUEST)

    try:
        academic_year_obj = AcademicYear.objects.get(id=academic_year) if academic_year else None
     
        saved_grades = []
        skipped_students = []
        errors = []
        affected_student_ids = []

        for grade_data in grades:
            student_id = grade_data.get('student_id')
            score = grade_data.get('score')
            grade_period_id = grade_data.get('period_id', period_id)
            if not student_id or score is None or not grade_period_id:
                errors.append({"student_id": student_id, "error": "Missing student_id, score, or period_id"})
                continue

            # Validate score is an integer
            try:
                score = int(score)
                if not (0 <= score <= 100):
                    errors.append({"student_id": student_id, "error": "Score must be an integer between 0 and 100"})
                    continue
            except (ValueError, TypeError):
                errors.append({"student_id": student_id, "error": "Score must be an integer"})
                continue

            enrollment = get_enrollment_by_student_level(student_id, level_id, academic_year_obj.id)
            if not enrollment:
                logger.warning(f"No enrollment found for student_id={student_id}, level_id={level_id}, academic_year={academic_year}")
                skipped_students.append(student_id)
                continue

            grade, result = save_grade(enrollment, subject_id, grade_period_id, score, request)
            if grade:
                saved_grades.append(grade.id)
                affected_student_ids.append(student_id)
                logger.info(f"Saved grade: id={grade.id}, enrollment_id={enrollment.id}, subject_id={subject_id}, period_id={grade_period_id}, score={score}")
            else:
                errors.append({"student_id": student_id, "error": result})

        if saved_grades:
            StudentGradeSheetPDF.objects.filter(
                level_id=level_id,
                student_id__in=affected_student_ids,
                academic_year=academic_year_obj
            ).delete()
            logger.info(f"Deleted existing PDFs for students {affected_student_ids} due to new/updated grades.")

        response_data = {
            "message": "Grades processed.",
            "saved_grades": saved_grades,
            "skipped_students": skipped_students,
            "errors": errors
        }
        status_code = status.HTTP_201_CREATED if saved_grades else status.HTTP_400_BAD_REQUEST
        if errors:
            response_data["message"] = "Some grades failed validation."
            status_code = status.HTTP_400_BAD_REQUEST
        return Response(response_data, status=status_code)

    except Exception as e:
        logger.error(f"Error in input_grades: {str(e)}")
        return Response({"error": f"Internal server error: {str(e)}"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

@action(detail=False, methods=['GET'], url_path='by_level')
def list_by_level(self, request):
    """GET /api/grade_sheets/by_level/ - Retrieve grades for a level."""
    level_id = request.query_params.get('level_id')
    academic_year = request.query_params.get('academic_year')
    if not level_id:
        return Response({"error": "Level ID is required"}, status=status.HTTP_400_BAD_REQUEST)
    try:
        result = build_gradesheet(level_id, academic_year)
        return Response(result)
    except Exception as e:
        logger.error(f"Error in list_by_level:{str(e)}")
        return Response({"error":str(e)}, status=status.HTTP_400_BAD_REQUEST)

   
@action(detail=False, methods=['GET'], url_path='by_period_subject')
def fetch_by_subject_and_period(self, request):
    """GET /api/grade_sheets/by_period_subject/ - Retrieve grades."""
    level_id = request.query_params.get('level_id')
    subject_id = request.query_params.get('subject_id')
    period_id = request.query_params.get('period_id')
    academic_year = request.query_params.get('academic_year')

    if not all([level_id, subject_id]):
        return Response({"error": "Missing required parameters."}, status=status.HTTP_400_BAD_REQUEST)

    try:
        academic_year_obj = AcademicYear.objects.get(name=academic_year) if academic_year else None
        grades = Grade.objects.filter(
            enrollment__level_id=level_id,
            subject_id=subject_id,
        ).select_related('enrollment__student', 'period')
        if period_id:
            grades = grades.filter(period_id=period_id)
        if academic_year_obj:
            grades = grades.filter(enrollment__academic_year=academic_year_obj)

        result = [
            {
                "student_id": grade.enrollment.student.id,
                "student_name": format_student_name(grade.enrollment.student),
                "score": grade.score,  # No float casting needed
                "period_id": grade.period.id
            }
            for grade in grades
        ]
        return Response(result)

    except Exception as e:
        logger.error(f"Error in fetch_by_subject_and_period: {str(e)}")
        return Response({"error": str(e)}, status=status.HTTP_400_BAD_REQUEST)

@action(detail=False, methods=['GET'], url_path='check_enrollment')
def check_enrollment(self, request):
    """GET /api/grade_sheets/check_enrollment/ - Verify enrollment."""
    student_id = request.query_params.get('student_id')
    level_id = request.query_params.get('level_id')
    academic_year = request.query_params.get('academic_year')
    if not all([student_id, level_id]):
        return Response({"error": "student_id and level_id are required"}, status=status.HTTP_400_BAD_REQUEST)

    try:
        academic_year_obj = AcademicYear.objects.get(name=academic_year) if academic_year else None
        enrollment = get_enrollment_by_student_level(student_id, level_id, academic_year_obj.id if academic_year_obj else None)
        if not enrollment:
            return Response({"enrolled": False}, status=status.HTTP_200_OK)
        return Response({"enrolled": True, "enrollment_id": enrollment.id}, status=status.HTTP_200_OK)

    except Exception as e:
        logger.error(f"Error in check_enrollment: {str(e)}")
        return Response({"error": str(e)}, status=status.HTTP_400_BAD_REQUEST)

def gradesheet_home(request): """Render grade input home page.""" levels = get_all_levels() academic_years = AcademicYear.objects.all()

selected_level_id = request.GET.get('level_id')
students = []
subjects = []
periods = get_all_periods()
selected_level_name = None
selected_subject_id = request.GET.get('subject_id')
selected_period_id = request.GET.get('period_id')
selected_academic_year = request.GET.get('academic_year')

if selected_level_id:
    students = get_students_by_level(selected_level_id)
    subjects = get_subjects_by_level(selected_level_id)
    level = get_level_by_id(selected_level_id)
    selected_level_name = level.name if level else None
    if selected_academic_year:
        students = students.filter(enrollment__academic_year__name=selected_academic_year).distinct()
    if selected_subject_id and selected_period_id:
        students_with_grades = Grade.objects.filter(
            enrollment__level_id=selected_level_id,
            subject_id=selected_subject_id,
            period_id=selected_period_id
        ).values_list('enrollment__student_id', flat=True)
        students = [s for s in students if s.id not in students_with_grades]

return render(request, 'grade_sheets/gradesheet.html', {
    'levels': levels,
    'academic_years': academic_years,
    'students': students,
    'subjects': subjects,
    'periods': periods,
    'selected_level_id': selected_level_id,
    'selected_level_name': selected_level_name,
    'selected_subject_id': selected_subject_id,
    'selected_period_id': selected_period_id,
    'selected_academic_year': selected_academic_year
})

def gradesheet_view(request): """Render grade sheet view page.""" level_id = request.GET.get('level_id') academic_year = request.GET.get('academic_year') if not level_id: messages.error(request, "Please select a valid level") return redirect('gradesheet-home')

try:
    # Validate level_id
    level = get_level_by_id(level_id)
    if not level:
        messages.error(request, f"Invalid level ID: {level_id}")
        return redirect('gradesheet-home')

    gradesheet = build_gradesheet(level_id, academic_year)
    level_name = level.name
    return render(request, 'grade_sheets/gradesheet_view.html', {
        'gradesheet': gradesheet,
        'level_name': level_name,
        'level_id': level_id,
        'academic_year': academic_year
    })
except Exception as e:
    logger.error(f"Error in gradesheet_view: {str(e)}")
    messages.error(request, f"Error loading grades: {str(e)}")
    return redirect('gradesheet-home')

def input_grades_view(request): """Handle grade input via web form.""" if request.method == 'POST': level_id = request.POST.get('level_id') subject_id = request.POST.get('subject_id') period_id = request.POST.get('period_id') academic_year = request.POST.get('academic_year') grades = []

    for key, score in request.POST.items():
        if key.startswith('grades'):
            student_id = key.split('[')[1].split(']')[0]
            if score:
                try:
                    score = int(score)  # Parse as integer
                    if not (0 <= score <= 100):
                        messages.error(request, f"Score for student ID {student_id} must be an integer between 0 and 100")
                        continue
                    grades.append({'student_id': student_id, 'score': score})
                except ValueError:
                    messages.error(request, f"Invalid score for student ID {student_id}: must be an integer")
                    continue

    if not all([level_id, subject_id, academic_year, grades]):
        messages.error(request, "Missing required fields")
        return redirect('gradesheet-home')

    try:
        academic_year_obj = AcademicYear.objects.get(name=academic_year)
        saved_grades = []
        skipped_students = []
        errors = []
        affected_student_ids = []

        for grade_data in grades:
            student_id = grade_data['student_id']
            score = grade_data['score']
            grade_period_id = period_id

            enrollment = get_enrollment_by_student_level(student_id, level_id, academic_year_obj.id)
            if not enrollment:
                skipped_students.append(student_id)
                continue

            grade, result = save_grade(enrollment, subject_id, grade_period_id, score, request)
            if grade:
                saved_grades.append(grade.id)
                affected_student_ids.append(student_id)
            else:
                errors.append(f"Student ID {student_id}: {result}")

        if saved_grades:
            StudentGradeSheetPDF.objects.filter(
                level_id=level_id,
                student_id__in=affected_student_ids,
                academic_year=academic_year_obj
            ).delete()
            logger.info(f"Deleted existing PDFs for students {affected_student_ids} due to new grades.")

        if errors:
            messages.error(request, f"Some grades failed: {', '.join(errors)}")
            return redirect('gradesheet-home')

        messages.success(request, f"Grades saved successfully for {len(saved_grades)} students")
        return redirect(f"{reverse('gradesheet-home')}?level_id={level_id}&academic_year={academic_year}")

    except AcademicYear.DoesNotExist:
        messages.error(request, f"Invalid academic year: {academic_year}")
        return redirect('gradesheet-home')
    except Exception as e:
        logger.error(f"Error in input_grades_view: {str(e)}")
        messages.error(request, f"Error saving grades: {str(e)}")
        return redirect('gradesheet-home')

return redirect('gradesheet-home')

class ReportCardPrintView(APIView): """POST /api/grade_sheets/report_card_print/ - Generate yearly report card PDFs.""" def post(self, request): level_id = request.data.get("level_id") student_id = request.data.get("student_id") academic_year = request.data.get("academic_year")

    if not level_id:
        return Response({"error": "level_id is required"}, status=status.HTTP_400_BAD_REQUEST)

    try:
        academic_year_obj = AcademicYear.objects.get(name=academic_year) if academic_year else None
        status_obj = PassFailedStatus.objects.filter(
            student_id=student_id, level_id=level_id, academic_year=academic_year_obj
        ).first() if student_id else None
        pass_template = status_obj.status in ['PASS', 'CONDITIONAL'] if status_obj else True
        pdf_paths = generate_yearly_gradesheet_pdf(
            level_id=level_id,
            student_id=student_id,
            pass_template=pass_template,
            academic_year=academic_year
        )

        if not pdf_paths:
            return Response({"error": "No PDFs generated"}, status=status.HTTP_404_NOT_FOUND)

        pdf_path = pdf_paths[0]
        pdf_filename = os.path.basename(pdf_path)
        model = StudentGradeSheetPDF if student_id else LevelGradeSheetPDF
        model.objects.update_or_create(
            level_id=level_id,
            student_id=student_id if student_id else None,
            academic_year=academic_year_obj,
            defaults={'pdf_path': pdf_path, 'filename': pdf_filename}
        )

        view_url = f"{settings.MEDIA_URL}output_gradesheets/{pdf_filename}"
        return Response({
            "message": "PDF generated successfully",
            "view_url": view_url,
            "pdf_path": pdf_path
        })

    except Exception as e:
        logger.error(f"Error generating PDF: {str(e)}")
        return Response({"error": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

def cors_test(request): """Test CORS configuration.""" return Response({"message": "CORS test successful"}, status=status.HTTP_200_OK) but no avearages are appearing on my react fronend:

This gradesheets component i will also make sure it aligns with what i need doing development: