This commit is contained in:
root
2026-05-30 03:29:48 -04:00
parent 9ee75fe4cc
commit 079c869477
+54 -15
View File
@@ -1014,9 +1014,9 @@ $drawRankCell = static function (
$pdf->Rect($x, $y, $w, $h);
$pdf->SetXY($x + $pad, $y + 3);
$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->SetXY($x + 2 + $labelWidth, $y + 3);
$pdf->Write(5, $rankValue);
@@ -1732,14 +1732,14 @@ $scoresEndY = $pdf->GetY();
];
}
private function calculateTermRanking(
private function calculateTermRanking(
int $studentId,
int $sectionCode,
?int $sectionId,
string $schoolYear,
?string $semester,
?float $studentScore
): ?array {
): ?array {
if ($studentId <= 0 || $schoolYear === '' || $studentScore === null) {
return null;
}
@@ -1770,42 +1770,51 @@ $scoresEndY = $pdf->GetY();
}
$rows = $builder->get()->getResultArray();
if (empty($rows)) {
return null;
}
$scoresByStudent = [];
$studentIds = [];
foreach ($rows as $row) {
$sid = (int)($row['student_id'] ?? 0);
if ($sid <= 0 || isset($scoresByStudent[$sid])) {
continue;
}
$scoreVal = $row['semester_score'] ?? null;
if (!is_numeric($scoreVal)) {
continue;
}
$rawScore = (float)$scoreVal;
$scoresByStudent[$sid] = [
'student_id' => $sid,
'score' => round((float)$scoreVal, 4),
'score' => $rawScore,
'rank_score' => round($rawScore, 1),
'firstname' => trim((string)($row['firstname'] ?? '')),
'lastname' => trim((string)($row['lastname'] ?? '')),
];
$studentIds[] = $sid;
}
if (empty($scoresByStudent) || !isset($scoresByStudent[$studentId])) {
return null;
}
// For Spring, rank by final year average:
// (first semester score + second semester score) / 2.
if ($rankByFinalScore) {
$studentIds = array_keys($scoresByStudent);
$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)
->whereIn('ss.class_section_id', $sectionIds)
->whereIn('ss.student_id', $studentIds)
->whereIn('ss.class_section_id', $sectionIds)
->orderBy('ss.updated_at', 'DESC')
->orderBy('ss.id', 'DESC');
@@ -1814,14 +1823,18 @@ $scoresEndY = $pdf->GetY();
}
$firstRows = $firstRowsBuilder->get()->getResultArray();
$firstScoresByStudent = [];
foreach ($firstRows as $row) {
$sid = (int)($row['student_id'] ?? 0);
if ($sid <= 0 || isset($firstScoresByStudent[$sid])) {
continue;
}
$scoreVal = $row['semester_score'] ?? null;
if (!is_numeric($scoreVal)) {
continue;
}
@@ -1835,30 +1848,48 @@ $scoresEndY = $pdf->GetY();
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);
if (empty($scoresByStudent) || !isset($scoresByStudent[$studentId])) {
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);
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) {
return $scoreCmp;
}
// Tie-breakers only control display order.
// They do NOT change rank.
$lastCmp = strcasecmp($a['lastname'], $b['lastname']);
if ($lastCmp !== 0) {
return $lastCmp;
}
$firstCmp = strcasecmp($a['firstname'], $b['firstname']);
if ($firstCmp !== 0) {
return $firstCmp;
}
@@ -1868,24 +1899,32 @@ $scoresEndY = $pdf->GetY();
$position = null;
$previousScore = null;
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;
$previousScore = $row['score'];
$previousScore = $currentScore;
}
if ((int)$row['student_id'] === $studentId) {
$total = count($rankable);
return [
'position' => $position,
'total' => $total,
'score' => $currentScore,
'display' => $this->formatOrdinal($position) . ' out of ' . $total,
];
}
}
return null;
}
}
private function formatOrdinal(?int $value): string
{