From 49ebfda9a82c2b263a5a849d996839798463f1dc Mon Sep 17 00:00:00 2001 From: Matt Young Date: Wed, 10 Jul 2024 00:10:56 -0500 Subject: [PATCH] CalculateScoreSheetTotal working --- app/Actions/CalculateScoreSheetTotal.php | 57 +++++++++++++++ app/Providers/AppServiceProvider.php | 69 ++++++++---------- app/Services/ScoreService.php | 6 ++ .../Actions/CalculateScoreSheetTotalTest.php | 72 +++++++++++++++++++ .../Pages/Seating/auditionSeatingTest.php | 37 ++++++++++ tests/Feature/Services/ScoreServiceTest.php | 7 -- 6 files changed, 200 insertions(+), 48 deletions(-) create mode 100644 app/Actions/CalculateScoreSheetTotal.php create mode 100644 tests/Feature/Actions/CalculateScoreSheetTotalTest.php create mode 100644 tests/Feature/Pages/Seating/auditionSeatingTest.php diff --git a/app/Actions/CalculateScoreSheetTotal.php b/app/Actions/CalculateScoreSheetTotal.php new file mode 100644 index 0000000..7a72c90 --- /dev/null +++ b/app/Actions/CalculateScoreSheetTotal.php @@ -0,0 +1,57 @@ +basicValidations($mode, $entry, $judge); + $scoreSheet = ScoreSheet::where('entry_id', $entry->id)->where('user_id', $judge->id)->first(); + 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'), + }; + $scoreTotal = 0; + $weightsTotal = 0; + $scoreArray = []; + foreach ($subscores as $subscore) { + $weight = $subscore['weight']; + $score = $scoreSheet->subscores[$subscore->id]['score']; + $scoreArray[] = $score; + $scoreTotal += ($score * $weight); + $weightsTotal += $weight; + } + $finalScore = $scoreTotal / $weightsTotal; + // put $final score at the beginning of the $ScoreArray + array_unshift($scoreArray, $finalScore); + return $scoreArray; + } + + protected function basicValidations($mode, $entry, $judge): void + { + if ($mode !== 'seating' and $mode !== 'advancement') { + throw new TabulationException('Invalid mode requested. Mode must be seating or advancement'); + } + if (! $entry->exists()) { + throw new TabulationException('Invalid entry provided'); + } + if (! $judge->exists()) { + throw new TabulationException('Invalid judge provided'); + } + } +} diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index b804a12..bc0e89d 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -2,21 +2,12 @@ namespace App\Providers; -use App\Events\AuditionChange; -use App\Events\EntryChange; -use App\Events\ScoreSheetChange; -use App\Events\ScoringGuideChange; -use App\Events\SeatingLimitChange; -use App\Listeners\RefreshAuditionCache; -use App\Listeners\RefreshEntryCache; -use App\Listeners\RefreshScoreSheetCache; -use App\Listeners\RefreshScoringGuideCache; -use App\Listeners\RefreshSeatingLimitCache; use App\Models\Audition; use App\Models\Entry; use App\Models\Room; use App\Models\RoomUser; use App\Models\School; +use App\Models\ScoreSheet; use App\Models\ScoringGuide; use App\Models\SeatingLimit; use App\Models\Student; @@ -40,10 +31,7 @@ use App\Services\EntryService; use App\Services\ScoreService; use App\Services\SeatingService; use App\Services\TabulationService; -use Illuminate\Support\Facades\Event; use Illuminate\Support\ServiceProvider; -use App\Models\ScoreSheet; - class AppServiceProvider extends ServiceProvider { @@ -52,36 +40,36 @@ class AppServiceProvider extends ServiceProvider */ public function register(): void { - $this->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(SeatingService::class, function ($app) { + // return new SeatingService($app->make(TabulationService::class)); + // }); + // + $this->app->singleton(EntryService::class, function () { + return new EntryService(); }); - $this->app->singleton(AuditionService::class, function () { - return new AuditionService(); - }); - - $this->app->singleton(SeatingService::class, function ($app) { - return new SeatingService($app->make(TabulationService::class)); - }); - - $this->app->singleton(EntryService::class, function ($app) { - return new EntryService($app->make(AuditionService::class)); - }); - - $this->app->singleton(ScoreService::class, function ($app) { - return new ScoreService($app->make(AuditionService::class), $app->make(EntryService::class)); - }); - - $this->app->singleton(TabulationService::class, function ($app) { - return new TabulationService( - $app->make(AuditionService::class), - $app->make(ScoreService::class), - $app->make(EntryService::class)); - }); - - $this->app->singleton(DoublerService::class, function ($app) { - return new DoublerService($app->make(AuditionService::class), $app->make(TabulationService::class), $app->make(SeatingService::class)); + $this->app->singleton(ScoreService::class, function () { + return new ScoreService(); }); + // + // $this->app->singleton(TabulationService::class, function ($app) { + // return new TabulationService( + // $app->make(AuditionService::class), + // $app->make(ScoreService::class), + // $app->make(EntryService::class)); + // }); + // + // $this->app->singleton(DoublerService::class, function ($app) { + // return new DoublerService($app->make(AuditionService::class), $app->make(TabulationService::class), $app->make(SeatingService::class)); + // }); } /** @@ -102,6 +90,5 @@ class AppServiceProvider extends ServiceProvider User::observe(UserObserver::class); SeatingLimit::observe(SeatingLimitObserver::class); - } } diff --git a/app/Services/ScoreService.php b/app/Services/ScoreService.php index 7728591..25d7dc7 100644 --- a/app/Services/ScoreService.php +++ b/app/Services/ScoreService.php @@ -3,6 +3,7 @@ namespace App\Services; use App\Models\Entry; +use App\Models\User; class ScoreService { @@ -21,5 +22,10 @@ class ScoreService return $requiredJudges === $scoreSheets; } + public function scoreSheetTotal(string $mode, User $judge, Entry $entry): float + { + + } + } diff --git a/tests/Feature/Actions/CalculateScoreSheetTotalTest.php b/tests/Feature/Actions/CalculateScoreSheetTotalTest.php new file mode 100644 index 0000000..ec18e55 --- /dev/null +++ b/tests/Feature/Actions/CalculateScoreSheetTotalTest.php @@ -0,0 +1,72 @@ +calculator = new CalculateScoreSheetTotal(); +}); +it('throws an exception if an invalid mode is called for', function () { + $calculator = new CalculateScoreSheetTotal(); + $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('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('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(); + // Act + $calculator('seating', Entry::factory()->create(), User::factory()->create()); + //Assert +})->throws(TabulationException::class, 'No score sheet by that judge for that entry'); +it('correctly calculates final score for seating', function () { + loadSampleAudition(); + $judge = User::factory()->create(); + Room::find(1000)->addJudge($judge); + $entry = Entry::factory()->create(['audition_id' => 1000]); + $scores = [ + 1001 => 50, + 1002 => 60, + 1003 => 70, + 1004 => 80, + 1005 => 90, + ]; + enterScore($judge, $entry, $scores); + $calculator = new CalculateScoreSheetTotal(); + $total = $calculator('seating', $entry, $judge); + expect($total[0])->toBe(68.75); + $expectedArray = [68.75, 80, 60, 70, 50]; + expect($total)->toBe($expectedArray); +}); +it('correctly calculates final score for advancement', function () { + loadSampleAudition(); + $judge = User::factory()->create(); + Room::find(1000)->addJudge($judge); + $entry = Entry::factory()->create(['audition_id' => 1000]); + $scores = [ + 1001 => 50, + 1002 => 60, + 1003 => 70, + 1004 => 80, + 1005 => 90, + ]; + enterScore($judge, $entry, $scores); + $calculator = new CalculateScoreSheetTotal(); + $total = $calculator('advancement', $entry, $judge); + expect($total[0])->toBe(73.75); + $expectedArray = [73.75, 90, 80, 60, 70]; + expect($total)->toBe($expectedArray); +}); diff --git a/tests/Feature/Pages/Seating/auditionSeatingTest.php b/tests/Feature/Pages/Seating/auditionSeatingTest.php new file mode 100644 index 0000000..d46e0ed --- /dev/null +++ b/tests/Feature/Pages/Seating/auditionSeatingTest.php @@ -0,0 +1,37 @@ +audition = Audition::factory()->create(); + $this->r = route('seating.audition', $this->audition); +}); + +it('denies access to a guest', function () { + get($this->r) + ->assertRedirect(route('home')); +}); + +it('denies access to a normal user', function () { + actAsNormal(); + get($this->r) + ->assertRedirect(route('dashboard')) + ->assertSessionHas('error', 'You are not authorized to perform this action'); +}); +it('grants access to admin', function () { + // Arrange + actAsAdmin(); + // Act & Assert + get($this->r)->assertOk(); +}); +it('grants access to tabulators', function () { + // Arrange + actAsTab(); + // Act & Assert + get($this->r)->assertOk(); +}); diff --git a/tests/Feature/Services/ScoreServiceTest.php b/tests/Feature/Services/ScoreServiceTest.php index 97728f6..3c19f15 100644 --- a/tests/Feature/Services/ScoreServiceTest.php +++ b/tests/Feature/Services/ScoreServiceTest.php @@ -14,13 +14,6 @@ uses(RefreshDatabase::class); beforeEach(function () { $this->scoreService = new ScoreService(); }); -it('can record a score', function () { - // Arrange - // run the seeder AuditionWithScoringGuideAndRoom - artisan('db:seed', ['--class' => 'AuditionWithScoringGuideAndRoom']); - // Act & Assert - expect(Audition::find(1000)->name)->toBe('Test Audition'); -}); it('can check if an entry is fully scored', function () { $room = Room::factory()->create();