109 lines
3.5 KiB
PHP
109 lines
3.5 KiB
PHP
<?php
|
|
|
|
/** @noinspection PhpUnhandledExceptionInspection */
|
|
|
|
namespace App\Actions\Tabulation;
|
|
|
|
use App\Exceptions\TabulationException;
|
|
use App\Models\Audition;
|
|
use Illuminate\Database\Eloquent\Collection;
|
|
use Illuminate\Support\Facades\Cache;
|
|
|
|
use function is_numeric;
|
|
|
|
class RankAuditionEntries
|
|
{
|
|
protected CalculateEntryScore $calculator;
|
|
|
|
public function __construct(CalculateEntryScore $calculator)
|
|
{
|
|
$this->calculator = $calculator;
|
|
}
|
|
|
|
public function rank(string $mode, Audition $audition): Collection
|
|
{
|
|
$cacheKey = 'audition'.$audition->id.$mode;
|
|
|
|
return Cache::remember($cacheKey, 300, function () use ($mode, $audition) {
|
|
return $this->calculateRank($mode, $audition);
|
|
});
|
|
|
|
}
|
|
|
|
/**
|
|
* For a given audition, return a collection of entries ranked by total score. Each entry will have a
|
|
* property rank that either is their rank or a flag reflecting no-show, declined, or failed-prelim status
|
|
*
|
|
* @throws TabulationException
|
|
*/
|
|
public function calculateRank(string $mode, Audition $audition): Collection
|
|
{
|
|
$this->basicValidation($mode, $audition);
|
|
$entries = match ($mode) {
|
|
'seating' => $audition->entries()->forSeating()->with('scoreSheets')->get(),
|
|
'advancement' => $audition->entries()->forAdvancement()->with('scoreSheets')->get(),
|
|
};
|
|
|
|
foreach ($entries as $entry) {
|
|
$entry->setRelation('audition', $audition);
|
|
try {
|
|
$entry->score_totals = $this->calculator->calculate($mode, $entry);
|
|
} catch (TabulationException $ex) {
|
|
$entry->score_totals = [-1];
|
|
$entry->score_message = $ex->getMessage();
|
|
}
|
|
}
|
|
// Sort entries based on their total score, then by subscores in tiebreak order
|
|
$entries = $entries->sort(function ($a, $b) {
|
|
for ($i = 0; $i < count($a->score_totals); $i++) {
|
|
if (! array_key_exists($i, $a->score_totals)) {
|
|
return -1;
|
|
}
|
|
if (! array_key_exists($i, $b->score_totals)) {
|
|
return -1;
|
|
}
|
|
if ($a->score_totals[$i] > $b->score_totals[$i]) {
|
|
return -1;
|
|
} elseif ($a->score_totals[$i] < $b->score_totals[$i]) {
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
});
|
|
$rank = 1;
|
|
$rawRank = 1;
|
|
foreach ($entries as $entry) {
|
|
$entry->rank = $rank;
|
|
$entry->raw_rank = $rawRank;
|
|
// We don't really get a rank for seating if we have certain flags
|
|
if ($mode === 'seating') {
|
|
if ($entry->hasFlag('failed_prelim')) {
|
|
$entry->rank = 'Failed Prelim';
|
|
} elseif ($entry->hasFlag('declined')) {
|
|
$entry->rank = 'Declined';
|
|
} elseif ($entry->hasFlag('no_show')) {
|
|
$entry->rank = 'No Show';
|
|
}
|
|
}
|
|
|
|
if (is_numeric($entry->rank)) {
|
|
$rank++;
|
|
}
|
|
$rawRank++;
|
|
}
|
|
|
|
return $entries;
|
|
}
|
|
|
|
protected function basicValidation($mode, Audition $audition): void
|
|
{
|
|
if ($mode !== 'seating' && $mode !== 'advancement') {
|
|
throw new TabulationException('Mode must be seating or advancement');
|
|
}
|
|
if (! $audition->exists()) {
|
|
throw new TabulationException('Invalid audition provided');
|
|
}
|
|
}
|
|
}
|