{{-- ================================================================ STUDENT DASHBOARD RESULT ANALYSIS + CBT ANALYSIS ================================================================ --}} @extends('backend.student_backend.student_dashboard') @section('student') @php /* |-------------------------------------------------------------------------- | IMPORT REQUIRED MODELS |-------------------------------------------------------------------------- | | We are keeping the calculations inside this Blade because the | existing dashboard structure already works this way. | | Later, when the system becomes very large, these calculations | can be moved into a Controller/Service for better performance. | */ use App\Models\Student; use App\Models\Result; use App\Models\Subject; use App\Models\StudentClasses; use App\Models\AssignClassSubjectStudent; use App\Models\CbtTest; use App\Models\CbtAttempt; use Illuminate\Support\Facades\DB; /* |-------------------------------------------------------------------------- | 1. GET LOGGED-IN STUDENT |-------------------------------------------------------------------------- | | We NEVER use a student ID supplied by the browser. | | Instead, we use the authenticated user's ID and find the student | belonging to that user. | | This prevents Student A from changing a URL and viewing Student B's | results. | */ $loggedInStudent = Student::where('user_id', auth()->id())->first(); /* |-------------------------------------------------------------------------- | SAFETY CHECK |-------------------------------------------------------------------------- | | If no student profile is connected to the logged-in account, | create empty collections so the dashboard does not crash. | */ if (!$loggedInStudent) { $assignedRecords = collect(); $studentResults = collect(); $studentPerformance = collect(); $subjectPerformance = collect(); $classPerformance = collect(); } else { /* |-------------------------------------------------------------------------- | 2. GET THE STUDENT'S ASSIGNED SUBJECTS / CLASSES |-------------------------------------------------------------------------- | | VERY IMPORTANT: | | We do NOT simply get every class or every subject. | | We use AssignClassSubjectStudent because that table tells us: | | student_id | student_classes_id | subject_id | session | term | | Therefore, this is the correct source for determining what | this particular student is allowed to see. | */ $assignedRecords = AssignClassSubjectStudent::with([ 'subject', 'class' ]) ->where('student_id', $loggedInStudent->id) ->get(); /* |-------------------------------------------------------------------------- | 3. CURRENT TERM AND SESSION |-------------------------------------------------------------------------- */ $currentTerm = DB::table('terms') ->where('is_current', 1) ->value('name'); $currentSession = DB::table('academic_sessions') ->where('is_current', 1) ->value('name'); /* |-------------------------------------------------------------------------- | 4. FILTER DATA |-------------------------------------------------------------------------- | | We only display terms/sessions that actually appear in the | student's assignment records. | */ $availableTerms = $assignedRecords ->pluck('term') ->filter() ->unique() ->values(); $availableSessions = $assignedRecords ->pluck('session') ->filter() ->unique() ->sort() ->values(); /* |-------------------------------------------------------------------------- | 5. SELECTED FILTER VALUES |-------------------------------------------------------------------------- | | Student can choose: | | - Session | - Term | - Class | | Subject does not need a separate filter because the dashboard | analyzes all subjects assigned to the selected class. | */ $selectedTerm = request( 'analysis_term', $currentTerm ); $selectedSession = request( 'analysis_session', $currentSession ); $selectedClass = request( 'analysis_class', 'all' ); /* |-------------------------------------------------------------------------- | 6. FILTER ASSIGNMENTS |-------------------------------------------------------------------------- | | First filter the assignment table. | | This guarantees that the student only analyzes subjects/classes | actually assigned to that student. | */ $filteredAssignments = $assignedRecords ->filter(function ($assignment) use ( $selectedTerm, $selectedSession, $selectedClass ) { /* |-------------------------------------------------------------------------- | SESSION MATCH |-------------------------------------------------------------------------- */ if ( $selectedSession && $assignment->session != $selectedSession ) { return false; } /* |-------------------------------------------------------------------------- | TERM MATCH |-------------------------------------------------------------------------- */ if ( $selectedTerm && $assignment->term != $selectedTerm ) { return false; } /* |-------------------------------------------------------------------------- | CLASS MATCH |-------------------------------------------------------------------------- */ if ( $selectedClass !== 'all' && (string) $assignment->student_classes_id !== (string) $selectedClass ) { return false; } return true; }) ->values(); /* |-------------------------------------------------------------------------- | 7. GET ASSIGNED CLASS IDs |-------------------------------------------------------------------------- */ $assignedClassIds = $filteredAssignments ->pluck('student_classes_id') ->filter() ->unique() ->values(); /* |-------------------------------------------------------------------------- | 8. GET ASSIGNED SUBJECT IDs |-------------------------------------------------------------------------- */ $assignedSubjectIds = $filteredAssignments ->pluck('subject_id') ->filter() ->unique() ->values(); /* |-------------------------------------------------------------------------- | 9. GET THE STUDENT'S RESULTS |-------------------------------------------------------------------------- | | The result query is restricted by: | | 1. Logged-in student | 2. Selected session | 3. Selected term | 4. Assigned classes | 5. Assigned subjects | */ $resultQuery = Result::with([ 'subject', 'studentClass' ]) ->where( 'student_id', $loggedInStudent->id ) ->where( 'term', $selectedTerm ) ->where( 'session', $selectedSession ); /* |-------------------------------------------------------------------------- | ONLY ASSIGNED CLASSES |-------------------------------------------------------------------------- */ if ($assignedClassIds->count() > 0) { $resultQuery->whereIn( 'student_classes_id', $assignedClassIds ); } else { /* |-------------------------------------------------------------------------- | If no assignment exists, return no result. |-------------------------------------------------------------------------- */ $resultQuery->whereRaw('1 = 0'); } /* |-------------------------------------------------------------------------- | ONLY ASSIGNED SUBJECTS |-------------------------------------------------------------------------- */ if ($assignedSubjectIds->count() > 0) { $resultQuery->whereIn( 'subject_id', $assignedSubjectIds ); } else { $resultQuery->whereRaw('1 = 0'); } /* |-------------------------------------------------------------------------- | GET RESULTS |-------------------------------------------------------------------------- */ $studentResults = $resultQuery ->orderBy('subject_id') ->get(); /* |-------------------------------------------------------------------------- | 10. PASS MARK |-------------------------------------------------------------------------- | | Your existing school result system uses 40 as the pass mark. | */ $passMark = 40; /* |-------------------------------------------------------------------------- | 11. TOTAL SUBJECTS |-------------------------------------------------------------------------- */ $totalSubjects = $studentResults ->pluck('subject_id') ->unique() ->count(); /* |-------------------------------------------------------------------------- | 12. OVERALL STUDENT AVERAGE |-------------------------------------------------------------------------- | | Each result total is already stored in the database. | | Example: | | Mathematics = 75 | English = 65 | Biology = 80 | | Average: | | (75 + 65 + 80) / 3 | | We do NOT recalculate CA + Exam here because your Result model | already stores the final total. | */ $overallAverage = round( (float) ( $studentResults->avg('total') ?? 0 ), 1 ); /* |-------------------------------------------------------------------------- | 13. OVERALL PERFORMANCE PERCENTAGE |-------------------------------------------------------------------------- | | Your result total is already out of 100. | | Therefore: | | Average = Percentage | | Example: | | Average = 68.5 | | Performance = 68.5% | */ $overallPercentage = $overallAverage; /* |-------------------------------------------------------------------------- | 14. PASSED SUBJECTS |-------------------------------------------------------------------------- */ $passedSubjects = $studentResults ->filter(function ($result) use ($passMark) { return (float) $result->total >= $passMark; }) ->count(); /* |-------------------------------------------------------------------------- | 15. FAILED SUBJECTS |-------------------------------------------------------------------------- */ $failedSubjects = $studentResults ->filter(function ($result) use ($passMark) { return (float) $result->total < $passMark; }) ->count(); /* |-------------------------------------------------------------------------- | 16. PASS RATE |-------------------------------------------------------------------------- */ $passRate = $studentResults->count() > 0 ? round( ( $passedSubjects / $studentResults->count() ) * 100, 1 ) : 0; /* |-------------------------------------------------------------------------- | 17. HIGHEST SUBJECT SCORE |-------------------------------------------------------------------------- */ $highestResult = $studentResults ->sortByDesc('total') ->first(); /* |-------------------------------------------------------------------------- | 18. LOWEST SUBJECT SCORE |-------------------------------------------------------------------------- */ $lowestResult = $studentResults ->sortBy('total') ->first(); /* |-------------------------------------------------------------------------- | 19. SUBJECT PERFORMANCE |-------------------------------------------------------------------------- | | Since each student normally has one result per subject for a | selected term/session, this gives the subject score. | */ $subjectPerformance = $studentResults ->groupBy('subject_id') ->map(function ($rows) { $first = $rows->first(); return (object) [ 'subject_id' => $first->subject_id, 'subject_name' => optional($first->subject)->subject_name ?? 'Unknown Subject', 'average' => round( (float) $rows->avg('total'), 1 ), 'highest' => round( (float) $rows->max('total'), 1 ), 'lowest' => round( (float) $rows->min('total'), 1 ), ]; }) ->sortByDesc('average') ->values(); /* |-------------------------------------------------------------------------- | 20. BEST SUBJECT |-------------------------------------------------------------------------- */ $bestSubject = $subjectPerformance ->sortByDesc('average') ->first(); /* |-------------------------------------------------------------------------- | 21. WEAKEST SUBJECT |-------------------------------------------------------------------------- */ $weakestSubject = $subjectPerformance ->sortBy('average') ->first(); /* |-------------------------------------------------------------------------- | 22. CLASS PERFORMANCE |-------------------------------------------------------------------------- | | If the student has more than one assigned class, we calculate | the student's performance separately for each class. | */ $classPerformance = $studentResults ->groupBy('student_classes_id') ->map(function ($rows, $classId) use ($passMark) { $first = $rows->first(); $average = (float) $rows->avg('total'); $passed = $rows ->where('total', '>=', $passMark) ->count(); $subjectCount = $rows ->pluck('subject_id') ->unique() ->count(); $passRate = $rows->count() > 0 ? round( ( $passed / $rows->count() ) * 100, 1 ) : 0; return (object) [ 'class_id' => $classId, 'class_name' => optional($first->studentClass)->class_name ?? 'Unknown Class', 'average' => round($average, 1), 'subjects' => $subjectCount, 'passed' => $passed, 'failed' => $rows->count() - $passed, 'pass_rate' => $passRate, ]; }) ->sortByDesc('average') ->values(); /* |-------------------------------------------------------------------------- | 23. STUDENT'S POSITION-STYLE INFORMATION |-------------------------------------------------------------------------- | | We compare the student's average with other students in the | SAME selected class/session/term. | | This is NOT a stored school position. | | It is simply an analytical position calculated from the same | result records. | */ /* |-------------------------------------------------------------------------- | DETERMINE THE CLASS USED FOR POSITION |-------------------------------------------------------------------------- | | If the student selected a particular class, use that class. | If "all" is selected, use the student's actual class from the | currently filtered result records. | | This is important because position must NEVER compare students | from different classes. | */ $positionClassId = null; if ($selectedClass !== 'all') { $positionClassId = $selectedClass; } elseif ($studentResults->count() > 0) { $positionClassId = $studentResults ->pluck('student_classes_id') ->filter() ->first(); } /* |-------------------------------------------------------------------------- | GET STUDENTS FROM THE SAME CLASS ONLY |-------------------------------------------------------------------------- */ $comparisonResultsQuery = Result::where( 'term', $selectedTerm ) ->where( 'session', $selectedSession ); if ($positionClassId) { $comparisonResultsQuery->where( 'student_classes_id', $positionClassId ); } else { $comparisonResultsQuery->whereRaw('1 = 0'); } /* |-------------------------------------------------------------------------- | GET CLASS COMPARISON RESULTS |-------------------------------------------------------------------------- */ $comparisonResults = $comparisonResultsQuery->get(); /* |-------------------------------------------------------------------------- | CALCULATE EVERY STUDENT'S AVERAGE IN THIS CLASS |-------------------------------------------------------------------------- | | Each student is grouped separately and their result totals are | averaged. Because the query above is already restricted to one | class, students from another class cannot affect the position. | */ $classStudentAverages = $comparisonResults ->groupBy('student_id') ->map(function ($rows) { return round( (float) $rows->avg('total'), 2 ); }) ->sortDesc() ->values(); /* |-------------------------------------------------------------------------- | CALCULATE THIS STUDENT'S AVERAGE FOR THE SAME CLASS ONLY |-------------------------------------------------------------------------- */ $positionStudentResults = $studentResults ->filter(function ($result) use ($positionClassId) { return (string) $result->student_classes_id === (string) $positionClassId; }) ->values(); $positionStudentAverage = $positionStudentResults->count() > 0 ? round( (float) $positionStudentResults->avg('total'), 2 ) : null; /* |-------------------------------------------------------------------------- | STUDENT ANALYTICAL POSITION |-------------------------------------------------------------------------- | | Position is calculated by counting only students whose average | is HIGHER than this student's average. | | Example: | Student A = 80.80% | Student B = 67.34% | | Student A = 1st | Student B = 2nd | | Students with the same average receive the same position. | */ $studentPosition = null; if ( $positionStudentAverage !== null && $classStudentAverages->count() > 0 ) { $studentsAboveThisStudent = $classStudentAverages ->filter(function ($otherAverage) use ( $positionStudentAverage ) { return $otherAverage > $positionStudentAverage; }) ->count(); $studentPosition = $studentsAboveThisStudent + 1; } /* |-------------------------------------------------------------------------- | NUMBER OF STUDENTS USED FOR COMPARISON |-------------------------------------------------------------------------- */ $studentsCompared = $classStudentAverages->count(); /* |-------------------------------------------------------------------------- | CLASS COMPARISON AVERAGE |-------------------------------------------------------------------------- */ $comparisonAverage = round( (float) ( $classStudentAverages->avg() ?? 0 ), 1 ); /* |-------------------------------------------------------------------------- | ABOVE / BELOW CLASS AVERAGE |-------------------------------------------------------------------------- */ $aboveClassAverage = $positionStudentAverage !== null && $positionStudentAverage > $comparisonAverage; $belowClassAverage = $positionStudentAverage !== null && $positionStudentAverage < $comparisonAverage; /* |-------------------------------------------------------------------------- | SCORE BAND ANALYSIS |-------------------------------------------------------------------------- | | This tells the student how many subjects fall inside each | performance band. | */ $scoreBands = [ '80 - 100' => 0, '70 - 79' => 0, '60 - 69' => 0, '50 - 59' => 0, '40 - 49' => 0, 'Below 40' => 0, ]; foreach ($studentResults as $result) { $score = (float) $result->total; if ($score >= 80) { $scoreBands['80 - 100']++; } elseif ($score >= 70) { $scoreBands['70 - 79']++; } elseif ($score >= 60) { $scoreBands['60 - 69']++; } elseif ($score >= 50) { $scoreBands['50 - 59']++; } elseif ($score >= 40) { $scoreBands['40 - 49']++; } else { $scoreBands['Below 40']++; } } /* |-------------------------------------------------------------------------- | GRADE DISTRIBUTION |-------------------------------------------------------------------------- */ $gradeDistribution = $studentResults ->groupBy(function ($result) { return $result->grade ?: 'Not Graded'; }) ->map(function ($rows) { return $rows->count(); }) ->sortKeys() ->toArray(); /* |-------------------------------------------------------------------------- | FULL PUBLICATION STATUS |-------------------------------------------------------------------------- | | Your Result model has the legacy "publish" field. | | We count fully published result rows here. | */ $publishedResults = $studentResults ->filter(function ($result) { return (int) $result->publish === 1; }) ->count(); /* |-------------------------------------------------------------------------- | PUBLICATION RATE |-------------------------------------------------------------------------- */ $publicationRate = $studentResults->count() > 0 ? round( ( $publishedResults / $studentResults->count() ) * 100, 1 ) : 0; /* |-------------------------------------------------------------------------- | ============================================================== | CBT ANALYSIS | ============================================================== |-------------------------------------------------------------------------- | | We keep CBT analysis on the same student dashboard. | */ /* |-------------------------------------------------------------------------- | CBT TESTS FOR CURRENT FILTER |-------------------------------------------------------------------------- */ $cbtTests = CbtTest::query() ->where('session', $selectedSession) ->where('term', $selectedTerm) ->get(); /* |-------------------------------------------------------------------------- | CBT ATTEMPTS BELONGING TO THIS STUDENT |-------------------------------------------------------------------------- */ $cbtAttempts = CbtAttempt::query() ->where( 'student_id', $loggedInStudent->id ) ->whereIn( 'cbt_test_id', $cbtTests->pluck('id') ) ->get(); /* |-------------------------------------------------------------------------- | CBT TESTS TAKEN |-------------------------------------------------------------------------- */ $cbtTestsTaken = $cbtAttempts ->pluck('cbt_test_id') ->unique() ->count(); /* |-------------------------------------------------------------------------- | CBT AVERAGE |-------------------------------------------------------------------------- */ $cbtAverage = round( (float) ( $cbtAttempts->avg('score') ?? 0 ), 1 ); /* |-------------------------------------------------------------------------- | CBT HIGHEST SCORE |-------------------------------------------------------------------------- */ $cbtHighest = round( (float) ( $cbtAttempts->max('score') ?? 0 ), 1 ); /* |-------------------------------------------------------------------------- | CBT LOWEST SCORE |-------------------------------------------------------------------------- */ $cbtLowest = round( (float) ( $cbtAttempts->min('score') ?? 0 ), 1 ); /* |-------------------------------------------------------------------------- | CBT PASS COUNT |-------------------------------------------------------------------------- */ $cbtPassCount = $cbtAttempts ->filter(function ($attempt) { return (float) $attempt->score >= 40; }) ->count(); /* |-------------------------------------------------------------------------- | CBT PASS RATE |-------------------------------------------------------------------------- */ $cbtPassRate = $cbtAttempts->count() > 0 ? round( ( $cbtPassCount / $cbtAttempts->count() ) * 100, 1 ) : 0; } /* |-------------------------------------------------------------------------- | FALLBACK EMPTY VARIABLES |-------------------------------------------------------------------------- | | These make the dashboard safe if no student profile exists. |-------------------------------------------------------------------------- */ $currentTerm = $currentTerm ?? null; $currentSession = $currentSession ?? null; $availableTerms = $availableTerms ?? collect(); $availableSessions = $availableSessions ?? collect(); $filteredAssignments = $filteredAssignments ?? collect(); $assignedClassIds = $assignedClassIds ?? collect(); $assignedSubjectIds = $assignedSubjectIds ?? collect(); $studentResults = $studentResults ?? collect(); $studentPerformance = $studentPerformance ?? collect(); $subjectPerformance = $subjectPerformance ?? collect(); $classPerformance = $classPerformance ?? collect(); $scoreBands = $scoreBands ?? []; $gradeDistribution = $gradeDistribution ?? []; $totalSubjects = $totalSubjects ?? 0; $overallAverage = $overallAverage ?? 0; $overallPercentage = $overallPercentage ?? 0; $passedSubjects = $passedSubjects ?? 0; $failedSubjects = $failedSubjects ?? 0; $passRate = $passRate ?? 0; $publishedResults = $publishedResults ?? 0; $publicationRate = $publicationRate ?? 0; $comparisonAverage = $comparisonAverage ?? 0; $studentPosition = $studentPosition ?? null; $studentsCompared = $studentsCompared ?? 0; $cbtTestsTaken = $cbtTestsTaken ?? 0; $cbtAverage = $cbtAverage ?? 0; $cbtHighest = $cbtHighest ?? 0; $cbtLowest = $cbtLowest ?? 0; $cbtPassRate = $cbtPassRate ?? 0; $cbtAttempts = $cbtAttempts ?? collect(); $cbtTests = $cbtTests ?? collect(); @endphp @php use Illuminate\Support\Facades\Auth; use App\Models\StudentBill; use App\Models\StudentPayment; // ============================================================ // 1. LOGGED-IN STUDENT // ============================================================ $student = \App\Models\Student::where( 'user_id', Auth::id() )->first(); // ============================================================ // 2. CURRENT TERM & CURRENT SESSION // ============================================================ $currentTerm = DB::table('terms') ->where('is_current', 1) ->value('name'); $currentSession = DB::table('academic_sessions') ->where('is_current', 1) ->value('name'); // ============================================================ // STUDENT BILLING INFORMATION // IMPORTANT: // EVERYTHING BELOW IS FOR CURRENT TERM + CURRENT SESSION ONLY. // NO PREVIOUS TERM OR PREVIOUS SESSION IS INCLUDED. // ============================================================ $totalBill = 0; $totalPaid = 0; $totalOutstanding = 0; // ============================================================ // 3. GET CURRENT STUDENT BILL // ============================================================ $currentBill = StudentBill::where( 'student_id', $student->id ) ->where( 'term', $currentTerm ) ->where( 'session', $currentSession ) ->first(); // ============================================================ // 4. CURRENT TERM & CURRENT SESSION BILL // ============================================================ if ($currentBill) { $totalBill = (float) ( $currentBill->total_bill ?? 0 ); } // ============================================================ // 5. CURRENT TERM & CURRENT SESSION PAYMENTS // ============================================================ if ($currentBill) { $totalPaid = StudentPayment::where( 'student_bill_id', $currentBill->id ) ->sum('amount'); } $totalPaid = (float) $totalPaid; // ============================================================ // 6. CURRENT TERM & CURRENT SESSION OUTSTANDING // // Outstanding = Current Term Bill - Current Term Payment // // NO PREVIOUS OUTSTANDING IS INCLUDED. // ============================================================ $totalOutstanding = max( 0, $totalBill - $totalPaid ); // ============================================================ // 7. TOTAL SUBJECTS // CURRENT TERM + CURRENT SESSION ONLY // ============================================================ $totalSubjects = AssignClassSubjectStudent::where( 'student_id', $student->id ) ->where( 'term', $currentTerm ) ->where( 'session', $currentSession ) ->distinct('subject_id') ->count('subject_id'); // ============================================================ // 8. GET STUDENT CLASS // CURRENT TERM + CURRENT SESSION ONLY // ============================================================ $studentClassId = AssignClassSubjectStudent::where( 'student_id', $student->id ) ->where( 'term', $currentTerm ) ->where( 'session', $currentSession ) ->value('student_classes_id'); // ============================================================ // 8 STUDENT DEBT LIMIT // LIMIT IS SET BY THE BURSAR // CURRENT CLASS + CURRENT TERM + CURRENT SESSION //============================================================ $debtLimit = \App\Models\DebtLimit::where( 'class_id', $studentClassId ) ->where( 'term', $currentTerm ) ->where( 'session', $currentSession ) ->value('debt_limit'); $debtLimit = $debtLimit !== null ? (float) $debtLimit : null; /* ============================================================ PERFORMANCE ACCESS Student can access performance when: Outstanding <= Debt Limit Student is restricted when: Outstanding > Debt Limit ============================================================ */ $performanceRestricted = false; if ($debtLimit !== null) { $performanceRestricted = $totalOutstanding > $debtLimit; } /* ============================================================ AMOUNT REQUIRED TO MEET DEBT LIMIT ============================================================ */ $amountRequiredToMeetDebtLimit = 0; if ( $performanceRestricted && $debtLimit !== null ) { $amountRequiredToMeetDebtLimit = max( 0, $totalOutstanding - $debtLimit ); } // ============================================================ // 9. TOTAL CBT TESTS // CURRENT TERM + CURRENT SESSION ONLY // ============================================================ $totalCBTTests = CbtTest::where( 'student_classes_id', $studentClassId ) ->where( 'term', $currentTerm ) ->where( 'session', $currentSession ) ->count(); // ============================================================ // 10. RECENT CBT TESTS // CURRENT TERM + CURRENT SESSION ONLY // ============================================================ $recentCBT = CbtTest::where( 'student_classes_id', $studentClassId ) ->where( 'term', $currentTerm ) ->where( 'session', $currentSession ) ->latest() ->take(5) ->get(); // ============================================================ // 11. GREETING // ============================================================ $hour = now()->format('H'); if ($hour < 12) { $greeting = "Good Morning"; } elseif ($hour < 17) { $greeting = "Good Afternoon"; } else { $greeting = "Good Evening"; } // ============================================================ // 12. GET CLASS NAME // ============================================================ $studentClassName = \App\Models\StudentClasses::where( 'id', $studentClassId )->value('class_name'); // ============================================================ // . GET PROMOTION STATUS // ============================================================ $studentPromotion = AssignClassSubjectStudent::where( 'student_id', $student->id ) ->where( 'term', $currentTerm ) ->where( 'session', $currentSession ) ->whereNotNull( 'promoted' ) ->where( 'promoted', '!=', '' ) ->value('promoted'); // ============================================================ // 13. CBT PERFORMANCE DATA // CURRENT TERM + CURRENT SESSION ONLY // ============================================================ $attemptsQuery = CbtAttempt::where( 'student_id', $student->id ) ->where( 'status', 'completed' ) ->whereHas( 'test', function ($query) use ( $currentTerm, $currentSession ) { $query->where( 'term', $currentTerm ) ->where( 'session', $currentSession ); } ); // ============================================================ // TOTAL ATTEMPTS // ============================================================ $totalAttempts = $attemptsQuery->count(); // ============================================================ // PASS COUNT // ============================================================ $passCount = (clone $attemptsQuery) ->where( 'score', '>=', 50 ) ->count(); // ============================================================ // FAIL COUNT // ============================================================ $failCount = (clone $attemptsQuery) ->where( 'score', '<', 50 ) ->count(); // ============================================================ // AVERAGE SCORE // ============================================================ $averageScore = round( $attemptsQuery->avg('score') ?? 0, 1 ); // ============================================================ // HIGHEST SCORE // ============================================================ $highestScore = $attemptsQuery->max('score') ?? 0; // ============================================================ // SCORE TREND - LAST 6 TESTS // ============================================================ $scoreTrend = (clone $attemptsQuery) ->latest() ->take(6) ->get() ->reverse(); $trendLabels = []; $trendScores = []; foreach ($scoreTrend as $attempt) { $trendLabels[] = $attempt->created_at->format('d M'); $trendScores[] = $attempt->score; } @endphp {{-- ========================================================= CSS EFFECTS ========================================================== --}} {{-- ========================================================= STUDENT DASHBOARD ========================================================== --}}
{{-- ===================================================== 1. WELCOME HEADER ====================================================== --}}

