auditionadmin/app/Services/TabulationService.php

148 lines
5.2 KiB
PHP

<?php
namespace App\Services;
use App\Exceptions\TabulationException;
use App\Models\Audition;
use App\Models\Entry;
use App\Models\ScoreSheet;
use App\Models\User;
use Illuminate\Support\Facades\Cache;
use App\Services\AuditionCacheService;
use Illuminate\Support\Facades\DB;
class TabulationService
{
protected $cacheKey = 'entries';
protected $auditionCacheService;
/**
* Create a new class instance.
*/
public function __construct(AuditionCacheService $scoringGuideCacheService)
{
$this->auditionCacheService = $scoringGuideCacheService;
}
public function entryRank(Entry $entry) {
return $this->auditionEntries($entry->audition_id)[$entry->id]->rank;
}
public function auditionEntries(Int $auditionId)
{
$cache_key = 'audition'.$auditionId.'entries';
$audition = $this->auditionCacheService->getAudition($auditionId);
return Cache::remember($cache_key, 5, function () use ($audition) {
$entries = Entry::where('audition_id',$audition->id)->with(['student.school','scoreSheets'])->withCount('scoreSheets')->get();
foreach ($entries as $entry) {
$entry->final_score_array = $this->entryFinalScores($entry);
$entry->scoring_complete = ($entry->score_sheets_count == $audition->judges_count) ? true:false;
}
// Sort based on final_score_array
for ($n=0; $n <= $audition->judges_count; $n++) {
$entries = $entries->sortByDesc(function ($entry) use ($n) {
return $entry['final_score_array'][$n];
});
}
//TODO verify this actually sorts by subscores correcty
$n = 1;
foreach ($entries as $entry) {
$entry->rank = $n;
$n++;
}
return $entries->keyBy('id');
});
}
/**
* @throws TabulationException
*/
public function entryFinalScores(Entry $entry) {
$audition = $this->auditionCacheService->getAudition($entry->audition_id);
$this->validateEntryScoreSheets($entry);
$subscores = $audition->scoringGuide->subscores->sortBy('tiebreak_order');
$finalScoresArray = [];
foreach($subscores as $subscore) {
$finalScoresArray[$subscore->id] = 0;
}
foreach ($entry->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;
}
/**
* @throws TabulationException
*/
public function validateEntryScoreSheets(Entry $entry): bool {
$audition = $this->auditionCacheService->getAudition($entry->audition_id);
foreach ($entry->scoreSheets as $sheet) {
if (! $audition->judges->contains($sheet->user_id)) {
$invalidJudge = User::find($sheet->user_id);
throw new TabulationException('Invalid scores for entry ' . $entry->id . ' exist from ' . $invalidJudge->full_name());
}
}
return true;
}
public function remainingEntriesForAudition($auditionId)
{
$audition = $this->getAuditionsWithStatus()[$auditionId];
return $audition->entries_count - $audition->scored_entries_count;
}
public function getAuditionsWithStatus()
{
return Cache::remember('auditionsWithStatus',30,function() {
// Create an array with the number of scores for each entry
$scoreCountByEntry = ScoreSheet::select('entry_id', DB::raw('count(*) as count'))
->groupBy('entry_id')
->get()
->pluck('count','entry_id');
// Retrieve auditions from the cache and load entry IDs
$auditions = $this->auditionCacheService->getAuditions();
$auditions->load('entries');
// Eager load the count of related models
$auditions->loadCount(['judges', 'entries']);
// Iterate over the auditions and calculate the scored_entries_count
return $auditions->map(function ($audition) use ($scoreCountByEntry) {
$audition->scored_entries_count = $audition->entries->reduce(function ($carry, $entry) use ($audition, $scoreCountByEntry) {
$entry->fully_scored = $audition->judges_count == $scoreCountByEntry->has($entry->id) ? $scoreCountByEntry[$entry->id] : false;
return $carry + ($entry->fully_scored ? 1 : 0);
}, 0);
return $audition;
});
});
}
public function allResults() {
$auditions = $this->getAuditionsWithStatus();
foreach ($auditions as $audition) {
$audition->entries = $this->auditionEntries($audition->id);
}
return $auditions;
}
}