Work on audition service

This commit is contained in:
Matt Young 2024-07-11 00:32:53 -05:00
parent 6f0a4ac9bc
commit e11741a0a1
7 changed files with 145 additions and 26 deletions

View File

@ -13,7 +13,8 @@ class AllJudgesCount implements CalculateEntryScore
public function __construct() public function __construct()
{ {
$this->calculator = new CalculateScoreSheetTotal(); #$this->calculator = new CalculateScoreSheetTotal();
$this->calculator = app(CalculateScoreSheetTotal::class);
} }
public function calculate(string $mode, Entry $entry): array public function calculate(string $mode, Entry $entry): array
@ -31,6 +32,7 @@ class AllJudgesCount implements CalculateEntryScore
foreach ($entry->audition->judges as $judge) { foreach ($entry->audition->judges as $judge) {
$scores[] = $this->calculator->__invoke($mode, $entry, $judge); $scores[] = $this->calculator->__invoke($mode, $entry, $judge);
} }
$sums = [];
// Sum each subscore from the judges // Sum each subscore from the judges
foreach ($scores as $score) { foreach ($scores as $score) {
$index = 0; $index = 0;

View File

@ -5,14 +5,18 @@
namespace App\Actions\Tabulation; namespace App\Actions\Tabulation;
use App\Exceptions\TabulationException; use App\Exceptions\TabulationException;
use App\Models\Audition;
use App\Models\Entry; use App\Models\Entry;
use App\Models\ScoreSheet; use App\Models\ScoreSheet;
use App\Models\User; use App\Models\User;
use App\Services\AuditionService;
class CalculateScoreSheetTotal 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 public function __invoke(string $mode, Entry $entry, User $judge): array
@ -22,10 +26,8 @@ class CalculateScoreSheetTotal
if (! $scoreSheet) { if (! $scoreSheet) {
throw new TabulationException('No score sheet by that judge for that entry'); throw new TabulationException('No score sheet by that judge for that entry');
} }
$subscores = match ($mode) { #$subscores = $this->getSubscores($mode, $entry->audition);
'seating' => $entry->audition->scoringGuide->subscores->where('for_seating', true)->sortBy('tiebreak_order'), $subscores = $this->auditionService->getSubscores($entry->audition, $mode);
'advancement' => $entry->audition->scoringGuide->subscores->where('for_advance', true)->sortBy('tiebreak_order'),
};
$scoreTotal = 0; $scoreTotal = 0;
$weightsTotal = 0; $weightsTotal = 0;
$scoreArray = []; $scoreArray = [];
@ -39,6 +41,7 @@ class CalculateScoreSheetTotal
$finalScore = $scoreTotal / $weightsTotal; $finalScore = $scoreTotal / $weightsTotal;
// put $final score at the beginning of the $ScoreArray // put $final score at the beginning of the $ScoreArray
array_unshift($scoreArray, $finalScore); array_unshift($scoreArray, $finalScore);
return $scoreArray; return $scoreArray;
} }
@ -54,4 +57,29 @@ class CalculateScoreSheetTotal
throw new TabulationException('Invalid judge provided'); 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');
}
} }

View File

@ -0,0 +1,10 @@
<?php
namespace App\Exceptions;
use Exception;
class AuditionServiceException extends Exception
{
//
}

View File

