update controllers logic
This commit is contained in:
@@ -0,0 +1,345 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Admin\CompetitionWinners;
|
||||
|
||||
/** Pure domain logic ported from CodeIgniter `Admin\CompetitionWinnersController` (private helpers). */
|
||||
final class CompetitionWinnersDomain
|
||||
{
|
||||
/** @var array<int, array{min:int, max:?int, winners:int}> */
|
||||
private const WINNER_TIERS = [
|
||||
['min' => 1, 'max' => 12, 'winners' => 2],
|
||||
['min' => 13, 'max' => 16, 'winners' => 3],
|
||||
['min' => 17, 'max' => 22, 'winners' => 4],
|
||||
['min' => 23, 'max' => 27, 'winners' => 5],
|
||||
['min' => 28, 'max' => null, 'winners' => 6],
|
||||
];
|
||||
|
||||
public static function winnersForCount(int $count): int
|
||||
{
|
||||
if ($count <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
foreach (self::WINNER_TIERS as $tier) {
|
||||
$min = (int) $tier['min'];
|
||||
$max = $tier['max'];
|
||||
if ($count < $min) {
|
||||
continue;
|
||||
}
|
||||
if ($max === null || $count <= (int) $max) {
|
||||
return (int) $tier['winners'];
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, array<string, mixed>> $classSections
|
||||
* @param array<int, int> $classCounts
|
||||
* @param array<int, int> $overrides
|
||||
* @param array<int, int|null> $questionCounts
|
||||
* @param array<int, array<int, float|null>> $prizeMap
|
||||
* @return list<array<string, mixed>>
|
||||
*/
|
||||
public static function buildClassRows(
|
||||
array $classSections,
|
||||
array $classCounts,
|
||||
array $overrides,
|
||||
array $questionCounts,
|
||||
array $prizeMap,
|
||||
int $limitClassId = 0
|
||||
): array {
|
||||
$rows = [];
|
||||
foreach ($classSections as $section) {
|
||||
$classId = (int) ($section['class_section_id'] ?? 0);
|
||||
if ($classId <= 0) {
|
||||
continue;
|
||||
}
|
||||
if ($limitClassId > 0 && $classId !== $limitClassId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$count = $classCounts[$classId] ?? 0;
|
||||
if ($count <= 0) {
|
||||
continue;
|
||||
}
|
||||
$auto = self::winnersForCount($count);
|
||||
$override = isset($overrides[$classId]) ? (int) $overrides[$classId] : null;
|
||||
$questionCount = isset($questionCounts[$classId]) ? (int) $questionCounts[$classId] : null;
|
||||
$prizes = $prizeMap[$classId] ?? [];
|
||||
$rows[] = [
|
||||
'class_section_id' => $classId,
|
||||
'class_section_name' => $section['class_section_name'] ?? (string) $classId,
|
||||
'student_count' => $count,
|
||||
'auto_winners' => $auto,
|
||||
'override_winners' => $override,
|
||||
'final_winners' => $override !== null ? $override : $auto,
|
||||
'question_count' => $questionCount,
|
||||
'prize_1' => $prizes[1] ?? null,
|
||||
'prize_2' => $prizes[2] ?? null,
|
||||
'prize_3' => $prizes[3] ?? null,
|
||||
'prize_4' => $prizes[4] ?? null,
|
||||
'prize_5' => $prizes[5] ?? null,
|
||||
'prize_6' => $prizes[6] ?? null,
|
||||
];
|
||||
}
|
||||
|
||||
return $rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, array<string, mixed>> $classSections
|
||||
* @param array<int, int> $classCounts
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
public static function filterSectionsWithStudents(array $classSections, array $classCounts): array
|
||||
{
|
||||
return array_values(array_filter($classSections, static function ($section) use ($classCounts) {
|
||||
$classId = (int) ($section['class_section_id'] ?? 0);
|
||||
if ($classId <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ($classCounts[$classId] ?? 0) > 0;
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $competition Row including optional class_section_id
|
||||
* @param array<int, int> $classCounts
|
||||
* @param array<int, int> $scoreCounts
|
||||
* @param array<int, int> $overrides
|
||||
* @return array<int, int>
|
||||
*/
|
||||
public static function getWinnersCountByClass(
|
||||
array $competition,
|
||||
array $classCounts,
|
||||
array $scoreCounts,
|
||||
array $overrides
|
||||
): array {
|
||||
$classIds = array_unique(array_merge(
|
||||
array_keys($classCounts),
|
||||
array_keys($scoreCounts),
|
||||
array_keys($overrides)
|
||||
));
|
||||
|
||||
$limitClassId = (int) ($competition['class_section_id'] ?? 0);
|
||||
$map = [];
|
||||
foreach ($classIds as $classId) {
|
||||
$classId = (int) $classId;
|
||||
if ($classId <= 0) {
|
||||
continue;
|
||||
}
|
||||
if ($limitClassId > 0 && $classId !== $limitClassId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$override = $overrides[$classId] ?? null;
|
||||
if ($override !== null) {
|
||||
$map[$classId] = (int) $override;
|
||||
continue;
|
||||
}
|
||||
|
||||
$baseCount = $classCounts[$classId] ?? ($scoreCounts[$classId] ?? 0);
|
||||
if ($baseCount <= 0) {
|
||||
$map[$classId] = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
$maxWinners = self::winnersForCount((int) $baseCount);
|
||||
$minWinners = 3;
|
||||
$map[$classId] = $maxWinners >= $minWinners ? $maxWinners : $minWinners;
|
||||
}
|
||||
|
||||
return $map;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, array<string, mixed>> $rows
|
||||
* @return array<int, list<array<string, mixed>>>
|
||||
*/
|
||||
public static function groupScoresByClass(array $rows): array
|
||||
{
|
||||
$grouped = [];
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$classSectionId = (int) ($row['class_section_id'] ?? 0);
|
||||
if ($classSectionId <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$name = trim(($row['firstname'] ?? '').' '.($row['lastname'] ?? ''));
|
||||
$score = (float) ($row['score'] ?? 0);
|
||||
|
||||
$grouped[$classSectionId][] = [
|
||||
'student_id' => $row['student_id'],
|
||||
'name' => $name !== '' ? $name : ('Student #'.$row['student_id']),
|
||||
'score' => $score,
|
||||
];
|
||||
}
|
||||
|
||||
foreach ($grouped as &$rowsForClass) {
|
||||
usort($rowsForClass, static function ($a, $b) {
|
||||
return $b['score'] <=> $a['score'];
|
||||
});
|
||||
}
|
||||
unset($rowsForClass);
|
||||
|
||||
return $grouped;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, list<array<string, mixed>>> $rankedByClass
|
||||
* @return array<int, int>
|
||||
*/
|
||||
public static function getScoreCountsByClass(array $rankedByClass): array
|
||||
{
|
||||
$counts = [];
|
||||
foreach ($rankedByClass as $classId => $rows) {
|
||||
$counts[(int) $classId] = count($rows);
|
||||
}
|
||||
|
||||
return $counts;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, list<array<string, mixed>>> $rankedByClass
|
||||
* @param array<int, int> $winnersCountByClass
|
||||
* @param array<int, array<int, float|null>> $prizeMap
|
||||
* @param array<int, int|null> $questionCounts
|
||||
* @return array<int, list<array<string, mixed>>>
|
||||
*/
|
||||
public static function getTopWinnersByClass(
|
||||
array $rankedByClass,
|
||||
array $winnersCountByClass,
|
||||
array $prizeMap,
|
||||
array $questionCounts
|
||||
): array {
|
||||
$out = [];
|
||||
|
||||
foreach ($rankedByClass as $classSectionId => $rows) {
|
||||
$limit = (int) ($winnersCountByClass[$classSectionId] ?? 0);
|
||||
if ($limit <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$out[$classSectionId] = self::assignPrizesByScore(
|
||||
$rows,
|
||||
$prizeMap[$classSectionId] ?? [],
|
||||
$limit,
|
||||
$questionCounts[$classSectionId] ?? null
|
||||
);
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, mixed> $prizes
|
||||
* @return array<int, float|null>
|
||||
*/
|
||||
public static function normalizePrizeInputs(array $prizes): array
|
||||
{
|
||||
$out = [1 => null, 2 => null, 3 => null, 4 => null, 5 => null, 6 => null];
|
||||
foreach ($out as $rank => $_value) {
|
||||
$raw = trim((string) ($prizes[$rank] ?? ''));
|
||||
if ($raw === '') {
|
||||
$out[$rank] = null;
|
||||
} else {
|
||||
$out[$rank] = (float) $raw;
|
||||
}
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
public static function castPrize(mixed $value): ?float
|
||||
{
|
||||
if ($value === null || $value === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (float) $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, float|null> $classPrizes
|
||||
*/
|
||||
public static function getPrizeForRank(array $classPrizes, int $rank): float
|
||||
{
|
||||
$val = $classPrizes[$rank] ?? null;
|
||||
|
||||
return $val !== null ? (float) $val : 0.0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<array<string, mixed>> $rows
|
||||
* @param array<int, float|null> $classPrizes
|
||||
* @return list<array<string, mixed>>
|
||||
*/
|
||||
public static function assignPrizesByScore(array $rows, array $classPrizes, int $limit, ?int $questionCount = null): array
|
||||
{
|
||||
if ($limit <= 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
usort($rows, static function ($a, $b) {
|
||||
return ($b['score'] ?? 0) <=> ($a['score'] ?? 0);
|
||||
});
|
||||
|
||||
$scoreGroups = [];
|
||||
$scoreOrder = [];
|
||||
foreach ($rows as $row) {
|
||||
$score = (float) ($row['score'] ?? 0);
|
||||
$scoreKey = number_format($score, 5, '.', '');
|
||||
if (! array_key_exists($scoreKey, $scoreGroups)) {
|
||||
$scoreGroups[$scoreKey] = [];
|
||||
$scoreOrder[] = $scoreKey;
|
||||
}
|
||||
$scoreGroups[$scoreKey][] = $row;
|
||||
}
|
||||
|
||||
$top = [];
|
||||
$rank = 1;
|
||||
foreach ($scoreOrder as $scoreKey) {
|
||||
if (count($top) >= $limit) {
|
||||
break;
|
||||
}
|
||||
foreach ($scoreGroups[$scoreKey] as $row) {
|
||||
$row['rank'] = $rank;
|
||||
$top[] = $row;
|
||||
}
|
||||
$rank++;
|
||||
}
|
||||
|
||||
$sharedPrize = null;
|
||||
if ($questionCount !== null && $questionCount > 0 && ! empty($top)) {
|
||||
$allMax = true;
|
||||
foreach ($top as $row) {
|
||||
$score = (float) ($row['score'] ?? 0);
|
||||
if ($score < $questionCount) {
|
||||
$allMax = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($allMax) {
|
||||
$sharedPrize = self::getPrizeForRank($classPrizes, 1);
|
||||
}
|
||||
}
|
||||
|
||||
$out = [];
|
||||
foreach ($top as $row) {
|
||||
if ($sharedPrize !== null) {
|
||||
$prize = $sharedPrize;
|
||||
} else {
|
||||
$prize = self::getPrizeForRank($classPrizes, (int) ($row['rank'] ?? 1));
|
||||
}
|
||||
|
||||
$row['prize_amount'] = $prize;
|
||||
$out[] = $row;
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user