Student Academic Dashboard

Result performance, academic analysis and CBT overview

{{ $selectedTerm ?: 'No Term' }} | {{ $selectedSession ?: 'No Session' }}
{{-- ============================================================ ANALYSIS FILTER ============================================================= --}}
Analysis Filter
{{-- SESSION --}}
{{-- TERM --}}
{{-- CLASS --}}
{{-- ========================================================= STUDENT FINANCIAL SUMMARY ========================================================== --}}
{{-- ===================================================== TOTAL BILLS ====================================================== --}}

Total Bills

₦{{ number_format( $totalBill, 2 ) }}

Current term bill
{{-- ===================================================== PAYMENT MADE ====================================================== --}}

Payment Made

₦{{ number_format( $totalPaid, 2 ) }}

Current term payment
{{-- ===================================================== OUTSTANDING ====================================================== --}}

Outstanding

₦{{ number_format( $totalOutstanding, 2 ) }}

Current term outstanding
{{-- ========================================================= 2. QUICK STATS CARDS ========================================================== --}}
{{-- TOTAL SUBJECTS --}}

My Subjects

{{ $totalSubjects }}

{{-- TOTAL CBT TESTS --}}

Test Available

{{ $totalCBTTests }}

{{-- TESTS TAKEN --}}

Tests Taken

{{ $totalAttempts }}

{{-- ========================================================= 3. RECENT CBT & RESULTS ========================================================== --}}
Recent CBT Tests
@if($recentCBT->isEmpty())

No CBT tests available.

@else

Attempt CBT

Take Test @endif
{{-- ================================================================ PAGE HEADER ================================================================ --}}
{{-- ============================================================ STUDENT INFORMATION ============================================================= --}} @if($loggedInStudent)
@if($loggedInStudent->photo) Student Photo @else
@endif

{{ $loggedInStudent->name }}

Student ID: {{ $loggedInStudent->user->user_name }}

{{ $totalSubjects }} Subjects Assessed {{ $passedSubjects }} Passed {{ $failedSubjects }} Failed
@endif {{-- ============================================================ RESULT SUMMARY CARDS ============================================================= --}}
{{-- OVERALL AVERAGE --}}

Overall Average

{{ number_format($overallAverage, 1) }}%

Based on all assessed subjects
{{-- PASS RATE --}}

Pass Rate

{{ number_format($passRate, 1) }}%

Pass mark: {{ $passMark }}%
{{-- PASSED --}}

Subjects Passed

{{ $passedSubjects }}

Out of {{ $totalSubjects }} subjects
{{-- FAILED --}}

Subjects Below Pass

