fix financial issues
This commit is contained in:
@@ -2357,6 +2357,7 @@ public function belowSixty()
|
||||
's.id AS student_id',
|
||||
's.firstname',
|
||||
's.lastname',
|
||||
's.age',
|
||||
's.school_id',
|
||||
'cs.class_section_name',
|
||||
'LOWER(TRIM(ss.semester)) AS sem_key',
|
||||
@@ -2389,6 +2390,7 @@ public function belowSixty()
|
||||
'school_id' => $row['school_id'] ?? '',
|
||||
'firstname' => $row['firstname'] ?? '',
|
||||
'lastname' => $row['lastname'] ?? '',
|
||||
'age' => $row['age'] ?? null,
|
||||
'class_section_name' => $row['class_section_name'] ?? '',
|
||||
'fall_score' => null,
|
||||
'spring_score' => null,
|
||||
@@ -3397,6 +3399,8 @@ public function allDecisions()
|
||||
's.school_id',
|
||||
's.firstname',
|
||||
's.lastname',
|
||||
's.gender',
|
||||
'ss.class_section_id',
|
||||
'cs.class_section_name',
|
||||
'LOWER(TRIM(ss.semester)) AS sem_key',
|
||||
'ss.semester_score',
|
||||
@@ -3428,6 +3432,8 @@ public function allDecisions()
|
||||
'school_id' => $sr['school_id'] ?? '',
|
||||
'firstname' => $sr['firstname'] ?? '',
|
||||
'lastname' => $sr['lastname'] ?? '',
|
||||
'gender' => $sr['gender'] ?? '',
|
||||
'class_section_id' => (int)($sr['class_section_id'] ?? 0),
|
||||
'class_section_name' => $sr['class_section_name'] ?? '',
|
||||
'fall_score' => null,
|
||||
'spring_score' => null,
|
||||
@@ -3513,6 +3519,8 @@ public function allDecisions()
|
||||
'school_id' => $info['school_id'],
|
||||
'firstname' => $info['firstname'],
|
||||
'lastname' => $info['lastname'],
|
||||
'gender' => $info['gender'] ?? '',
|
||||
'class_section_id' => (int)($info['class_section_id'] ?? 0),
|
||||
'class_section_name' => $info['class_section_name'],
|
||||
'fall_score' => $fall,
|
||||
'spring_score' => $spring,
|
||||
@@ -3521,9 +3529,47 @@ public function allDecisions()
|
||||
'source' => $source,
|
||||
'notes' => $notes,
|
||||
'saved' => isset($savedMap[$sid]),
|
||||
'is_trophy' => false,
|
||||
];
|
||||
}
|
||||
|
||||
$rowsByClass = [];
|
||||
|
||||
foreach ($rows as $index => $row) {
|
||||
$classSectionId = (int)($row['class_section_id'] ?? 0);
|
||||
|
||||
if ($classSectionId <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$rowsByClass[$classSectionId][] = $index;
|
||||
}
|
||||
|
||||
foreach ($rowsByClass as $classIndexes) {
|
||||
$scores = [];
|
||||
|
||||
foreach ($classIndexes as $rowIndex) {
|
||||
$yearScore = $rows[$rowIndex]['year_score'] ?? null;
|
||||
|
||||
if (is_numeric($yearScore)) {
|
||||
$scores[] = (float)$yearScore;
|
||||
}
|
||||
}
|
||||
|
||||
$thresholdInfo = $this->calculateTrophyThreshold($scores, 75.0);
|
||||
$threshold = $thresholdInfo['threshold'];
|
||||
|
||||
if ($threshold === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($classIndexes as $rowIndex) {
|
||||
$yearScore = $rows[$rowIndex]['year_score'] ?? null;
|
||||
|
||||
$rows[$rowIndex]['is_trophy'] = is_numeric($yearScore) && (float)$yearScore >= $threshold;
|
||||
}
|
||||
}
|
||||
|
||||
$generated = !empty($saved);
|
||||
|
||||
return view('grading/all_decisions', [
|
||||
@@ -3696,6 +3742,108 @@ public function generateAllDecisions()
|
||||
->with('status', "Decisions generated for {$savedCount} students.");
|
||||
}
|
||||
|
||||
private function calculateTrophyThreshold(array $scores, float $percentile = 75.0): array
|
||||
{
|
||||
$scores = array_values(array_filter(
|
||||
$scores,
|
||||
static fn ($value): bool => is_numeric($value) && $value !== null
|
||||
));
|
||||
$scores = array_map('floatval', $scores);
|
||||
sort($scores);
|
||||
|
||||
$count = count($scores);
|
||||
|
||||
if ($count === 0) {
|
||||
return ['threshold' => null, 'winners' => 0, 'method' => 'empty'];
|
||||
}
|
||||
|
||||
$minWinners = 3;
|
||||
$maxWinners = max($minWinners, (int) floor($count * (1 - $percentile / 100)));
|
||||
|
||||
$threshold = $this->empiricalTrophyPercentile($scores, $percentile);
|
||||
$winners = $this->countScoresAtOrAbove($scores, $threshold);
|
||||
|
||||
if ($winners < $minWinners) {
|
||||
$target = min($minWinners, $count);
|
||||
$descending = array_reverse($scores);
|
||||
$threshold = $descending[$target - 1];
|
||||
$winners = $this->countScoresAtOrAbove($scores, $threshold);
|
||||
|
||||
return ['threshold' => $threshold, 'winners' => $winners, 'method' => 'min3_reduced'];
|
||||
}
|
||||
|
||||
if ($winners <= $maxWinners) {
|
||||
return ['threshold' => $threshold, 'winners' => $winners, 'method' => 'empirical_percentile'];
|
||||
}
|
||||
|
||||
$result = $this->capTrophyThresholdByRank($scores, $maxWinners);
|
||||
|
||||
if ($result['winners'] < $minWinners) {
|
||||
$target = min($minWinners, $count);
|
||||
$descending = array_reverse($scores);
|
||||
$threshold = $descending[$target - 1];
|
||||
$winners = $this->countScoresAtOrAbove($scores, $threshold);
|
||||
|
||||
return ['threshold' => $threshold, 'winners' => $winners, 'method' => 'min3_after_cap'];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function capTrophyThresholdByRank(array $sortedScores, int $max): array
|
||||
{
|
||||
$descending = array_reverse($sortedScores);
|
||||
$threshold = $descending[$max - 1];
|
||||
$winners = $this->countScoresAtOrAbove($sortedScores, $threshold);
|
||||
|
||||
if ($winners <= $max) {
|
||||
return ['threshold' => $threshold, 'winners' => $winners, 'method' => 'capped_25pct'];
|
||||
}
|
||||
|
||||
$uniqueHigherScores = array_values(array_unique(array_filter(
|
||||
$sortedScores,
|
||||
static fn ($score): bool => $score > $threshold
|
||||
)));
|
||||
sort($uniqueHigherScores);
|
||||
|
||||
foreach ($uniqueHigherScores as $candidate) {
|
||||
$winnerCount = $this->countScoresAtOrAbove($sortedScores, $candidate);
|
||||
|
||||
if ($winnerCount <= $max) {
|
||||
return ['threshold' => $candidate, 'winners' => $winnerCount, 'method' => 'capped_25pct'];
|
||||
}
|
||||
}
|
||||
|
||||
return ['threshold' => $sortedScores[0], 'winners' => count($sortedScores), 'method' => 'all_equal'];
|
||||
}
|
||||
|
||||
private function empiricalTrophyPercentile(array $sortedScores, float $percentile): float
|
||||
{
|
||||
$count = count($sortedScores);
|
||||
|
||||
if ($count === 0) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
$index = ($percentile / 100.0) * ($count - 1);
|
||||
$lower = (int) floor($index);
|
||||
$upper = (int) ceil($index);
|
||||
|
||||
if ($lower === $upper) {
|
||||
return $sortedScores[$lower];
|
||||
}
|
||||
|
||||
return $sortedScores[$lower] + ($index - $lower) * ($sortedScores[$upper] - $sortedScores[$lower]);
|
||||
}
|
||||
|
||||
private function countScoresAtOrAbove(array $scores, float $threshold): int
|
||||
{
|
||||
return count(array_filter(
|
||||
$scores,
|
||||
static fn ($score): bool => $score >= $threshold
|
||||
));
|
||||
}
|
||||
|
||||
public function getScoreComment()
|
||||
{
|
||||
// Get all students for the current semester and school year
|
||||
|
||||
Reference in New Issue
Block a user