auditionCache = $auditionCache; $this->entryCache = $entryCache; } /** * Cache all scoring guides */ public function getScoringGuides(): \Illuminate\Database\Eloquent\Collection { $cacheKey = 'scoringGuides'; return Cache::remember($cacheKey, 3600, fn () => ScoringGuide::with('subscores')->withCount('subscores')->get()); } /** * Retrieve a single scoring guide from the cache */ public function getScoringGuide($id): ScoringGuide { return $this->getScoringGuides()->find($id); } /** * Clear the scoring guide cache */ public function clearScoringGuideCache(): void { Cache::forget('scoringGuides'); } /** * Returns an array where each key is an entry id and the value is the number * of score sheets assigned to that entry. * * @return Collection */ public function entryScoreSheetCounts() { $cacheKey = 'entryScoreSheetCounts'; return Cache::remember($cacheKey, 10, function () { // For each Entry get the number of ScoreSheets associated with it $scoreSheetCountsByEntry = ScoreSheet::select('entry_id', DB::raw('count(*) as count')) ->groupBy('entry_id') ->get() ->pluck('count', 'entry_id'); $entryScoreSheetCounts = []; $entries = $this->entryCache->getAllEntries(); foreach ($entries as $entry) { $entryScoreSheetCounts[$entry->id] = $scoreSheetCountsByEntry[$entry->id] ?? 0; } return $entryScoreSheetCounts; }); } /** * Get final scores array for the requested entry. The first element is the total score. The following elements are sums * of each subscore in tiebreaker order * * @return array */ public function entryTotalScores(Entry $entry) { $cacheKey = 'entry'.$entry->id.'totalScores'; return Cache::remember($cacheKey, 3600, function () use ($entry) { return $this->calculateFinalScoreArray($entry->audition->scoring_guide_id, $entry->scoreSheets); }); } /** * Calculate and cache scores for all entries for the provided audition ID * * @return void */ public function calculateScoresForAudition($auditionId) { static $alreadyChecked = []; // if $auditionId is in the array $alreadyChecked return if (in_array($auditionId, $alreadyChecked)) { return; } $alreadyChecked[] = $auditionId; $audition = $this->auditionCache->getAudition($auditionId); $scoringGuideId = $audition->scoring_guide_id; $entries = $this->entryCache->getEntriesForAudition($auditionId); $entries->load('scoreSheets'); // TODO Cache this somehow, it's expensive and repetitive on the seating page foreach ($entries as $entry) { $cacheKey = 'entry'.$entry->id.'totalScores'; if (Cache::has($cacheKey)) { continue; } $thisTotalScore = $this->calculateFinalScoreArray($scoringGuideId, $entry->scoreSheets); Cache::put($cacheKey, $thisTotalScore, 3600); } } public function clearScoreSheetCountCache() { $cacheKey = 'entryScoreSheetCounts'; Cache::forget($cacheKey); } public function clearEntryTotalScoresCache($entryId) { $cacheKey = 'entry'.$entryId.'totalScores'; Cache::forget($cacheKey); } public function clearAllCachedTotalScores() { foreach ($this->entryCache->getAllEntries() as $entry) { $cacheKey = 'entry'.$entry->id.'totalScores'; Cache::forget($cacheKey); } } /** * Calculate final score using the provided scoring guide and score sheets. Returns an array of scores * The first element is the total score. The following elements are the sum of each subscore * in tiebreaker order. */ public function calculateFinalScoreArray($scoringGuideId, array|Collection $scoreSheets): array { $sg = $this->getScoringGuide($scoringGuideId); // TODO cache the scoring guides with their subscores $subscores = $sg->subscores->sortBy('tiebreak_order'); $ignoredSubscores = []; // This will be subscores not used for seating // Init final scores array $finalScoresArray = []; foreach ($subscores as $subscore) { if (! $subscore->for_seating) { // Ignore scores that are not for seating $ignoredSubscores[] = $subscore->id; continue; } $finalScoresArray[$subscore->id] = 0; } foreach ($scoreSheets as $sheet) { foreach ($sheet->subscores as $ss) { if (in_array($ss['subscore_id'], $ignoredSubscores)) { // Ignore scores that are not for seating continue; } $finalScoresArray[$ss['subscore_id']] += $ss['score']; } } // calculate weighted final score $totalScore = 0; $totalWeight = 0; foreach ($subscores as $subscore) { if (in_array($subscore->id, $ignoredSubscores)) { // Ignore scores that are not for seating continue; } $totalScore += ($finalScoresArray[$subscore->id] * $subscore->weight); $totalWeight += $subscore->weight; } $totalScore = ($totalScore / $totalWeight); array_unshift($finalScoresArray, $totalScore); return $finalScoresArray; } /** * Validate that the judge on the score sheet is actually assigned to judge * then entry * * @return bool */ public function validateScoreSheet(ScoreSheet $sheet) { // TODO use this when calculating scores $entry = $this->entryCache->getAllEntries()->find($sheet->entry_id); $audition = $this->auditionCache->getAudition($entry->audition_id); $validJudges = $audition->judges; // send a laravel flash message with an error if the $sheet->user_id is not in the collection $validJudges if (! $validJudges->contains('id', $sheet->user_id)) { session()->flash('error', 'Entry ID '.$sheet->entry_id.' has an invalid score entered by '.$sheet->judge->full_name()); } // check if $sheet->user_id is in the collection $validJudges, return false if not, true if it is return $validJudges->contains('id', $sheet->user_id); } }