@ -2,9 +2,6 @@
namespace App\Providers; namespace App\Providers;
use App\Actions\Tabulation\AllJudgesCount;
use App\Actions\Tabulation\CalculateEntryScore;
use App\Actions\Tabulation\CalculateScoreSheetTotal;
use App\Models\Audition; use App\Models\Audition;
use App\Models\Entry; use App\Models\Entry;
use App\Models\Room; use App\Models\Room;
@ -43,13 +40,13 @@ class AppServiceProvider extends ServiceProvider
*/ */
public function register(): void public function register(): void
{ {
$this->app->singleton(DrawService::class, function () { $this->app->singleton(DrawService::class, function () {
return new DrawService(); return new DrawService();
}); });
// //
// $this->app->singleton(AuditionService::class, function () { $this->app->singleton(AuditionService::class, function () {
// return new AuditionService(); return new AuditionService();
// }); });
// //
// $this->app->singleton(SeatingService::class, function ($app) { // $this->app->singleton(SeatingService::class, function ($app) {
// return new SeatingService($app->make(TabulationService::class)); // return new SeatingService($app->make(TabulationService::class));

View File

@ -2,14 +2,12 @@
namespace App\Services; namespace App\Services;
use App\Exceptions\AuditionServiceException;
use App\Models\Audition; use App\Models\Audition;
use App\Models\ScoringGuide; use Illuminate\Support\Facades\Log;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\Cache;
class AuditionService class AuditionService
{ {
/** /**
* Create a new class instance. * 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');
}
}
} }

View File

@ -11,23 +11,23 @@ use Illuminate\Foundation\Testing\RefreshDatabase;
uses(RefreshDatabase::class); uses(RefreshDatabase::class);
beforeEach(function () { beforeEach(function () {
$this->calculator = new CalculateScoreSheetTotal(); $this->calculator = app(CalculateScoreSheetTotal::class);
}); });
it('throws an exception if an invalid mode is called for', function () { 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()); $calculator('anything', Entry::factory()->create(), User::factory()->create());
})->throws(TabulationException::class, 'Invalid mode requested. Mode must be seating or advancement'); })->throws(TabulationException::class, 'Invalid mode requested. Mode must be seating or advancement');
it('throws an exception if an invalid judge is provided', function () { 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()); $calculator('seating', Entry::factory()->create(), User::factory()->make());
})->throws(TabulationException::class, 'Invalid judge provided'); })->throws(TabulationException::class, 'Invalid judge provided');
it('throws an exception if an invalid entry is provided', function () { 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()); $calculator('advancement', Entry::factory()->make(), User::factory()->create());
})->throws(TabulationException::class, 'Invalid entry provided'); })->throws(TabulationException::class, 'Invalid entry provided');
it('throws an exception if the specified judge has not scored the entry', function () { it('throws an exception if the specified judge has not scored the entry', function () {
// Arrange // Arrange
$calculator = new CalculateScoreSheetTotal(); $calculator = app(CalculateScoreSheetTotal::class);
// Act // Act
$calculator('seating', Entry::factory()->create(), User::factory()->create()); $calculator('seating', Entry::factory()->create(), User::factory()->create());
//Assert //Assert
@ -45,7 +45,7 @@ it('correctly calculates final score for seating', function () {
1005 => 90, 1005 => 90,
]; ];
enterScore($judge, $entry, $scores); enterScore($judge, $entry, $scores);
$calculator = new CalculateScoreSheetTotal(); $calculator = app(CalculateScoreSheetTotal::class);
$total = $calculator('seating', $entry, $judge); $total = $calculator('seating', $entry, $judge);
expect($total[0])->toBe(68.75); expect($total[0])->toBe(68.75);
$expectedArray = [68.75, 80, 60, 70, 50]; $expectedArray = [68.75, 80, 60, 70, 50];
@ -64,7 +64,7 @@ it('correctly calculates final score for advancement', function () {
1005 => 90, 1005 => 90,
]; ];
enterScore($judge, $entry, $scores); enterScore($judge, $entry, $scores);
$calculator = new CalculateScoreSheetTotal(); $calculator = app(CalculateScoreSheetTotal::class);
$total = $calculator('advancement', $entry, $judge); $total = $calculator('advancement', $entry, $judge);
expect($total[0])->toBe(73.75); expect($total[0])->toBe(73.75);
$expectedArray = [73.75, 90, 80, 60, 70]; $expectedArray = [73.75, 90, 80, 60, 70];

View File

@ -0,0 +1,38 @@
<?php
/** @noinspection PhpUnhandledExceptionInspection */
use App\Models\Audition;
use Illuminate\Foundation\Testing\RefreshDatabase;
uses(RefreshDatabase::class);
it('throws an exception when an invalid mode is requested', function () {
$auditionService = new \App\Services\AuditionService();
$this->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());
});