From e11741a0a1d09b9135b22bb33eaed5b5c3561ac6 Mon Sep 17 00:00:00 2001 From: Matt Young Date: Thu, 11 Jul 2024 00:32:53 -0500 Subject: [PATCH] Work on audition service --- app/Actions/Tabulation/AllJudgesCount.php | 4 +- .../Tabulation/CalculateScoreSheetTotal.php | 38 ++++++++++++-- app/Exceptions/AuditionServiceException.php | 10 ++++ app/Providers/AppServiceProvider.php | 15 +++--- app/Services/AuditionService.php | 52 +++++++++++++++++-- .../Actions/CalculateScoreSheetTotalTest.php | 14 ++--- .../Feature/Services/AuditionServiceTest.php | 38 ++++++++++++++ 7 files changed, 145 insertions(+), 26 deletions(-) create mode 100644 app/Exceptions/AuditionServiceException.php create mode 100644 tests/Feature/Services/AuditionServiceTest.php diff --git a/app/Actions/Tabulation/AllJudgesCount.php b/app/Actions/Tabulation/AllJudgesCount.php index 4cdb648..d0b9978 100644 --- a/app/Actions/Tabulation/AllJudgesCount.php +++ b/app/Actions/Tabulation/AllJudgesCount.php @@ -13,7 +13,8 @@ class AllJudgesCount implements CalculateEntryScore public function __construct() { - $this->calculator = new CalculateScoreSheetTotal(); + #$this->calculator = new CalculateScoreSheetTotal(); + $this->calculator = app(CalculateScoreSheetTotal::class); } public function calculate(string $mode, Entry $entry): array @@ -31,6 +32,7 @@ class AllJudgesCount implements CalculateEntryScore foreach ($entry->audition->judges as $judge) { $scores[] = $this->calculator->__invoke($mode, $entry, $judge); } + $sums = []; // Sum each subscore from the judges foreach ($scores as $score) { $index = 0; diff --git a/app/Actions/Tabulation/CalculateScoreSheetTotal.php b/app/Actions/Tabulation/CalculateScoreSheetTotal.php index 1f814d5..e0c08ac 100644 --- a/app/Actions/Tabulation/CalculateScoreSheetTotal.php +++ b/app/Actions/Tabulation/CalculateScoreSheetTotal.php @@ -5,14 +5,18 @@ namespace App\Actions\Tabulation; use App\Exceptions\TabulationException; +use App\Models\Audition; use App\Models\Entry; use App\Models\ScoreSheet; use App\Models\User; +use App\Services\AuditionService; class CalculateScoreSheetTotal { - public function __construct() + protected AuditionService $auditionService; + public function __construct(AuditionService $auditionService) { + $this->auditionService = $auditionService; } public function __invoke(string $mode, Entry $entry, User $judge): array @@ -22,10 +26,8 @@ class CalculateScoreSheetTotal if (! $scoreSheet) { throw new TabulationException('No score sheet by that judge for that entry'); } - $subscores = match ($mode) { - 'seating' => $entry->audition->scoringGuide->subscores->where('for_seating', true)->sortBy('tiebreak_order'), - 'advancement' => $entry->audition->scoringGuide->subscores->where('for_advance', true)->sortBy('tiebreak_order'), - }; + #$subscores = $this->getSubscores($mode, $entry->audition); + $subscores = $this->auditionService->getSubscores($entry->audition, $mode); $scoreTotal = 0; $weightsTotal = 0; $scoreArray = []; @@ -39,6 +41,7 @@ class CalculateScoreSheetTotal $finalScore = $scoreTotal / $weightsTotal; // put $final score at the beginning of the $ScoreArray array_unshift($scoreArray, $finalScore); + return $scoreArray; } @@ -54,4 +57,29 @@ class CalculateScoreSheetTotal throw new TabulationException('Invalid judge provided'); } } + + protected function getSubscores($mode, Audition $audition) + { + static $seatingSubscores = []; + static $advancementSubscores = []; + + if ($mode === 'seating') { + if (! isset($seatingSubscores[$audition->id])) { + $seatingSubscores[$audition->id] = $audition->scoringGuide->subscores->where('for_seating', + true)->sortBy('tiebreak_order'); + } + + return $seatingSubscores[$audition->id]; + } + if ($mode === 'advancement') { + if (! isset($advancementSubscores[$audition->id])) { + $advancementSubscores[$audition->id] = $audition->scoringGuide->subscores->where('for_advance', + true)->sortBy('tiebreak_order'); + } + + return $advancementSubscores[$audition->id]; + } + + throw new TabulationException('Invalid mode requested. Mode must be seating or advancement'); + } } diff --git a/app/Exceptions/AuditionServiceException.php b/app/Exceptions/AuditionServiceException.php new file mode 100644 index 0000000..ef02c52 --- /dev/null +++ b/app/Exceptions/AuditionServiceException.php @@ -0,0 +1,10 @@ +app->singleton(DrawService::class, function () { - return new DrawService(); - }); + $this->app->singleton(DrawService::class, function () { + return new DrawService(); + }); // - // $this->app->singleton(AuditionService::class, function () { - // return new AuditionService(); - // }); + $this->app->singleton(AuditionService::class, function () { + return new AuditionService(); + }); // // $this->app->singleton(SeatingService::class, function ($app) { // return new SeatingService($app->make(TabulationService::class)); diff --git a/app/Services/AuditionService.php b/app/Services/AuditionService.php index ba0db24..fbed6f1 100644 --- a/app/Services/AuditionService.php +++ b/app/Services/AuditionService.php @@ -2,14 +2,12 @@ namespace App\Services; +use App\Exceptions\AuditionServiceException; use App\Models\Audition; -use App\Models\ScoringGuide; -use Illuminate\Database\Eloquent\Builder; -use Illuminate\Support\Facades\Cache; +use Illuminate\Support\Facades\Log; class AuditionService { - /** * Create a new class instance. */ @@ -18,5 +16,51 @@ class AuditionService // } + /** + * @throws AuditionServiceException + */ + public function getSubscores(Audition $audition, $mode = 'seating', $sort = 'tiebreak') + { + static $auditionSubscores = []; + $this->validateAudition($audition); + $this->validateMode($mode); + $this->validateSort($sort); + $sortColumn = match ($sort) { + 'tiebreak' => 'tiebreak_order', + 'display' => 'display_order', + }; + $modeColumn = match ($mode) { + 'seating' => 'for_seating', + 'advancement' => 'for_advance', + }; + if (! isset($auditionSubscores[$mode][$sort])) { + $auditionSubscores[$mode][$sort] = $audition->scoringGuide->subscores->where($modeColumn, true)->sortBy($sortColumn); + } else { + Log::debug('Using cached subscores'); + } + + return $auditionSubscores[$mode][$sort]; + } + + protected function validateAudition($audition) + { + if (! $audition->exists()) { + throw new AuditionServiceException('Invalid audition provided'); + } + } + + protected function validateMode($mode) + { + if ($mode !== 'seating' && $mode !== 'advancement') { + throw new AuditionServiceException('Invalid mode requested. Mode must be seating or advancement'); + } + } + + protected function validateSort($sort) + { + if ($sort !== 'tiebreak' && $sort !== 'display') { + throw new AuditionServiceException('Invalid sort requested. Sort must be tiebreak or weight'); + } + } } diff --git a/tests/Feature/Actions/CalculateScoreSheetTotalTest.php b/tests/Feature/Actions/CalculateScoreSheetTotalTest.php index 5953ab1..870d729 100644 --- a/tests/Feature/Actions/CalculateScoreSheetTotalTest.php +++ b/tests/Feature/Actions/CalculateScoreSheetTotalTest.php @@ -11,23 +11,23 @@ use Illuminate\Foundation\Testing\RefreshDatabase; uses(RefreshDatabase::class); beforeEach(function () { - $this->calculator = new CalculateScoreSheetTotal(); + $this->calculator = app(CalculateScoreSheetTotal::class); }); it('throws an exception if an invalid mode is called for', function () { - $calculator = new CalculateScoreSheetTotal(); + $calculator = app(CalculateScoreSheetTotal::class); $calculator('anything', Entry::factory()->create(), User::factory()->create()); })->throws(TabulationException::class, 'Invalid mode requested. Mode must be seating or advancement'); it('throws an exception if an invalid judge is provided', function () { - $calculator = new CalculateScoreSheetTotal(); + $calculator = app(CalculateScoreSheetTotal::class); $calculator('seating', Entry::factory()->create(), User::factory()->make()); })->throws(TabulationException::class, 'Invalid judge provided'); it('throws an exception if an invalid entry is provided', function () { - $calculator = new CalculateScoreSheetTotal(); + $calculator = app(CalculateScoreSheetTotal::class); $calculator('advancement', Entry::factory()->make(), User::factory()->create()); })->throws(TabulationException::class, 'Invalid entry provided'); it('throws an exception if the specified judge has not scored the entry', function () { // Arrange - $calculator = new CalculateScoreSheetTotal(); + $calculator = app(CalculateScoreSheetTotal::class); // Act $calculator('seating', Entry::factory()->create(), User::factory()->create()); //Assert @@ -45,7 +45,7 @@ it('correctly calculates final score for seating', function () { 1005 => 90, ]; enterScore($judge, $entry, $scores); - $calculator = new CalculateScoreSheetTotal(); + $calculator = app(CalculateScoreSheetTotal::class); $total = $calculator('seating', $entry, $judge); expect($total[0])->toBe(68.75); $expectedArray = [68.75, 80, 60, 70, 50]; @@ -64,7 +64,7 @@ it('correctly calculates final score for advancement', function () { 1005 => 90, ]; enterScore($judge, $entry, $scores); - $calculator = new CalculateScoreSheetTotal(); + $calculator = app(CalculateScoreSheetTotal::class); $total = $calculator('advancement', $entry, $judge); expect($total[0])->toBe(73.75); $expectedArray = [73.75, 90, 80, 60, 70]; diff --git a/tests/Feature/Services/AuditionServiceTest.php b/tests/Feature/Services/AuditionServiceTest.php new file mode 100644 index 0000000..8804a14 --- /dev/null +++ b/tests/Feature/Services/AuditionServiceTest.php @@ -0,0 +1,38 @@ +expectException(\App\Exceptions\AuditionServiceException::class); + $auditionService->getSubscores(new Audition(), 'invalid_mode'); +}); +it('throws an exception when an invalid sort is requested', function () { + // Arrange + $auditionService = new \App\Services\AuditionService(); + $this->expectException(\App\Exceptions\AuditionServiceException::class); + // Act + $auditionService->getSubscores(new Audition(), 'seating', 'invalid_sort'); +}); +it('throws an exception when an invalid audition is provided', function () { + // Arrange + $auditionService = new \App\Services\AuditionService(); + $this->expectException(\App\Exceptions\AuditionServiceException::class); + $auditionService->getSubscores(new Audition(), 'seating', 'tiebreak'); + // Act & Assert + +}); +it('gets subscores for an audition', function () { + // Arrange + loadSampleAudition(); + $auditionService = new \App\Services\AuditionService(); + // Act + $subscores = $auditionService->getSubscores(Audition::find(1000), 'seating', 'tiebreak'); + // Assert + expect($subscores->toArray())->toBe(Audition::find(1000)->scoringGuide->subscores->where('for_seating', true)->sortBy('tiebreak_order')->toArray()); +});