CalculateScoreSheetTotal working
This commit is contained in:
parent
0eda3ab32e
commit
49ebfda9a8
|
|
@ -0,0 +1,57 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/** @noinspection PhpUnhandledExceptionInspection */
|
||||||
|
|
||||||
|
namespace App\Actions;
|
||||||
|
|
||||||
|
use App\Exceptions\TabulationException;
|
||||||
|
use App\Models\Entry;
|
||||||
|
use App\Models\ScoreSheet;
|
||||||
|
use App\Models\User;
|
||||||
|
|
||||||
|
class CalculateScoreSheetTotal
|
||||||
|
{
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __invoke(string $mode, Entry $entry, User $judge): array
|
||||||
|
{
|
||||||
|
$this->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');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,21 +2,12 @@
|
||||||
|
|
||||||
namespace App\Providers;
|
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\Audition;
|
||||||
use App\Models\Entry;
|
use App\Models\Entry;
|
||||||
use App\Models\Room;
|
use App\Models\Room;
|
||||||
use App\Models\RoomUser;
|
use App\Models\RoomUser;
|
||||||
use App\Models\School;
|
use App\Models\School;
|
||||||
|
use App\Models\ScoreSheet;
|
||||||
use App\Models\ScoringGuide;
|
use App\Models\ScoringGuide;
|
||||||
use App\Models\SeatingLimit;
|
use App\Models\SeatingLimit;
|
||||||
use App\Models\Student;
|
use App\Models\Student;
|
||||||
|
|
@ -40,10 +31,7 @@ use App\Services\EntryService;
|
||||||
use App\Services\ScoreService;
|
use App\Services\ScoreService;
|
||||||
use App\Services\SeatingService;
|
use App\Services\SeatingService;
|
||||||
use App\Services\TabulationService;
|
use App\Services\TabulationService;
|
||||||
use Illuminate\Support\Facades\Event;
|
|
||||||
use Illuminate\Support\ServiceProvider;
|
use Illuminate\Support\ServiceProvider;
|
||||||
use App\Models\ScoreSheet;
|
|
||||||
|
|
||||||
|
|
||||||
class AppServiceProvider extends ServiceProvider
|
class AppServiceProvider extends ServiceProvider
|
||||||
{
|
{
|
||||||
|
|
@ -52,36 +40,36 @@ 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 () {
|
||||||
|
// 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 () {
|
$this->app->singleton(ScoreService::class, function () {
|
||||||
return new AuditionService();
|
return new ScoreService();
|
||||||
});
|
|
||||||
|
|
||||||
$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(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);
|
User::observe(UserObserver::class);
|
||||||
SeatingLimit::observe(SeatingLimitObserver::class);
|
SeatingLimit::observe(SeatingLimitObserver::class);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
namespace App\Services;
|
namespace App\Services;
|
||||||
|
|
||||||
use App\Models\Entry;
|
use App\Models\Entry;
|
||||||
|
use App\Models\User;
|
||||||
|
|
||||||
class ScoreService
|
class ScoreService
|
||||||
{
|
{
|
||||||
|
|
@ -21,5 +22,10 @@ class ScoreService
|
||||||
return $requiredJudges === $scoreSheets;
|
return $requiredJudges === $scoreSheets;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function scoreSheetTotal(string $mode, User $judge, Entry $entry): float
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/** @noinspection PhpUnhandledExceptionInspection */
|
||||||
|
|
||||||
|
use App\Actions\CalculateScoreSheetTotal;
|
||||||
|
use App\Exceptions\TabulationException;
|
||||||
|
use App\Models\Entry;
|
||||||
|
use App\Models\Room;
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
|
||||||
|
uses(RefreshDatabase::class);
|
||||||
|
beforeEach(function () {
|
||||||
|
$this->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);
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Models\Audition;
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
|
||||||
|
use function Pest\Laravel\get;
|
||||||
|
|
||||||
|
uses(RefreshDatabase::class);
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
$this->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();
|
||||||
|
});
|
||||||
|
|
@ -14,13 +14,6 @@ uses(RefreshDatabase::class);
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
$this->scoreService = new ScoreService();
|
$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 () {
|
it('can check if an entry is fully scored', function () {
|
||||||
$room = Room::factory()->create();
|
$room = Room::factory()->create();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue