This commit is contained in:
root
2026-05-30 03:29:48 -04:00
parent 9ee75fe4cc
commit 079c869477
+51 -12
View File
@@ -1014,9 +1014,9 @@ $drawRankCell = static function (
$pdf->Rect($x, $y, $w, $h); $pdf->Rect($x, $y, $w, $h);
$pdf->SetXY($x + $pad, $y + 3); $pdf->SetXY($x + $pad, $y + 3);
$pdf->SetFont('Helvetica', 'B', 11); $pdf->SetFont('Helvetica', 'B', 11);
$pdf->Write(5, ' Rank: '); $pdf->Write(5, ' Class Rank: ');
$labelWidth = $pdf->GetStringWidth(' Rank: '); $labelWidth = $pdf->GetStringWidth(' Class Rank: ');
$pdf->SetFont('Helvetica', '', 12); $pdf->SetFont('Helvetica', '', 12);
$pdf->SetXY($x + 2 + $labelWidth, $y + 3); $pdf->SetXY($x + 2 + $labelWidth, $y + 3);
$pdf->Write(5, $rankValue); $pdf->Write(5, $rankValue);
@@ -1770,42 +1770,51 @@ $scoresEndY = $pdf->GetY();
} }
$rows = $builder->get()->getResultArray(); $rows = $builder->get()->getResultArray();
if (empty($rows)) { if (empty($rows)) {
return null; return null;
} }
$scoresByStudent = []; $scoresByStudent = [];
$studentIds = [];
foreach ($rows as $row) { foreach ($rows as $row) {
$sid = (int)($row['student_id'] ?? 0); $sid = (int)($row['student_id'] ?? 0);
if ($sid <= 0 || isset($scoresByStudent[$sid])) { if ($sid <= 0 || isset($scoresByStudent[$sid])) {
continue; continue;
} }
$scoreVal = $row['semester_score'] ?? null; $scoreVal = $row['semester_score'] ?? null;
if (!is_numeric($scoreVal)) { if (!is_numeric($scoreVal)) {
continue; continue;
} }
$rawScore = (float)$scoreVal;
$scoresByStudent[$sid] = [ $scoresByStudent[$sid] = [
'student_id' => $sid, 'student_id' => $sid,
'score' => round((float)$scoreVal, 4), 'score' => $rawScore,
'rank_score' => round($rawScore, 1),
'firstname' => trim((string)($row['firstname'] ?? '')), 'firstname' => trim((string)($row['firstname'] ?? '')),
'lastname' => trim((string)($row['lastname'] ?? '')), 'lastname' => trim((string)($row['lastname'] ?? '')),
]; ];
$studentIds[] = $sid;
} }
if (empty($scoresByStudent) || !isset($scoresByStudent[$studentId])) { if (empty($scoresByStudent) || !isset($scoresByStudent[$studentId])) {
return null; return null;
} }
// For Spring, rank by final year average:
// (first semester score + second semester score) / 2.
if ($rankByFinalScore) { if ($rankByFinalScore) {
$studentIds = array_keys($scoresByStudent);
$firstRowsBuilder = $this->db->table('semester_scores ss') $firstRowsBuilder = $this->db->table('semester_scores ss')
->select('ss.student_id, ss.semester_score, ss.updated_at, ss.id') ->select('ss.student_id, ss.semester, ss.semester_score, ss.updated_at, ss.id')
->where('ss.school_year', $schoolYear) ->where('ss.school_year', $schoolYear)
->whereIn('ss.class_section_id', $sectionIds)
->whereIn('ss.student_id', $studentIds) ->whereIn('ss.student_id', $studentIds)
->whereIn('ss.class_section_id', $sectionIds)
->orderBy('ss.updated_at', 'DESC') ->orderBy('ss.updated_at', 'DESC')
->orderBy('ss.id', 'DESC'); ->orderBy('ss.id', 'DESC');
@@ -1814,14 +1823,18 @@ $scoresEndY = $pdf->GetY();
} }
$firstRows = $firstRowsBuilder->get()->getResultArray(); $firstRows = $firstRowsBuilder->get()->getResultArray();
$firstScoresByStudent = []; $firstScoresByStudent = [];
foreach ($firstRows as $row) { foreach ($firstRows as $row) {
$sid = (int)($row['student_id'] ?? 0); $sid = (int)($row['student_id'] ?? 0);
if ($sid <= 0 || isset($firstScoresByStudent[$sid])) { if ($sid <= 0 || isset($firstScoresByStudent[$sid])) {
continue; continue;
} }
$scoreVal = $row['semester_score'] ?? null; $scoreVal = $row['semester_score'] ?? null;
if (!is_numeric($scoreVal)) { if (!is_numeric($scoreVal)) {
continue; continue;
} }
@@ -1835,30 +1848,48 @@ $scoresEndY = $pdf->GetY();
continue; continue;
} }
$rankRow['score'] = round(((float)$firstScoresByStudent[$sid] + (float)$rankRow['score']) / 2, 4); $avg = ((float)$firstScoresByStudent[$sid] + (float)$rankRow['score']) / 2;
$rankRow['score'] = $avg;
$rankRow['rank_score'] = round($avg, 1);
} }
unset($rankRow); unset($rankRow);
if (empty($scoresByStudent) || !isset($scoresByStudent[$studentId])) { if (empty($scoresByStudent) || !isset($scoresByStudent[$studentId])) {
return null; return null;
} }
$scoresByStudent[$studentId]['score'] = round((float)$studentScore, 4); // Force the selected student to use the exact score already computed for the report.
// Then rank using the displayed precision: one decimal.
$scoresByStudent[$studentId]['score'] = (float)$studentScore;
$scoresByStudent[$studentId]['rank_score'] = round((float)$studentScore, 1);
} else {
// Fall: also force selected student to match the report-card computed score.
$scoresByStudent[$studentId]['score'] = (float)$studentScore;
$scoresByStudent[$studentId]['rank_score'] = round((float)$studentScore, 1);
} }
$rankable = array_values($scoresByStudent); $rankable = array_values($scoresByStudent);
usort($rankable, static function (array $a, array $b): int { usort($rankable, static function (array $a, array $b): int {
$scoreCmp = $b['score'] <=> $a['score']; // Highest displayed/rank score first.
$scoreCmp = $b['rank_score'] <=> $a['rank_score'];
if ($scoreCmp !== 0) { if ($scoreCmp !== 0) {
return $scoreCmp; return $scoreCmp;
} }
// Tie-breakers only control display order.
// They do NOT change rank.
$lastCmp = strcasecmp($a['lastname'], $b['lastname']); $lastCmp = strcasecmp($a['lastname'], $b['lastname']);
if ($lastCmp !== 0) { if ($lastCmp !== 0) {
return $lastCmp; return $lastCmp;
} }
$firstCmp = strcasecmp($a['firstname'], $b['firstname']); $firstCmp = strcasecmp($a['firstname'], $b['firstname']);
if ($firstCmp !== 0) { if ($firstCmp !== 0) {
return $firstCmp; return $firstCmp;
} }
@@ -1868,17 +1899,25 @@ $scoresEndY = $pdf->GetY();
$position = null; $position = null;
$previousScore = null; $previousScore = null;
foreach ($rankable as $index => $row) { foreach ($rankable as $index => $row) {
if ($previousScore === null || abs($row['score'] - $previousScore) > 0.0001) { $currentScore = (float)$row['rank_score'];
// Competition ranking:
// 1, 1, 3, 4...
// Same score = same rank.
if ($previousScore === null || abs($currentScore - $previousScore) > 0.0001) {
$position = $index + 1; $position = $index + 1;
$previousScore = $row['score']; $previousScore = $currentScore;
} }
if ((int)$row['student_id'] === $studentId) { if ((int)$row['student_id'] === $studentId) {
$total = count($rankable); $total = count($rankable);
return [ return [
'position' => $position, 'position' => $position,
'total' => $total, 'total' => $total,
'score' => $currentScore,
'display' => $this->formatOrdinal($position) . ' out of ' . $total, 'display' => $this->formatOrdinal($position) . ' out of ' . $total,
]; ];
} }