calculator = $calculator; $this->auditionService = $auditionService; $this->entryService = $entryService; } public function calculate(string $mode, Entry $entry): array { $calculated = CalculatedScore::where('entry_id', $entry->id)->where('mode', $mode)->first(); if ($calculated) { return $calculated->calculatedScore; } $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('failed_prelim')) { throw new TabulationException('Failed Prelim'); } if ($entry->hasFlag('no_show')) { throw new TabulationException('No Show'); } } }