{{ $failedSubjects }}

Subjects requiring attention
{{-- ============================================================ SECONDARY ANALYTICS ============================================================= --}}
{{-- BEST SUBJECT --}}
Best Subject
@if($bestSubject)
{{ $bestSubject->subject_name }}

{{ number_format($bestSubject->average, 1) }}%

@else

No result available

@endif
{{-- WEAKEST SUBJECT --}}
Subject Requiring Attention
@if($weakestSubject)
{{ $weakestSubject->subject_name }}

{{ number_format($weakestSubject->average, 1) }}%

@else

No result available

@endif
{{-- CLASS COMPARISON --}}
Selected Class Average

{{ number_format($comparisonAverage, 1) }}%

@if($aboveClassAverage) Above Class Average @elseif($belowClassAverage) Below Class Average @else Equal to Class Average @endif
{{-- POSITION ANALYSIS --}}
Analytical Position
@if($studentPosition)

#{{ $studentPosition }}

Among {{ $studentsCompared }} students compared @else

Not enough data

@endif
{{-- ============================================================ PERFORMANCE CHARTS ============================================================= --}}
{{-- SUBJECT PERFORMANCE --}}
Subject Performance
Your performance in each assigned subject
{{-- PERFORMANCE DONUT --}}
Overall Performance

