auditionadmin/app/Services/ScoreService.php

178 lines
5.7 KiB
PHP

<?php
namespace App\Services;
use App\Models\Entry;
use App\Models\ScoreSheet;
use App\Models\ScoringGuide;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use function array_unshift;
class ScoreService
{
protected $auditionCache;
protected $entryCache;
/**
* Create a new class instance.
*/
public function __construct(AuditionCacheService $auditionCache, EntryCacheService $entryCache)
{
$this->auditionCache = $auditionCache;
$this->entryCache = $entryCache;
}
/**
* Cache all scoring guides
* @return \Illuminate\Database\Eloquent\Collection
*/
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
* @param $id
* @return ScoringGuide
*/
public function getScoringGuide($id): ScoringGuide
{
return $this->getScoringGuides()->find($id);
}
/**
* Clear the scoring guide cache
* @return void
*/
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
$entryScoreSheetCounts = ScoreSheet::select('entry_id', DB::raw('count(*) as count'))
->groupBy('entry_id')
->get()
->pluck('count','entry_id');
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
* @param Entry $entry
* @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
* @param $auditionId
* @return void
*/
public function calculateScoresForAudition($auditionId)
{
$audition = $this->auditionCache->getAudition($auditionId);
$scoringGuideId = $audition->scoring_guide_id;
// $entries = Entry::where('audition_id',$auditionId)->with('scoreSheets')->get();
$entries = $this->entryCache->getEntriesForAudition($auditionId);
$entries->load('scoreSheets');
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);
}
}
/**
* 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.
* @param $scoringGuideId
* @param array|Collection $scoreSheets
* @return array
*/
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');
// Init final scores array
$finalScoresArray = [];
foreach ($subscores as $subscore) {
$finalScoresArray[$subscore->id] = 0;
}
foreach($scoreSheets as $sheet) {
foreach ($sheet->subscores as $ss) {
$finalScoresArray[$ss['subscore_id']] += $ss['score'];
}
}
// calculate weighted final score
$totalScore = 0;
$totalWeight = 0;
foreach ($subscores as $subscore) {
$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
* @param ScoreSheet $sheet
* @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);
}
}