Rewrite tabulation #14
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Exceptions;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
class AuditionServiceException extends Exception
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
@ -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));
|
||||||
|
|
|
||||||
|
|
@ -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');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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];
|
||||||
|
|
|
||||||
|
|
@ -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());
|
||||||
|
});
|
||||||
Loading…
Reference in New Issue