{{ number_format($overallPercentage, 1) }}%

Overall Performance
{{-- ============================================================ SCORE BAND + GRADE DISTRIBUTION ============================================================= --}}
Score Distribution
Grade Distribution
{{-- ============================================================ CLASS PERFORMANCE ============================================================= --}} @if($classPerformance->count() > 0)
My Assigned Class Performance
This section is useful when a student belongs to more than one class.
@foreach( $classPerformance as $index => $class ) @endforeach
S/N Class Subjects Average Pass Rate Status
{{ $index + 1 }} {{ $class->class_name }} {{ $class->subjects }} {{ number_format($class->average, 1) }}%
{{ number_format($class->pass_rate, 1) }}%
@if($class->average >= 70) Excellent @elseif($class->average >= 60) Good @elseif($class->average >= 50) Fair @else Needs Attention @endif
@endif {{-- ============================================================ COMPLETE RESULT TABLE ============================================================= --}} @if(!$performanceRestricted)
My Result Analysis
{{ $selectedTerm }} | {{ $selectedSession }}
{{ $studentResults->count() }} Result Records
@if($studentResults->count() > 0)
@foreach( $studentResults as $index => $result ) @endforeach
S/N Class Subject CA1 CA2 CA3 Exam Total Grade Remark Status
{{ $index + 1 }} {{ optional($result->studentClass)->class_name ?? 'N/A' }} {{ optional($result->subject)->subject_name ?? 'Unknown Subject' }} {{ number_format((float)$result->ca1, 1) }} {{ number_format((float)$result->ca2, 1) }} {{ number_format((float)$result->ca3, 1) }} {{ number_format((float)$result->exam, 1) }} {{ number_format((float)$result->total, 1) }} {{ $result->grade ?: 'N/A' }} {{ $result->remark ?: 'N/A' }} @if((float)$result->total >= $passMark) Passed @else Attention @endif
Overall Average {{ number_format($overallAverage, 1) }} {{ number_format($overallPercentage, 1) }}%
@else
No Result Available

