auditionadmin/app/Actions/Tabulation/AllowForOlympicScoring.php

154 lines
5.0 KiB
PHP

<?php
/** @noinspection PhpUnhandledExceptionInspection */
namespace App\Actions\Tabulation;
use App\Exceptions\TabulationException;
use App\Models\BonusScore;
use App\Models\CalculatedScore;
use App\Models\Entry;
use App\Services\AuditionService;
use App\Services\EntryService;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log;
use function auditionSetting;
class AllowForOlympicScoring implements CalculateEntryScore
{
protected CalculateScoreSheetTotal $calculator;
protected AuditionService $auditionService;
protected EntryService $entryService;
public function __construct(
CalculateScoreSheetTotal $calculator,
AuditionService $auditionService,
EntryService $entryService
) {
$this->calculator = $calculator;
$this->auditionService = $auditionService;
$this->entryService = $entryService;
}
public function calculate(string $mode, Entry $entry): array
{
$cacheKey = 'entryScore-'.$entry->id.'-'.$mode;
return Cache::remember($cacheKey, 10, function () use ($mode, $entry) {
$this->basicValidation($mode, $entry);
$this->isEntryANoShow($entry);
$this->areAllJudgesIn($entry);
$this->areAllJudgesValid($entry);
$calculatedScores = $this->getJudgeTotals($mode, $entry);
CalculatedScore::create([
'entry_id' => $entry->id,
'mode' => $mode,
'calculatedScore' => $calculatedScores,
]);
return $calculatedScores;
// return $this->getJudgeTotals($mode, $entry);
});
}
protected function getJudgeTotals($mode, Entry $entry): array
{
$scores = [];
foreach ($this->auditionService->getJudges($entry->audition) as $judge) {
$scores[] = $this->calculator->__invoke($mode, $entry, $judge);
}
// sort the scores array by the total score
usort($scores, function ($a, $b) {
return $a[0] <=> $b[0];
});
// we can only really do olympic scoring if there are at least 3 scores
if (count($scores) >= 3 && auditionSetting('olympic_scoring')) {
// remove the highest and lowest scores
array_pop($scores);
array_shift($scores);
}
$sums = [];
// Sum each subscore from the judges
foreach ($scores as $score) {
$index = 0;
foreach ($score as $value) {
$sums[$index] = $sums[$index] ?? 0;
$sums[$index] += $value;
$index++;
}
}
// add the bonus points for a seating mode
if ($mode === 'seating' && $sums) {
$sums[0] += $this->getBonusPoints($entry);
}
return $sums;
}
protected function getBonusPoints(Entry $entry)
{
$bonusScoreDefinition = $entry->audition->bonusScore()->first();
if (! $bonusScoreDefinition) {
return 0;
}
/** @noinspection PhpPossiblePolymorphicInvocationInspection */
$bonusJudges = $bonusScoreDefinition->judges;
$bonusScoreSheets = BonusScore::where('entry_id', $entry->id)->get();
foreach ($bonusScoreSheets as $sheet) {
if (! $bonusJudges->contains($sheet->user_id)) {
throw new TabulationException('Entry has a bonus score from unassigned judge');
}
}
// sum the score property of the $bonusScoreSheets
return $bonusScoreSheets->sum('score');
}
protected function basicValidation($mode, $entry): void
{
if ($mode !== 'seating' && $mode !== 'advancement') {
throw new TabulationException('Mode must be seating or advancement');
}
if (! $this->entryService->entryExists($entry)) {
throw new TabulationException('Invalid entry specified');
}
}
protected function areAllJudgesIn(Entry $entry): void
{
$assignedJudgeCount = $this->auditionService->getJudges($entry->audition)->count();
if ($entry->scoreSheets->count() !== $assignedJudgeCount) {
throw new TabulationException('Not all score sheets are in');
}
}
protected function areAllJudgesValid(Entry $entry): void
{
$validJudgeIds = $this->auditionService->getJudges($entry->audition)->pluck('id')->toArray();
$existingJudgeIds = $entry->scoreSheets->pluck('user_id')->toArray();
if (array_diff($existingJudgeIds, $validJudgeIds)) {
Log::debug('EntryID: '.$entry->id);
Log::debug('Valid judge ids: ('.gettype($validJudgeIds).') '.json_encode($validJudgeIds));
Log::debug('Existing judge ids: ('.gettype($existingJudgeIds).') '.json_encode($existingJudgeIds));
throw new TabulationException('Score exists from a judge not assigned to this audition');
}
}
protected function isEntryANoShow(Entry $entry): void
{
if ($entry->hasFlag('no_show')) {
throw new TabulationException('No Show');
}
}
}