Implement EnterScore action
This commit is contained in:
parent
d45ebf4eec
commit
0eda3ab32e
|
|
@ -0,0 +1,101 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/** @noinspection PhpUnhandledExceptionInspection */
|
||||||
|
|
||||||
|
/** @noinspection PhpMissingReturnTypeInspection */
|
||||||
|
|
||||||
|
namespace App\Actions;
|
||||||
|
|
||||||
|
use App\Exceptions\ScoreEntryException;
|
||||||
|
use App\Models\Entry;
|
||||||
|
use App\Models\ScoreSheet;
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
|
class EnterScore
|
||||||
|
{
|
||||||
|
// TODO implement this action every place that can save a score
|
||||||
|
public function __invoke(User $user, Entry $entry, array $scores): ScoreSheet
|
||||||
|
{
|
||||||
|
$scores = collect($scores);
|
||||||
|
$this->basicChecks($user, $entry, $scores);
|
||||||
|
$this->checkJudgeAssignment($user, $entry);
|
||||||
|
$this->checkForExistingScore($user, $entry);
|
||||||
|
$this->validateScoresSubmitted($entry, $scores);
|
||||||
|
$entry->removeFlag('no_show');
|
||||||
|
$newScoreSheet = ScoreSheet::create([
|
||||||
|
'user_id' => $user->id,
|
||||||
|
'entry_id' => $entry->id,
|
||||||
|
'subscores' => $this->subscoresForStorage($entry, $scores),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return $newScoreSheet;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function subscoresForStorage(Entry $entry, Collection $scores)
|
||||||
|
{
|
||||||
|
$subscores = [];
|
||||||
|
foreach ($entry->audition->scoringGuide->subscores as $subscore) {
|
||||||
|
$subscores[$subscore->id] = [
|
||||||
|
'score' => $scores[$subscore->id],
|
||||||
|
'subscore_id' => $subscore->id,
|
||||||
|
'subscore_name' => $subscore->name,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $subscores;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function checkForExistingScore(User $user, Entry $entry)
|
||||||
|
{
|
||||||
|
if (ScoreSheet::where('user_id', $user->id)->where('entry_id', $entry->id)->exists()) {
|
||||||
|
throw new ScoreEntryException('That judge has already entered scores for that entry');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function validateScoresSubmitted(Entry $entry, Collection $scores)
|
||||||
|
{
|
||||||
|
$subscoresRequired = $entry->audition->scoringGuide->subscores;
|
||||||
|
|
||||||
|
foreach ($subscoresRequired as $subscore) {
|
||||||
|
// check that there is an element in the $scores collection with the key = $subscore->id
|
||||||
|
if (! $scores->keys()->contains($subscore->id)) {
|
||||||
|
throw new ScoreEntryException('Invalid Score Submission');
|
||||||
|
}
|
||||||
|
if ($scores[$subscore->id] > $subscore->max_score) {
|
||||||
|
throw new ScoreEntryException('Supplied subscore exceeds maximum allowed');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function checkJudgeAssignment(User $user, Entry $entry)
|
||||||
|
{
|
||||||
|
$check = DB::table('room_user')
|
||||||
|
->where('room_id', $entry->audition->room_id)
|
||||||
|
->where('user_id', $user->id)->exists();
|
||||||
|
if (! $check) {
|
||||||
|
throw new ScoreEntryException('This judge is not assigned to judge this entry');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function basicChecks(User $user, Entry $entry, Collection $scores)
|
||||||
|
{
|
||||||
|
if (! $user->exists()) {
|
||||||
|
throw new ScoreEntryException('User does not exist');
|
||||||
|
}
|
||||||
|
if (! $entry->exists()) {
|
||||||
|
throw new ScoreEntryException('Entry does not exist');
|
||||||
|
}
|
||||||
|
if ($entry->audition->hasFlag('seats_published')) {
|
||||||
|
throw new ScoreEntryException('Cannot score an entry in an audition with published seats');
|
||||||
|
}
|
||||||
|
if ($entry->audition->hasFlag('advancement_published')) {
|
||||||
|
throw new ScoreEntryException('Cannot score an entry in an audition with published advancement');
|
||||||
|
}
|
||||||
|
$requiredScores = $entry->audition->scoringGuide->subscores()->count();
|
||||||
|
if ($scores->count() !== $requiredScores) {
|
||||||
|
throw new ScoreEntryException('Invalid number of scores');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Exceptions;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
class ScoreEntryException extends Exception
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
@ -3,27 +3,23 @@
|
||||||
namespace App\Services;
|
namespace App\Services;
|
||||||
|
|
||||||
use App\Models\Entry;
|
use App\Models\Entry;
|
||||||
use App\Models\ScoreSheet;
|
|
||||||
use App\Models\ScoringGuide;
|
|
||||||
use Illuminate\Support\Collection;
|
|
||||||
use Illuminate\Support\Facades\Cache;
|
|
||||||
use Illuminate\Support\Facades\DB;
|
|
||||||
|
|
||||||
use function array_unshift;
|
|
||||||
|
|
||||||
class ScoreService
|
class ScoreService
|
||||||
{
|
{
|
||||||
protected $auditionCache;
|
|
||||||
|
|
||||||
protected $entryCache;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new class instance.
|
* Create a new class instance.
|
||||||
*/
|
*/
|
||||||
public function __construct(AuditionService $auditionCache, EntryService $entryCache)
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->auditionCache = $auditionCache;
|
|
||||||
$this->entryCache = $entryCache;
|
}
|
||||||
|
public function isEntryFullyScored(Entry $entry): bool
|
||||||
|
{
|
||||||
|
$requiredJudges = $entry->audition->judges()->count();
|
||||||
|
$scoreSheets = $entry->scoreSheets()->count();
|
||||||
|
|
||||||
|
return $requiredJudges === $scoreSheets;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,9 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use App\Actions\EnterScore;
|
||||||
|
use App\Exceptions\ScoreEntryException;
|
||||||
|
use App\Models\Entry;
|
||||||
|
use App\Models\User;
|
||||||
use App\Settings;
|
use App\Settings;
|
||||||
|
|
||||||
function tw_max_width_class_array(): array
|
function tw_max_width_class_array(): array
|
||||||
|
|
@ -25,7 +29,16 @@ function tw_max_width_class_array(): array
|
||||||
return $return;
|
return $return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function auditionSetting($key)
|
||||||
function auditionSetting($key) {
|
{
|
||||||
return Settings::get($key);
|
return Settings::get($key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws ScoreEntryException
|
||||||
|
*/
|
||||||
|
function enterScore(User $user, Entry $entry, array $scores): \App\Models\ScoreSheet
|
||||||
|
{
|
||||||
|
$scoreEntry = new EnterScore();
|
||||||
|
return $scoreEntry($user, $entry, $scores);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,84 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Seeders;
|
||||||
|
|
||||||
|
use App\Models\Audition;
|
||||||
|
use App\Models\Room;
|
||||||
|
use App\Models\ScoringGuide;
|
||||||
|
use App\Models\SubscoreDefinition;
|
||||||
|
use Illuminate\Database\Seeder;
|
||||||
|
|
||||||
|
class AuditionWithScoringGuideAndRoom extends Seeder
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the database seeds.
|
||||||
|
*/
|
||||||
|
// TiebreakOrder: Tone, Sightreading, Etude 1, Etude 2, Scale
|
||||||
|
// Scale is only for seating, Tone is only for advancement
|
||||||
|
public function run(): void
|
||||||
|
{
|
||||||
|
$room = Room::factory()->create(['id' => 1000]);
|
||||||
|
$sg = ScoringGuide::factory()->create(['id' => 1000]);
|
||||||
|
SubscoreDefinition::create([
|
||||||
|
'id' => 1001,
|
||||||
|
'scoring_guide_id' => $sg->id,
|
||||||
|
'name' => 'Scale',
|
||||||
|
'max_score' => 100,
|
||||||
|
'weight' => 1,
|
||||||
|
'display_order' => 1,
|
||||||
|
'tiebreak_order' => 5,
|
||||||
|
'for_seating' => 1,
|
||||||
|
'for_advance' => 0,
|
||||||
|
]);
|
||||||
|
SubscoreDefinition::create([
|
||||||
|
'id' => 1002,
|
||||||
|
'scoring_guide_id' => $sg->id,
|
||||||
|
'name' => 'Etude 1',
|
||||||
|
'max_score' => 100,
|
||||||
|
'weight' => 2,
|
||||||
|
'display_order' => 2,
|
||||||
|
'tiebreak_order' => 3,
|
||||||
|
'for_seating' => 1,
|
||||||
|
'for_advance' => 1,
|
||||||
|
]);
|
||||||
|
SubscoreDefinition::create([
|
||||||
|
'id' => 1003,
|
||||||
|
'scoring_guide_id' => $sg->id,
|
||||||
|
'name' => 'Etude 2',
|
||||||
|
'max_score' => 100,
|
||||||
|
'weight' => 2,
|
||||||
|
'display_order' => 3,
|
||||||
|
'tiebreak_order' => 4,
|
||||||
|
'for_seating' => 1,
|
||||||
|
'for_advance' => 1,
|
||||||
|
]);
|
||||||
|
SubscoreDefinition::create([
|
||||||
|
'id' => 1004,
|
||||||
|
'scoring_guide_id' => $sg->id,
|
||||||
|
'name' => 'Sight Reading',
|
||||||
|
'max_score' => 100,
|
||||||
|
'weight' => 3,
|
||||||
|
'display_order' => 4,
|
||||||
|
'tiebreak_order' => 2,
|
||||||
|
'for_seating' => 1,
|
||||||
|
'for_advance' => 1,
|
||||||
|
]);
|
||||||
|
SubscoreDefinition::create([
|
||||||
|
'id' => 1005,
|
||||||
|
'scoring_guide_id' => $sg->id,
|
||||||
|
'name' => 'Tone',
|
||||||
|
'max_score' => 100,
|
||||||
|
'weight' => 1,
|
||||||
|
'display_order' => 5,
|
||||||
|
'tiebreak_order' => 1,
|
||||||
|
'for_seating' => 0,
|
||||||
|
'for_advance' => 1,
|
||||||
|
]);
|
||||||
|
Audition::factory()->create([
|
||||||
|
'id' => 1000,
|
||||||
|
'room_id' => $room->id,
|
||||||
|
'scoring_guide_id' => $sg->id,
|
||||||
|
'name' => 'Test Audition',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Database\Seeders;
|
|
||||||
|
|
||||||
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
|
||||||
use Illuminate\Database\Seeder;
|
|
||||||
|
|
||||||
class RoomSeeder extends Seeder
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Run the database seeds.
|
|
||||||
*/
|
|
||||||
public function run(): void
|
|
||||||
{
|
|
||||||
//
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Database\Seeders;
|
|
||||||
|
|
||||||
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
|
||||||
use Illuminate\Database\Seeder;
|
|
||||||
|
|
||||||
class SchoolSeeder extends Seeder
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Run the database seeds.
|
|
||||||
*/
|
|
||||||
public function run(): void
|
|
||||||
{
|
|
||||||
//
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Database\Seeders;
|
|
||||||
|
|
||||||
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
|
||||||
use Illuminate\Database\Seeder;
|
|
||||||
|
|
||||||
class ScoreSheetSeeder extends Seeder
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Run the database seeds.
|
|
||||||
*/
|
|
||||||
public function run(): void
|
|
||||||
{
|
|
||||||
//
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Database\Seeders;
|
|
||||||
|
|
||||||
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
|
||||||
use Illuminate\Database\Seeder;
|
|
||||||
|
|
||||||
class ScoringGuideSeeder extends Seeder
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Run the database seeds.
|
|
||||||
*/
|
|
||||||
public function run(): void
|
|
||||||
{
|
|
||||||
//
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Database\Seeders;
|
|
||||||
|
|
||||||
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
|
||||||
use Illuminate\Database\Seeder;
|
|
||||||
|
|
||||||
class StudentSeeder extends Seeder
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Run the database seeds.
|
|
||||||
*/
|
|
||||||
public function run(): void
|
|
||||||
{
|
|
||||||
//
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Database\Seeders;
|
|
||||||
|
|
||||||
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
|
||||||
use Illuminate\Database\Seeder;
|
|
||||||
|
|
||||||
class SubscoreDefinitionSeeder extends Seeder
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Run the database seeds.
|
|
||||||
*/
|
|
||||||
public function run(): void
|
|
||||||
{
|
|
||||||
//
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -22,7 +22,7 @@
|
||||||
<div class="w-56 shrink rounded-xl bg-white p-4 text-sm font-semibold leading-6 text-gray-900 shadow-lg ring-1 ring-gray-900/5">
|
<div class="w-56 shrink rounded-xl bg-white p-4 text-sm font-semibold leading-6 text-gray-900 shadow-lg ring-1 ring-gray-900/5">
|
||||||
<a href="{{ route('scores.chooseEntry') }}" class="block p-2 hover:text-indigo-600">Enter Scores</a>
|
<a href="{{ route('scores.chooseEntry') }}" class="block p-2 hover:text-indigo-600">Enter Scores</a>
|
||||||
<a href="{{ route('entry-flags.noShowSelect') }}" class="block p-2 hover:text-indigo-600">Enter No-Shows</a>
|
<a href="{{ route('entry-flags.noShowSelect') }}" class="block p-2 hover:text-indigo-600">Enter No-Shows</a>
|
||||||
<a href="{{ route('tabulation.status') }}" class="block p-2 hover:text-indigo-600">Audition Status</a>
|
<a href="{{ route('seating.status') }}" class="block p-2 hover:text-indigo-600">Audition Status</a>
|
||||||
<a href="{{ route('advancement.status') }}" class="block p-2 hover:text-indigo-600">{{ auditionSetting('advanceTo') }} Status</a>
|
<a href="{{ route('advancement.status') }}" class="block p-2 hover:text-indigo-600">{{ auditionSetting('advanceTo') }} Status</a>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
@php use App\Enums\AuditionFlags;use App\Models\Audition;use App\Models\AuditionFlag; @endphp
|
@php use App\Enums\AuditionFlags;use App\Models\Audition;use App\Models\AuditionFlag;use App\Models\Entry;use App\Models\User; @endphp
|
||||||
@php @endphp
|
@php @endphp
|
||||||
@inject('scoreservice','App\Services\ScoreService');
|
@inject('scoreservice','App\Services\ScoreService');
|
||||||
@inject('auditionService','App\Services\AuditionService');
|
@inject('auditionService','App\Services\AuditionService');
|
||||||
|
|
@ -8,8 +8,17 @@
|
||||||
<x-layout.app>
|
<x-layout.app>
|
||||||
<x-slot:page_title>Test Page</x-slot:page_title>
|
<x-slot:page_title>Test Page</x-slot:page_title>
|
||||||
@php
|
@php
|
||||||
$audition = Audition::first();
|
$entry = Entry::find(1127);
|
||||||
$audition->addFlag('drawn');
|
$judge = User::find(65);
|
||||||
|
$scoreArray = [
|
||||||
|
1 => 50,
|
||||||
|
2 => 60,
|
||||||
|
3 => 70,
|
||||||
|
4 => 80,
|
||||||
|
5 => 90,
|
||||||
|
];
|
||||||
|
enterScore($judge, $entry, $scoreArray);
|
||||||
|
dump($entry->audition->name);
|
||||||
@endphp
|
@endphp
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,185 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/** @noinspection PhpUnhandledExceptionInspection */
|
||||||
|
|
||||||
|
use App\Actions\EnterScore;
|
||||||
|
use App\Exceptions\ScoreEntryException;
|
||||||
|
use App\Models\Entry;
|
||||||
|
use App\Models\Room;
|
||||||
|
use App\Models\ScoreSheet;
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
|
||||||
|
uses(RefreshDatabase::class);
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
$this->scoreEntry = new EnterScore();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('throws an exception if the user does not exist', function () {
|
||||||
|
$user = User::factory()->make();
|
||||||
|
$entry = Entry::factory()->create();
|
||||||
|
enterScore($user, $entry, []);
|
||||||
|
})->throws(ScoreEntryException::class, 'User does not exist');
|
||||||
|
test('throws an exception if the entry does not exist', function () {
|
||||||
|
$user = User::factory()->create();
|
||||||
|
$entry = Entry::factory()->make();
|
||||||
|
enterScore($user, $entry, []);
|
||||||
|
})->throws(ScoreEntryException::class, 'Entry does not exist');
|
||||||
|
it('throws an exception if the seats for the entries audition are published', function () {
|
||||||
|
// Arrange
|
||||||
|
$user = User::factory()->create();
|
||||||
|
$entry = Entry::factory()->create();
|
||||||
|
$entry->audition->addFlag('seats_published');
|
||||||
|
// Act & Assert
|
||||||
|
enterScore($user, $entry, []);
|
||||||
|
})->throws(ScoreEntryException::class, 'Cannot score an entry in an audition with published seats');
|
||||||
|
it('throws an exception if the advancement for the entries audition is published', function () {
|
||||||
|
// Arrange
|
||||||
|
$user = User::factory()->create();
|
||||||
|
$entry = Entry::factory()->create();
|
||||||
|
$entry->audition->addFlag('advancement_published');
|
||||||
|
// Act & Assert
|
||||||
|
enterScore($user, $entry, []);
|
||||||
|
})->throws(ScoreEntryException::class, 'Cannot score an entry in an audition with published advancement');
|
||||||
|
it('throws an exception if too many scores are submitted', function () {
|
||||||
|
// Arrange
|
||||||
|
loadSampleAudition();
|
||||||
|
$judge = User::factory()->create();
|
||||||
|
$entry = Entry::factory()->create(['audition_id' => 1000]);
|
||||||
|
Room::find(1000)->addJudge($judge);
|
||||||
|
// Act & Assert
|
||||||
|
enterScore($judge, $entry, [1, 2, 5, 3, 2, 1]);
|
||||||
|
})->throws(ScoreEntryException::class, 'Invalid number of scores');
|
||||||
|
it('throws an exception if too few scores are submitted', function () {
|
||||||
|
// Arrange
|
||||||
|
loadSampleAudition();
|
||||||
|
$judge = User::factory()->create();
|
||||||
|
$entry = Entry::factory()->create(['audition_id' => 1000]);
|
||||||
|
Room::find(1000)->addJudge($judge);
|
||||||
|
// Act & Assert
|
||||||
|
enterScore($judge, $entry, [1, 2, 5, 3]);
|
||||||
|
})->throws(ScoreEntryException::class, 'Invalid number of scores');
|
||||||
|
it('throws an exception if the user is not assigned to judge the entry', function () {
|
||||||
|
// Arrange
|
||||||
|
loadSampleAudition();
|
||||||
|
$judge = User::factory()->create();
|
||||||
|
$entry = Entry::factory()->create(['audition_id' => 1000]);
|
||||||
|
// Act & Assert
|
||||||
|
enterScore($judge, $entry, [1, 2, 5, 3, 7]);
|
||||||
|
})->throws(ScoreEntryException::class, 'This judge is not assigned to judge this entry');
|
||||||
|
it('throws an exception if an invalid subscore is provided', function () {
|
||||||
|
loadSampleAudition();
|
||||||
|
$judge = User::factory()->create();
|
||||||
|
$entry = Entry::factory()->create(['audition_id' => 1000]);
|
||||||
|
Room::find(1000)->addJudge($judge);
|
||||||
|
$scores = [
|
||||||
|
1001 => 98,
|
||||||
|
1002 => 90,
|
||||||
|
1003 => 87,
|
||||||
|
1004 => 78,
|
||||||
|
1006 => 88,
|
||||||
|
];
|
||||||
|
enterScore($judge, $entry, $scores);
|
||||||
|
})->throws(ScoreEntryException::class, 'Invalid Score Submission');
|
||||||
|
it('throws an exception if a submitted subscore exceeds the maximum allowed', function () {
|
||||||
|
loadSampleAudition();
|
||||||
|
$judge = User::factory()->create();
|
||||||
|
$entry = Entry::factory()->create(['audition_id' => 1000]);
|
||||||
|
Room::find(1000)->addJudge($judge);
|
||||||
|
$scores = [
|
||||||
|
1001 => 98,
|
||||||
|
1002 => 90,
|
||||||
|
1003 => 87,
|
||||||
|
1004 => 78,
|
||||||
|
1005 => 101,
|
||||||
|
];
|
||||||
|
enterScore($judge, $entry, $scores);
|
||||||
|
})->throws(ScoreEntryException::class, 'Supplied subscore exceeds maximum allowed');
|
||||||
|
it('removes an existing no_show flag from the entry if one exists', function () {
|
||||||
|
// Arrange
|
||||||
|
loadSampleAudition();
|
||||||
|
$judge = User::factory()->create();
|
||||||
|
$entry = Entry::factory()->create(['audition_id' => 1000]);
|
||||||
|
$entry->addFlag('no_show');
|
||||||
|
Room::find(1000)->addJudge($judge);
|
||||||
|
$scores = [
|
||||||
|
1001 => 98,
|
||||||
|
1002 => 90,
|
||||||
|
1003 => 87,
|
||||||
|
1004 => 78,
|
||||||
|
1005 => 98,
|
||||||
|
];
|
||||||
|
// Act
|
||||||
|
enterScore($judge, $entry, $scores);
|
||||||
|
// Assert
|
||||||
|
expect($entry->hasFlag('no_show'))->toBeFalse();
|
||||||
|
});
|
||||||
|
it('saves the score with a properly formatted subscore object', function () {
|
||||||
|
// Arrange
|
||||||
|
// Arrange
|
||||||
|
loadSampleAudition();
|
||||||
|
$judge = User::factory()->create();
|
||||||
|
$entry = Entry::factory()->create(['audition_id' => 1000]);
|
||||||
|
$entry->addFlag('no_show');
|
||||||
|
Room::find(1000)->addJudge($judge);
|
||||||
|
$scores = [
|
||||||
|
1001 => 98,
|
||||||
|
1002 => 90,
|
||||||
|
1003 => 87,
|
||||||
|
1004 => 78,
|
||||||
|
1005 => 98,
|
||||||
|
];
|
||||||
|
$desiredReturn = [
|
||||||
|
1001 => [
|
||||||
|
'score' => 98,
|
||||||
|
'subscore_id' => 1001,
|
||||||
|
'subscore_name' => 'Scale',
|
||||||
|
],
|
||||||
|
1002 => [
|
||||||
|
'score' => 90,
|
||||||
|
'subscore_id' => 1002,
|
||||||
|
'subscore_name' => 'Etude 1',
|
||||||
|
],
|
||||||
|
1003 => [
|
||||||
|
'score' => 87,
|
||||||
|
'subscore_id' => 1003,
|
||||||
|
'subscore_name' => 'Etude 2',
|
||||||
|
],
|
||||||
|
1004 => [
|
||||||
|
'score' => 78,
|
||||||
|
'subscore_id' => 1004,
|
||||||
|
'subscore_name' => 'Sight Reading',
|
||||||
|
],
|
||||||
|
1005 => [
|
||||||
|
'score' => 98,
|
||||||
|
'subscore_id' => 1005,
|
||||||
|
'subscore_name' => 'Tone',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
// Act
|
||||||
|
$newScore = enterScore($judge, $entry, $scores);
|
||||||
|
// Assert
|
||||||
|
$checkScoreSheet = ScoreSheet::find($newScore->id);
|
||||||
|
expect($checkScoreSheet->exists())->toBeTrue()
|
||||||
|
->and($checkScoreSheet->subscores)->toBe($desiredReturn);
|
||||||
|
});
|
||||||
|
it('throws an exception of the entry already has a score by the judge', function() {
|
||||||
|
// Arrange
|
||||||
|
loadSampleAudition();
|
||||||
|
$judge = User::factory()->create();
|
||||||
|
$entry = Entry::factory()->create(['audition_id' => 1000]);
|
||||||
|
$entry->addFlag('no_show');
|
||||||
|
Room::find(1000)->addJudge($judge);
|
||||||
|
$scores = [
|
||||||
|
1001 => 98,
|
||||||
|
1002 => 90,
|
||||||
|
1003 => 87,
|
||||||
|
1004 => 78,
|
||||||
|
1005 => 98,
|
||||||
|
];
|
||||||
|
// Act
|
||||||
|
enterScore($judge, $entry, $scores);
|
||||||
|
// Assert
|
||||||
|
enterScore($judge, $entry, $scores);
|
||||||
|
})->throws(ScoreEntryException::class, 'That judge has already entered scores for that entry');
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Models\Audition;
|
||||||
|
use App\Models\Entry;
|
||||||
|
use App\Models\Room;
|
||||||
|
use App\Models\ScoreSheet;
|
||||||
|
use App\Models\User;
|
||||||
|
use App\Services\ScoreService;
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
use function Pest\Laravel\artisan;
|
||||||
|
|
||||||
|
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();
|
||||||
|
$judges = User::factory()->count(2)->create();
|
||||||
|
$judges->each(fn ($judge) => $room->addJudge($judge));
|
||||||
|
$audition = Audition::factory()->create(['room_id' => $room->id]);
|
||||||
|
$entry = Entry::factory()->create(['audition_id' => $audition->id]);
|
||||||
|
expect($this->scoreService->isEntryFullyScored($entry))->toBeFalse();
|
||||||
|
ScoreSheet::create([
|
||||||
|
'user_id' => $judges->first()->id,
|
||||||
|
'entry_id' => $entry->id,
|
||||||
|
'subscores' => 7,
|
||||||
|
]);
|
||||||
|
expect($this->scoreService->isEntryFullyScored($entry))->toBeFalse();
|
||||||
|
ScoreSheet::create([
|
||||||
|
'user_id' => $judges->last()->id,
|
||||||
|
'entry_id' => $entry->id,
|
||||||
|
'subscores' => 7,
|
||||||
|
]);
|
||||||
|
expect($this->scoreService->isEntryFullyScored($entry))->toBeTrue();
|
||||||
|
});
|
||||||
|
|
@ -15,6 +15,7 @@ use App\Models\User;
|
||||||
use App\Settings;
|
use App\Settings;
|
||||||
use Illuminate\Foundation\Testing\TestCase;
|
use Illuminate\Foundation\Testing\TestCase;
|
||||||
use function Pest\Laravel\actingAs;
|
use function Pest\Laravel\actingAs;
|
||||||
|
use function Pest\Laravel\artisan;
|
||||||
|
|
||||||
uses(
|
uses(
|
||||||
Tests\TestCase::class,
|
Tests\TestCase::class,
|
||||||
|
|
@ -59,6 +60,10 @@ function actAsNormal()
|
||||||
{
|
{
|
||||||
actingAs(User::factory()->create());
|
actingAs(User::factory()->create());
|
||||||
}
|
}
|
||||||
|
function loadSampleAudition()
|
||||||
|
{
|
||||||
|
artisan('db:seed', ['--class' => 'AuditionWithScoringGuideAndRoom']);
|
||||||
|
}
|
||||||
|
|
||||||
uses()->beforeEach(function () {
|
uses()->beforeEach(function () {
|
||||||
Settings::set('auditionName', 'Somewhere Band Directors Association');
|
Settings::set('auditionName', 'Somewhere Band Directors Association');
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue