From 761f63aa55c561716f34af7601affd687ae1a402 Mon Sep 17 00:00:00 2001 From: Matt Young Date: Mon, 13 Oct 2025 22:13:11 -0500 Subject: [PATCH] Added CheckPrelimResult action to check if an entry passed it's prelim audition and make the appropriate flag on the entry. --- app/Actions/Tabulation/CheckPrelimResult.php | 61 ++++++ app/Enums/EntryFlags.php | 1 + app/Models/Entry.php | 1 + .../Tabulation/ChekPrelimResultTest.php | 174 ++++++++++++++++++ 4 files changed, 237 insertions(+) create mode 100644 app/Actions/Tabulation/CheckPrelimResult.php create mode 100644 tests/Feature/app/Actions/Tabulation/ChekPrelimResultTest.php diff --git a/app/Actions/Tabulation/CheckPrelimResult.php b/app/Actions/Tabulation/CheckPrelimResult.php new file mode 100644 index 0000000..3317196 --- /dev/null +++ b/app/Actions/Tabulation/CheckPrelimResult.php @@ -0,0 +1,61 @@ +removeFlag('passed_prelim'); + $entry->removeFlag('failed_prelim'); + } + + if (! $entry->exists) { + throw new AuditionAdminException('Entry does not exist'); + } + + if (! $entry->audition->prelimDefinition) { + throw new AuditionAdminException('Entry does not have a prelim'); + } + + if ($entry->hasFlag('failed_prelim') || $entry->hasFlag('passed_prelim')) { + return 'noChange'; + } + + if (! $entry->audition->prelimDefinition->room || $entry->audition->prelimDefinition->room->judges()->count() == 0) { + return 'noJudgesAssigned'; + } + + $scoresRequired = $entry->audition->prelimDefinition->room->judges()->count(); + $scoresAssigned = $entry->prelimScoreSheets()->count(); + if ($scoresAssigned < $scoresRequired) { + return 'missing'.$scoresRequired - $scoresAssigned.'scores'; + } + + $totalScore = 0; + foreach ($entry->prelimScoreSheets as $scoreSheet) { + $totalScore += $scoreSheet->total; + } + $averageScore = $totalScore / $scoresAssigned; + if ($averageScore >= $entry->audition->prelimDefinition->passing_score) { + $entry->addFlag('passed_prelim'); + + return 'markedPassed'; + } else { + $entry->addFlag('failed_prelim'); + + return 'markedFailed'; + } + } +} diff --git a/app/Enums/EntryFlags.php b/app/Enums/EntryFlags.php index bb87b0d..5129f35 100644 --- a/app/Enums/EntryFlags.php +++ b/app/Enums/EntryFlags.php @@ -8,5 +8,6 @@ enum EntryFlags: string case DECLINED = 'declined'; case NO_SHOW = 'no_show'; case FAILED_PRELIM = 'failed_prelim'; + case PASSED_PRELIM = 'passed_prelim'; case LATE_FEE_WAIVED = 'late_fee_waived'; } diff --git a/app/Models/Entry.php b/app/Models/Entry.php index ad57b42..6eb5ba5 100644 --- a/app/Models/Entry.php +++ b/app/Models/Entry.php @@ -177,6 +177,7 @@ class Entry extends Model 'declined' => EntryFlags::DECLINED, 'no_show' => EntryFlags::NO_SHOW, 'failed_prelim' => EntryFlags::FAILED_PRELIM, + 'passed_prelim' => EntryFlags::PASSED_PRELIM, 'late_fee_waived' => EntryFlags::LATE_FEE_WAIVED, }; $this->flags()->create(['flag_name' => $enum]); diff --git a/tests/Feature/app/Actions/Tabulation/ChekPrelimResultTest.php b/tests/Feature/app/Actions/Tabulation/ChekPrelimResultTest.php new file mode 100644 index 0000000..3594379 --- /dev/null +++ b/tests/Feature/app/Actions/Tabulation/ChekPrelimResultTest.php @@ -0,0 +1,174 @@ +tabulator = app(CheckPrelimResult::class); +}); + +it('throws an exception if the provided entry does not exist', function () { + $entry = Entry::factory()->make(); + ($this->tabulator)($entry); +})->throws(AuditionAdminException::class, 'Entry does not exist'); + +it('throws an exception if the entries audition does not have a prelim', function () { + $entry = Entry::factory()->create(); + ($this->tabulator)($entry); +})->throws(AuditionAdminException::class, 'Entry does not have a prelim'); + +it('does not change an existing decision unless forced', function () { + $entry = Entry::factory()->create(); + PrelimDefinition::create([ + 'audition_id' => $entry->audition_id, + 'passing_score' => 80, + ]); + $entry->addFlag('failed_prelim'); + $result = ($this->tabulator)($entry); + expect($result)->toBe('noChange'); + + $entry2 = Entry::factory()->create(['audition_id' => $entry->audition_id]); + $entry2->addFlag('passed_prelim'); + $result = ($this->tabulator)($entry); + expect($result)->toBe('noChange'); +}); + +it('doesnt make a decision if there are no judges assigned', function () { + $entry = Entry::factory()->create(); + PrelimDefinition::create([ + 'audition_id' => $entry->audition_id, + 'passing_score' => 80, + ]); + $result = ($this->tabulator)($entry); + expect($result)->toBe('noJudgesAssigned'); +}); + +it('doesnt make a decision if there are insufficient scores', function () { + $prelimRoom = Room::factory()->create(); + $judge1 = User::factory()->create(); + $judge2 = User::factory()->create(); + $prelimRoom->addJudge($judge1); + $prelimRoom->addJudge($judge2); + $prelimScoringGuide = ScoringGuide::factory()->create(); + $entry = Entry::factory()->create(); + PrelimDefinition::create([ + 'audition_id' => $entry->audition_id, + 'passing_score' => 80, + 'room_id' => $prelimRoom->id, + 'scoring_guide_id' => $prelimScoringGuide->id, + ]); + $result = ($this->tabulator)($entry); + expect($result)->toBe('missing2scores'); + app(EnterPrelimScore::class)($judge1, $entry, [ + 1 => 50, + 2 => 50, + 3 => 50, + 4 => 50, + 5 => 50, + ]); + $result = ($this->tabulator)($entry); + expect($result)->toBe('missing1scores'); +}); + +it('correctly identifies passing entries', function () { + $prelimRoom = Room::factory()->create(); + $judge1 = User::factory()->create(); + $judge2 = User::factory()->create(); + $prelimRoom->addJudge($judge1); + $prelimRoom->addJudge($judge2); + $prelimScoringGuide = ScoringGuide::factory()->create(); + $entry = Entry::factory()->create(); + PrelimDefinition::create([ + 'audition_id' => $entry->audition_id, + 'passing_score' => 80, + 'room_id' => $prelimRoom->id, + 'scoring_guide_id' => $prelimScoringGuide->id, + ]); + PrelimScoreSheet::create([ + 'entry_id' => $entry->id, + 'user_id' => $judge1->id, + 'subscores' => [], + 'total' => 85, + ]); + PrelimScoreSheet::create([ + 'entry_id' => $entry->id, + 'user_id' => $judge2->id, + 'subscores' => [], + 'total' => 75, + ]); + $result = ($this->tabulator)($entry); + expect($result)->toBe('markedPassed'); +}); + +it('correctly identifies failing entries', function () { + $prelimRoom = Room::factory()->create(); + $judge1 = User::factory()->create(); + $judge2 = User::factory()->create(); + $prelimRoom->addJudge($judge1); + $prelimRoom->addJudge($judge2); + $prelimScoringGuide = ScoringGuide::factory()->create(); + $entry = Entry::factory()->create(); + PrelimDefinition::create([ + 'audition_id' => $entry->audition_id, + 'passing_score' => 81, + 'room_id' => $prelimRoom->id, + 'scoring_guide_id' => $prelimScoringGuide->id, + ]); + PrelimScoreSheet::create([ + 'entry_id' => $entry->id, + 'user_id' => $judge1->id, + 'subscores' => [], + 'total' => 85, + ]); + PrelimScoreSheet::create([ + 'entry_id' => $entry->id, + 'user_id' => $judge2->id, + 'subscores' => [], + 'total' => 75, + ]); + $result = ($this->tabulator)($entry); + expect($result)->toBe('markedFailed'); +}); + +it('can force a recalculation', function () { + $prelimRoom = Room::factory()->create(); + $judge1 = User::factory()->create(); + $judge2 = User::factory()->create(); + $prelimRoom->addJudge($judge1); + $prelimRoom->addJudge($judge2); + $prelimScoringGuide = ScoringGuide::factory()->create(); + $entry = Entry::factory()->create(); + $entry->addFlag('failed_prelim'); + PrelimDefinition::create([ + 'audition_id' => $entry->audition_id, + 'passing_score' => 80, + 'room_id' => $prelimRoom->id, + 'scoring_guide_id' => $prelimScoringGuide->id, + ]); + PrelimScoreSheet::create([ + 'entry_id' => $entry->id, + 'user_id' => $judge1->id, + 'subscores' => [], + 'total' => 85, + ]); + PrelimScoreSheet::create([ + 'entry_id' => $entry->id, + 'user_id' => $judge2->id, + 'subscores' => [], + 'total' => 75, + ]); + $result = ($this->tabulator)($entry); + expect($result)->toBe('noChange'); + $result = ($this->tabulator)($entry, true); + expect($result)->toBe('markedPassed'); +});