No result has been recorded for the selected session, term and assigned class/subjects.

@endif
@else {{-- ============================================================ PERFORMANCE RESTRICTED BY BURSAR DEBT LIMIT ============================================================= --}}

Academic Performance Restricted

Your academic performance is currently unavailable because your outstanding balance is above the allowed debt limit set by the school bursary.

Current Outstanding: ₦{{ number_format($totalOutstanding, 2) }}

Allowed Debt Limit: ₦{{ number_format($debtLimit, 2) }}

Amount Required: ₦{{ number_format($amountRequiredToMeetDebtLimit, 2) }}

Please balance your account by at least ₦{{ number_format($amountRequiredToMeetDebtLimit, 2) }} to bring your outstanding balance within the allowed debt limit and access your academic performance.
@endif {{-- ============================================================ PERFORMANCE INSIGHT ============================================================= --}}
{{-- STRONG PERFORMANCE --}}
Strong Performance
@if($bestSubject)

Your highest-performing subject is {{ $bestSubject->subject_name }} with a score of {{ number_format($bestSubject->average, 1) }}% .

@else

There is not enough result data for performance analysis.

@endif
{{-- IMPROVEMENT AREA --}}
Area Requiring Attention
@if($weakestSubject)

The subject requiring the most attention based on this analysis is {{ $weakestSubject->subject_name }} with {{ number_format($weakestSubject->average, 1) }}% .

@else

There is not enough result data for performance analysis.

@endif
{{-- ============================================================ CBT SECTION ============================================================= --}}

CBT Performance

Computer Based Test performance for the selected term and session
{{-- CBT TAKEN --}}
CBT Tests Taken

{{ $cbtTestsTaken }}

{{-- CBT AVERAGE --}}
CBT Average

{{ number_format($cbtAverage, 1) }}%

{{-- CBT HIGHEST --}}
Highest CBT Score

{{ number_format($cbtHighest, 1) }}%

{{-- CBT PASS RATE --}}
CBT Pass Rate

{{ number_format($cbtPassRate, 1) }}%

{{-- ============================================================ CBT ATTEMPT TABLE ============================================================= --}}
CBT Attempt History
@if($cbtAttempts->count() > 0)
@foreach( $cbtAttempts as $index => $attempt ) @php $test = $cbtTests ->firstWhere( 'id', $attempt->cbt_test_id ); $score = (float) $attempt->score; @endphp @endforeach
S/N Test Score Status
{{ $index + 1 }} {{ optional($test)->subject->subject_name ?? 'CBT Test' }} {{ number_format($score, 1) }}% @if($score >= 40) Passed @else Below Pass Mark @endif
@else

No CBT attempt has been recorded for this term/session.

@endif
{{-- ================================================================ CHART.JS ================================================================ --}} @endsection