From c8ce16e7e66e6cf9058f78d04b37002860fd63c1 Mon Sep 17 00:00:00 2001 From: Matt Young Date: Sun, 7 Jul 2024 16:15:48 -0500 Subject: [PATCH 1/6] Clean up old draw junk from Audition controller and model --- .../Controllers/Admin/AuditionController.php | 33 ------- app/Models/Audition.php | 86 ------------------- 2 files changed, 119 deletions(-) diff --git a/app/Http/Controllers/Admin/AuditionController.php b/app/Http/Controllers/Admin/AuditionController.php index 9025bf0..9bd8fec 100644 --- a/app/Http/Controllers/Admin/AuditionController.php +++ b/app/Http/Controllers/Admin/AuditionController.php @@ -181,37 +181,4 @@ class AuditionController extends Controller return to_route('admin.auditions.index')->with('success', 'Audition deleted successfully'); } - - public function prepareDraw() - { - if (! Auth::user()->is_admin) { - abort(403); - } - $allAuditions = Audition::with('entries')->orderBy('score_order')->get(); - $nodraw_auditions = $allAuditions->filter(function ($audition) { - return $audition->has_no_draw(); - }); - $drawn_auditions = $allAuditions->filter(function ($audition) { - return $audition->has_complete_draw(); - }); - $partial_draw_auditions = $allAuditions->filter(function ($audition) { - return $audition->has_partial_draw(); - }); - - return view('admin.entries.prepare_draw', - compact('nodraw_auditions', 'drawn_auditions', 'partial_draw_auditions')); - } - - public function runDraw(Request $request) - { - if (! Auth::user()->is_admin) { - abort(403); - } - $draw_auditions = Audition::with('entries')->find(array_keys($request->input('auditions'))); - foreach ($draw_auditions as $audition) { - $audition->runDraw(); - } - - return redirect(' / admin / auditions / run_draw'); - } } diff --git a/app/Models/Audition.php b/app/Models/Audition.php index 175c053..d33b721 100644 --- a/app/Models/Audition.php +++ b/app/Models/Audition.php @@ -16,14 +16,6 @@ class Audition extends Model protected $guarded = []; - protected $rankedEntries = null; - - protected static $completeAuditions = null; - - protected $fully_scored; // Set by TabulationService - - protected $scored_entries_count; //Set by TabulationService - public function event(): BelongsTo { return $this->belongsTo(Event::class); @@ -49,84 +41,6 @@ class Audition extends Model return '$'.number_format($this->entry_fee / 100, 2); } - public function has_no_draw(): bool - { - // return true if all of my entries have a null draw_number - //return $this->entries->every(fn($entry) => is_null($entry->draw_number)); - // return $this->entries()->whereNotNull('draw_number')->doesntExist(); - if ($this->entries->count() == 0) { - return false; - } - if ($this->relationLoaded('entries')) { - return $this->entries->every(function ($entry) { - return is_null($entry->draw_number); - }); - } else { - return $this->entries()->whereNotNull('draw_number')->doesntExist(); - } - - } - - public function has_complete_draw(): bool - { - // return true if all of my entries have a draw_number - // return $this->entries->every(fn($entry) => !is_null($entry->draw_number)); - // return $this->entries()->whereNull('draw_number')->doesntExist(); - if ($this->entries->count() == 0) { - return false; - } - if ($this->relationLoaded('entries')) { - return $this->entries->every(function ($entry) { - return ! is_null($entry->draw_number); - }); - } else { - return $this->entries()->whereNull('draw_number')->doesntExist(); - } - } - - public function has_partial_draw(): bool - { - if ($this->entries->count() == 0) { - return false; - } - // return true I have at least one entry with a null draw number AND at least one entry with a non-null draw number - //return $this->entries->contains(fn($entry) => is_null($entry->draw_number)) && $this->entries->contains(fn($entry) => !is_null($entry->draw_number)); - - //$hasNull = $this->entries()->whereNull('draw_number')->exists(); - //$hasNotNull = $this->entries()->whereNotNull('draw_number')->exists(); - //return $hasNull && $hasNotNull; - - if ($this->relationLoaded('entries')) { - $hasNull = $this->entries->contains(function ($entry) { - return is_null($entry->draw_number); - }); - - $hasNotNull = $this->entries->contains(function ($entry) { - return ! is_null($entry->draw_number); - }); - - return $hasNull && $hasNotNull; - } else { - $hasNull = $this->entries()->whereNull('draw_number')->exists(); - $hasNotNull = $this->entries()->whereNotNull('draw_number')->exists(); - - return $hasNull && $hasNotNull; - } - } - - public function runDraw(): null - { - $entries = $this->entries->shuffle(); - - foreach ($entries as $index => $entry) { - $entry->update(['draw_number' => $index + 1]); - $entry->save(); - } - - return null; - // TODO move all draw functions to a DrawService - } - /** * @return BelongsToMany|User[] */ -- 2.39.5 From 87f091ee5b80e8b39d684b9f4bf87331b232c6d8 Mon Sep 17 00:00:00 2001 From: Matt Young Date: Sun, 7 Jul 2024 17:18:03 -0500 Subject: [PATCH 2/6] Allow for reuse of entry select form to choose entry by ID --- .../Tabulation/ScoreController.php | 37 ++++++++++++------- .../views/tabulation/choose_entry.blade.php | 9 ++++- 2 files changed, 31 insertions(+), 15 deletions(-) diff --git a/app/Http/Controllers/Tabulation/ScoreController.php b/app/Http/Controllers/Tabulation/ScoreController.php index 5504d77..5f2fe69 100644 --- a/app/Http/Controllers/Tabulation/ScoreController.php +++ b/app/Http/Controllers/Tabulation/ScoreController.php @@ -7,36 +7,44 @@ use App\Models\Entry; use App\Models\ScoreSheet; use Illuminate\Http\Request; use Illuminate\Support\Facades\Session; + class ScoreController extends Controller { public function chooseEntry(Request $request) { - return view('tabulation.choose_entry'); + $method = 'GET'; + $formRoute = 'scores.entryScoreSheet'; + + return view('tabulation.choose_entry', compact('method', 'formRoute')); } - public function destroyScore(ScoreSheet $score) { + public function destroyScore(ScoreSheet $score) + { $score->delete(); - return redirect()->back()->with('success','Score Deleted'); + + return redirect()->back()->with('success', 'Score Deleted'); } public function entryScoreSheet(Request $request) { $existing_sheets = []; - $entry = Entry::with(['student','audition.room.judges'])->find($request->input('entry_id')); + $entry = Entry::with(['student', 'audition.room.judges'])->find($request->input('entry_id')); $judges = $entry->audition->room->judges; foreach ($judges as $judge) { - $scoreSheet = ScoreSheet::where('entry_id',$entry->id)->where('user_id',$judge->id)->first(); + $scoreSheet = ScoreSheet::where('entry_id', $entry->id)->where('user_id', $judge->id)->first(); if ($scoreSheet) { - Session::flash('caution','Scores exist for this entry. Now editing existing scores'); + Session::flash('caution', 'Scores exist for this entry. Now editing existing scores'); $existing_sheets[$judge->id] = $scoreSheet; } } $scoring_guide = $entry->audition->scoringGuide; $subscores = $entry->audition->scoringGuide->subscores->sortBy('display_order'); - if (!$entry) { - return redirect()->route('tabulation.chooseEntry')->with('error','Entry not found'); + if (! $entry) { + return redirect()->route('tabulation.chooseEntry')->with('error', 'Entry not found'); } - return view('tabulation.entry_score_sheet', compact('entry','judges','scoring_guide','subscores','existing_sheets')); + + return view('tabulation.entry_score_sheet', + compact('entry', 'judges', 'scoring_guide', 'subscores', 'existing_sheets')); } public function saveEntryScoreSheet(Request $request, Entry $entry) @@ -52,16 +60,17 @@ class ScoreController extends Controller $scoreValidation = $scoringGuide->validateScores($request->input('judge'.$judge->id)); if ($scoreValidation != 'success') { - return redirect(url()->previous())->with('error', $judge->full_name() . ': ' . $scoreValidation)->with('oldScores',$request->all()); + return redirect(url()->previous())->with('error', + $judge->full_name().': '.$scoreValidation)->with('oldScores', $request->all()); } $scoreSubmission = $request->input('judge'.$judge->id); $scoresToSave = []; foreach ($subscores as $subscore) { $scoresToSave[$subscore->id] = [ - 'subscore_id'=>$subscore->id, + 'subscore_id' => $subscore->id, 'subscore_name' => $subscore->name, - 'score' => intval($scoreSubmission[$subscore->id]) + 'score' => intval($scoreSubmission[$subscore->id]), ]; } $preparedScoreSheets[$judge->id]['scores'] = $scoresToSave; @@ -72,7 +81,7 @@ class ScoreController extends Controller ['subscores' => $sheet['scores']] ); } - return redirect()->route('scores.chooseEntry')->with('success',count($preparedScoreSheets) . " Scores saved"); - } + return redirect()->route('scores.chooseEntry')->with('success', count($preparedScoreSheets).' Scores saved'); + } } diff --git a/resources/views/tabulation/choose_entry.blade.php b/resources/views/tabulation/choose_entry.blade.php index c7a88e9..65b8585 100644 --- a/resources/views/tabulation/choose_entry.blade.php +++ b/resources/views/tabulation/choose_entry.blade.php @@ -1,9 +1,16 @@ +@php + /** +* @var string $method Method for the select form + * @var string $formRoute Route for the form action. Should be a route name + */ +@endphp + Choose Entry Choose Entry
- + Select -- 2.39.5 From 0f18a7c62ed573f581918cd6176d9d4694fdaca9 Mon Sep 17 00:00:00 2001 From: Matt Young Date: Sun, 7 Jul 2024 19:33:52 -0500 Subject: [PATCH 3/6] Select audition and generate forms for setting and removing no_show flags --- .runParalellTestsAll/tests - paralell.run.xml | 11 ++++ .../Tabulation/EntryFlagController.php | 41 ++++++++++++ app/Http/Middleware/CheckIfCanTab.php | 2 +- .../views/tabulation/choose_entry.blade.php | 2 +- .../tabulation/no_show_confirm.blade.php | 19 ++++++ routes/tabulation.php | 7 ++ .../Pages/Tabulation/noShowConfirmTest.php | 66 +++++++++++++++++++ .../Pages/Tabulation/noShowSelectTest.php | 41 ++++++++++++ 8 files changed, 187 insertions(+), 2 deletions(-) create mode 100644 .runParalellTestsAll/tests - paralell.run.xml create mode 100644 app/Http/Controllers/Tabulation/EntryFlagController.php create mode 100644 resources/views/tabulation/no_show_confirm.blade.php create mode 100644 tests/Feature/Pages/Tabulation/noShowConfirmTest.php create mode 100644 tests/Feature/Pages/Tabulation/noShowSelectTest.php diff --git a/.runParalellTestsAll/tests - paralell.run.xml b/.runParalellTestsAll/tests - paralell.run.xml new file mode 100644 index 0000000..06e48dc --- /dev/null +++ b/.runParalellTestsAll/tests - paralell.run.xml @@ -0,0 +1,11 @@ + + + + + + + \ No newline at end of file diff --git a/app/Http/Controllers/Tabulation/EntryFlagController.php b/app/Http/Controllers/Tabulation/EntryFlagController.php new file mode 100644 index 0000000..0c3ef77 --- /dev/null +++ b/app/Http/Controllers/Tabulation/EntryFlagController.php @@ -0,0 +1,41 @@ +validate([ + 'entry_id' => 'required|exists:entries,id', + ]); + $entry = Entry::with('flags')->find($validData['entry_id']); + + if ($entry->hasFlag('no_show')) { + $formId = 'no-show-cancellation-form'; + $buttonName = 'Remove No Show'; + $submitRouteName = 'entry-flags.enterNoShow'; + $cardHeading = 'Undo No Show'; + } else { + $formId = 'no-show-confirmation-form'; + $buttonName = 'Confirm No Show'; + $submitRouteName = 'entry-flags.enterNoShow'; + $cardHeading = 'Confirm No Show'; + } + + return view('tabulation.no_show_confirm', + compact('entry', 'formId', 'buttonName', 'submitRouteName', 'cardHeading')); + } +} diff --git a/app/Http/Middleware/CheckIfCanTab.php b/app/Http/Middleware/CheckIfCanTab.php index 7d28c2c..67b0513 100644 --- a/app/Http/Middleware/CheckIfCanTab.php +++ b/app/Http/Middleware/CheckIfCanTab.php @@ -25,7 +25,7 @@ class CheckIfCanTab return $next($request); } - return redirect('/')->with('error', 'You do not have access to score tabulation.'); + return redirect('dashboard')->with('error', 'You are not authorized to perform this action'); } } diff --git a/resources/views/tabulation/choose_entry.blade.php b/resources/views/tabulation/choose_entry.blade.php index 65b8585..2f4c01a 100644 --- a/resources/views/tabulation/choose_entry.blade.php +++ b/resources/views/tabulation/choose_entry.blade.php @@ -10,7 +10,7 @@ Choose Entry
- + Select diff --git a/resources/views/tabulation/no_show_confirm.blade.php b/resources/views/tabulation/no_show_confirm.blade.php new file mode 100644 index 0000000..95becb9 --- /dev/null +++ b/resources/views/tabulation/no_show_confirm.blade.php @@ -0,0 +1,19 @@ + + + {{ $cardHeading }} + + + {{ $entry->student->full_name() }} + {{$entry->school->name}} + + + {{ $entry->audition->name }} #{{ $entry->draw_number ?? ' no draw number' }} + + + + + {{ $buttonName }} + + + + diff --git a/routes/tabulation.php b/routes/tabulation.php index b2523cf..c9d6e18 100644 --- a/routes/tabulation.php +++ b/routes/tabulation.php @@ -14,6 +14,13 @@ Route::middleware(['auth', 'verified', CheckIfCanTab::class])->group(function () Route::delete('/{score}', [\App\Http\Controllers\Tabulation\ScoreController::class, 'destroyScore'])->name('scores.destroy'); }); + // Entry Flagging + Route::prefix('entry-flags/')->controller(\App\Http\Controllers\Tabulation\EntryFlagController::class)->group(function () { + Route::get('/choose_no_show', 'noShowSelect')->name('entry-flags.noShowSelect'); + Route::get('/propose-no-show', 'noShowConfirm')->name('entry-flags.confirmNoShow'); + Route::post('/no-show/{entry}', 'enterNoShow')->name('entry-flags.enterNoShow'); + }); + // Generic Tabulation Routes Route::prefix('tabulation/')->controller(\App\Http\Controllers\Tabulation\TabulationController::class)->group(function () { Route::get('/status', 'status')->name('tabulation.status'); diff --git a/tests/Feature/Pages/Tabulation/noShowConfirmTest.php b/tests/Feature/Pages/Tabulation/noShowConfirmTest.php new file mode 100644 index 0000000..4bfb895 --- /dev/null +++ b/tests/Feature/Pages/Tabulation/noShowConfirmTest.php @@ -0,0 +1,66 @@ +create(); + get(route('entry-flags.confirmNoShow', ['entry_id' => $entry->id])) + ->assertRedirect(route('home')); + actAsNormal(); + get(route('entry-flags.confirmNoShow', ['entry_id' => $entry->id])) + ->assertRedirect(route('dashboard')) + ->assertSessionHas('error', 'You are not authorized to perform this action'); + actAsTab(); + get(route('entry-flags.confirmNoShow', ['entry_id' => $entry->id])) + ->assertOk(); + actAsAdmin(); + get(route('entry-flags.confirmNoShow', ['entry_id' => $entry->id])) + ->assertOk(); +}); +it('has information for the requested entry', function () { + // Arrange + $entry = Entry::factory()->create(); + // Act & Assert + actAsTab(); + get(route('entry-flags.confirmNoShow', ['entry_id' => $entry->id])) + ->assertOk() + ->assertSee($entry->student->full_name()) + ->assertSee($entry->audition->name) + ->assertSee($entry->student->school->name) + ->assertSee($entry->draw_number); +}); +it('posts to entry-flags.enterNoShow and has a submit button', function () { + // Arrange + $entry = Entry::factory()->create(); + // Act & Assert + actAsTab(); + get(route('entry-flags.confirmNoShow', ['entry_id' => $entry->id])) + ->assertOk() + ->assertFormExists('#no-show-confirmation-form', function (AssertForm $form) use ($entry) { + $form->hasMethod('POST') + ->hasAction(route('entry-flags.enterNoShow', ['entry' => $entry->id])) + ->contains('button', ['type' => 'submit']) + ->hasCSRF(); + }); +}); +it('posts to entry-flags.undoNoShow and has a remove button if the entry is already a noshow', function () { + // Arrange + $entry = Entry::factory()->create(); + $entry->addFlag('no_show'); + // Act & Assert + actAsTab(); + get(route('entry-flags.confirmNoShow', ['entry_id' => $entry->id])) + ->assertOk() + ->assertFormExists('#no-show-cancellation-form', function (AssertForm $form) use ($entry) { + $form->hasMethod('POST') + ->hasAction(route('entry-flags.undoNoShow', ['entry' => $entry->id])) + ->contains('button', ['type' => 'submit']) + ->hasCSRF(); + }); +}); diff --git a/tests/Feature/Pages/Tabulation/noShowSelectTest.php b/tests/Feature/Pages/Tabulation/noShowSelectTest.php new file mode 100644 index 0000000..e898ad8 --- /dev/null +++ b/tests/Feature/Pages/Tabulation/noShowSelectTest.php @@ -0,0 +1,41 @@ +assertRedirect(route('home')); + actAsAdmin(); + get(route('entry-flags.noShowSelect')) + ->assertOk(); + actAsTab(); + get(route('entry-flags.noShowSelect')) + ->assertOk(); + actAsNormal(); + get(route('entry-flags.noShowSelect')) + ->assertRedirect(route('dashboard')) + ->assertSessionHas('error', 'You are not authorized to perform this action'); +}); +it('has an input for entry_id', function () { + actAsAdmin(); + get(route('entry-flags.noShowSelect')) + ->assertOk() + ->assertElementExists('#entry_id', function (AssertElement $element) { + $element->is('input'); + }); +}); +it('submits to entry-flags.confirmNoShow', function () { + actAsAdmin(); + get(route('entry-flags.noShowSelect')) + ->assertOk() + ->assertFormExists('#entry-select-form', function (AssertForm $form) { + $form->hasMethod('GET') + ->hasAction(route('entry-flags.confirmNoShow')); + }); +}); -- 2.39.5 From 03a2dd2e1d318fb80282aaa4893f6b009f872e34 Mon Sep 17 00:00:00 2001 From: Matt Young Date: Sun, 7 Jul 2024 22:12:36 -0500 Subject: [PATCH 4/6] Require confirmation for making a scored entry a no-show --- .../Tabulation/EntryFlagController.php | 35 +++++++++++++++---- database/seeders/ScoreAllAuditions.php | 13 ++++--- .../components/form/toggle-checkbox.blade.php | 2 +- .../tabulation/no_show_confirm.blade.php | 33 ++++++++++++++--- .../no_show_confirm_score_switch.blade.php | 6 ++++ routes/tabulation.php | 1 + .../Pages/Tabulation/noShowConfirmTest.php | 26 ++++++++++++++ 7 files changed, 97 insertions(+), 19 deletions(-) create mode 100644 resources/views/tabulation/no_show_confirm_score_switch.blade.php diff --git a/app/Http/Controllers/Tabulation/EntryFlagController.php b/app/Http/Controllers/Tabulation/EntryFlagController.php index 0c3ef77..14189e2 100644 --- a/app/Http/Controllers/Tabulation/EntryFlagController.php +++ b/app/Http/Controllers/Tabulation/EntryFlagController.php @@ -21,21 +21,42 @@ class EntryFlagController extends Controller $validData = $request->validate([ 'entry_id' => 'required|exists:entries,id', ]); - $entry = Entry::with('flags')->find($validData['entry_id']); - + $entry = Entry::with('flags')->withCount('scoreSheets')->find($validData['entry_id']); if ($entry->hasFlag('no_show')) { $formId = 'no-show-cancellation-form'; $buttonName = 'Remove No Show'; - $submitRouteName = 'entry-flags.enterNoShow'; - $cardHeading = 'Undo No Show'; + $submitRouteName = 'entry-flags.undoNoShow'; + $cardHeading = 'Undo No-Show'; + $method = 'DELETE'; } else { $formId = 'no-show-confirmation-form'; $buttonName = 'Confirm No Show'; $submitRouteName = 'entry-flags.enterNoShow'; - $cardHeading = 'Confirm No Show'; + $cardHeading = 'Confirm No-Show'; + $method = 'POST'; + } + $scores = []; + if($entry->score_sheets_count > 0) { + $scores = $entry->scoreSheets; + $scores->load('judge'); } - return view('tabulation.no_show_confirm', - compact('entry', 'formId', 'buttonName', 'submitRouteName', 'cardHeading')); + compact('entry', + 'formId', + 'buttonName', + 'submitRouteName', + 'cardHeading', + 'method', + 'scores')); + } + + public function enterNoShow(Entry $entry) + { + // + } + + public function undoNoShow(Entry $entry) + { + // } } diff --git a/database/seeders/ScoreAllAuditions.php b/database/seeders/ScoreAllAuditions.php index 230199d..9a9f569 100644 --- a/database/seeders/ScoreAllAuditions.php +++ b/database/seeders/ScoreAllAuditions.php @@ -4,7 +4,6 @@ namespace Database\Seeders; use App\Models\ScoreSheet; use App\Models\User; -use Illuminate\Database\Console\Seeds\WithoutModelEvents; use Illuminate\Database\Seeder; class ScoreAllAuditions extends Seeder @@ -16,23 +15,23 @@ class ScoreAllAuditions extends Seeder { $judges = User::all(); foreach ($judges as $judge) { - foreach( $judge->rooms as $room) { - foreach ($room->auditions as $audition){ + foreach ($judge->rooms as $room) { + foreach ($room->auditions as $audition) { $scoringGuide = $audition->scoringGuide; $subscores = $scoringGuide->subscores; - foreach ($audition->entries as $entry){ + foreach ($audition->entries as $entry) { $scoreArray = []; foreach ($subscores as $subscore) { $scoreArray[$subscore->id] = [ - 'score' => mt_rand(0,100), + 'score' => mt_rand(0, 100), 'subscore_id' => $subscore->id, - 'subscore_name' => $subscore->name + 'subscore_name' => $subscore->name, ]; } ScoreSheet::create([ 'user_id' => $judge->id, 'entry_id' => $entry->id, - 'subscores' => $scoreArray + 'subscores' => $scoreArray, ]); } } diff --git a/resources/views/components/form/toggle-checkbox.blade.php b/resources/views/components/form/toggle-checkbox.blade.php index 3d6086a..1e88ebf 100644 --- a/resources/views/components/form/toggle-checkbox.blade.php +++ b/resources/views/components/form/toggle-checkbox.blade.php @@ -2,7 +2,7 @@
@error($name) diff --git a/resources/views/tabulation/no_show_confirm.blade.php b/resources/views/tabulation/no_show_confirm.blade.php index 95becb9..0608e60 100644 --- a/resources/views/tabulation/no_show_confirm.blade.php +++ b/resources/views/tabulation/no_show_confirm.blade.php @@ -1,5 +1,30 @@ - - + + @if($scores) + + + WARNING: Entry has existing scores + +
+ @foreach($scores as $score) +
+
{{ $score->judge->full_name() }}
+
    + @foreach($score->subscores as $subscore) +
    + {{ $subscore['subscore_name'] }} + {{ $subscore['score'] }} +
    + @endforeach +
+
+ @endforeach +
+
+ I understand that marking this entry as a no-show will delete existing scores. The scores cannot be recovered. +
+
+ @endif + {{ $cardHeading }} @@ -11,8 +36,8 @@ - - {{ $buttonName }} + + {{ $buttonName }} diff --git a/resources/views/tabulation/no_show_confirm_score_switch.blade.php b/resources/views/tabulation/no_show_confirm_score_switch.blade.php new file mode 100644 index 0000000..e2d6a3d --- /dev/null +++ b/resources/views/tabulation/no_show_confirm_score_switch.blade.php @@ -0,0 +1,6 @@ +
+ diff --git a/routes/tabulation.php b/routes/tabulation.php index c9d6e18..956ddde 100644 --- a/routes/tabulation.php +++ b/routes/tabulation.php @@ -19,6 +19,7 @@ Route::middleware(['auth', 'verified', CheckIfCanTab::class])->group(function () Route::get('/choose_no_show', 'noShowSelect')->name('entry-flags.noShowSelect'); Route::get('/propose-no-show', 'noShowConfirm')->name('entry-flags.confirmNoShow'); Route::post('/no-show/{entry}', 'enterNoShow')->name('entry-flags.enterNoShow'); + Route::delete('/no-show/{entry}', 'undoNoShow')->name('entry-flags.undoNoShow'); }); // Generic Tabulation Routes diff --git a/tests/Feature/Pages/Tabulation/noShowConfirmTest.php b/tests/Feature/Pages/Tabulation/noShowConfirmTest.php index 4bfb895..4e09a6f 100644 --- a/tests/Feature/Pages/Tabulation/noShowConfirmTest.php +++ b/tests/Feature/Pages/Tabulation/noShowConfirmTest.php @@ -1,7 +1,13 @@ hasCSRF(); }); }); +it('shows a box with scores if the entry is scored', function () { + // Arrange + $judges = User::factory()->count(3)->create(); + $room = Room::factory()->create(); + $judges->each(fn ($judge) => $room->addJudge($judge->id)); + $scoringGuide = ScoringGuide::factory()->create(); + SubscoreDefinition::factory()->count(5)->create(['scoring_guide_id' => $scoringGuide->id]); + $audition = Audition::factory()->create(['room_id' => $room->id, 'scoring_guide_id' => $scoringGuide->id]); + $entry = Entry::factory()->create(['audition_id' => $audition->id]); + // Run the ScoreAllAuditions seeder + Artisan::call('db:seed', ['--class' => 'ScoreAllAuditions']); + // Act & Assert + actAsTab(); + $response = get(route('entry-flags.confirmNoShow', ['entry_id' => $entry->id])); + $response->assertOk() + ->assertSee('WARNING') + ->assertSee('existing scores'); + $judges->each(fn ($judge) => $response->assertSee($judge->full_name())); + +}); -- 2.39.5 From f4f1d6cff77ab65bf46a282488c390b6f2bd1af2 Mon Sep 17 00:00:00 2001 From: Matt Young Date: Sun, 7 Jul 2024 22:48:07 -0500 Subject: [PATCH 5/6] Recording no shows works --- .../Tabulation/EntryFlagController.php | 33 +++++++++- .../Pages/Tabulation/noShowConfirmTest.php | 21 +++++-- .../Tabulation/noShowEnterAndCancelTest.php | 60 +++++++++++++++++++ 3 files changed, 108 insertions(+), 6 deletions(-) create mode 100644 tests/Feature/Pages/Tabulation/noShowEnterAndCancelTest.php diff --git a/app/Http/Controllers/Tabulation/EntryFlagController.php b/app/Http/Controllers/Tabulation/EntryFlagController.php index 14189e2..3a9b1b1 100644 --- a/app/Http/Controllers/Tabulation/EntryFlagController.php +++ b/app/Http/Controllers/Tabulation/EntryFlagController.php @@ -5,6 +5,9 @@ namespace App\Http\Controllers\Tabulation; use App\Http\Controllers\Controller; use App\Models\Entry; use Illuminate\Http\Request; +use Illuminate\Support\Facades\DB; + +use function to_route; class EntryFlagController extends Controller { @@ -22,6 +25,17 @@ class EntryFlagController extends Controller 'entry_id' => 'required|exists:entries,id', ]); $entry = Entry::with('flags')->withCount('scoreSheets')->find($validData['entry_id']); + + // If any results are published, get gone + if ($entry->audition->hasFlag('seats_published')) { + return to_route('entry-flags.noShowSelect')->with('error', + 'Cannot enter a no-show for an entry in an audition where seats are published'); + } + if ($entry->audition->hasFlag('advance_published')) { + return to_route('entry-flags.noShowSelect')->with('error', + 'Cannot enter a no-show for an entry in an audition where advancement is published'); + } + if ($entry->hasFlag('no_show')) { $formId = 'no-show-cancellation-form'; $buttonName = 'Remove No Show'; @@ -36,10 +50,11 @@ class EntryFlagController extends Controller $method = 'POST'; } $scores = []; - if($entry->score_sheets_count > 0) { + if ($entry->score_sheets_count > 0) { $scores = $entry->scoreSheets; $scores->load('judge'); } + return view('tabulation.no_show_confirm', compact('entry', 'formId', @@ -52,7 +67,21 @@ class EntryFlagController extends Controller public function enterNoShow(Entry $entry) { - // + if ($entry->audition->hasFlag('seats_published')) { + return to_route('entry-flags.noShowSelect')->with('error', + 'Cannot enter a no-show for an entry in an audition where seats are published'); + } + if ($entry->audition->hasFlag('advance_published')) { + return to_route('entry-flags.noShowSelect')->with('error', + 'Cannot enter a no-show for an entry in an audition where advancement is published'); + } + DB::table('score_sheets')->where('entry_id', $entry->id)->delete(); + + $entry->addFlag('no_show'); + + $msg = 'No Show has been entered for '.$entry->audition->name.' #'.$entry->draw_number.' (ID: '.$entry->id.').'; + + return to_route('entry-flags.noShowSelect')->with('success', $msg); } public function undoNoShow(Entry $entry) diff --git a/tests/Feature/Pages/Tabulation/noShowConfirmTest.php b/tests/Feature/Pages/Tabulation/noShowConfirmTest.php index 4e09a6f..011f22f 100644 --- a/tests/Feature/Pages/Tabulation/noShowConfirmTest.php +++ b/tests/Feature/Pages/Tabulation/noShowConfirmTest.php @@ -29,6 +29,19 @@ it('only allows an admin or tab user to confirm a no-show', function () { get(route('entry-flags.confirmNoShow', ['entry_id' => $entry->id])) ->assertOk(); }); +it('will not work with an entry if it is in a published audition', function () { + $entry1 = Entry::factory()->create(); + $entry2 = Entry::factory()->create(); + $entry1->audition->addFlag('seats_published'); + $entry2->audition->addFlag('advance_published'); + actAsTab(); + get(route('entry-flags.confirmNoShow', ['entry_id' => $entry1->id])) + ->assertRedirect(route('entry-flags.noShowSelect')) + ->assertSessionHas('error', 'Cannot enter a no-show for an entry in an audition where seats are published'); + get(route('entry-flags.confirmNoShow', ['entry_id' => $entry2->id])) + ->assertRedirect(route('entry-flags.noShowSelect')) + ->assertSessionHas('error', 'Cannot enter a no-show for an entry in an audition where advancement is published'); +}); it('has information for the requested entry', function () { // Arrange $entry = Entry::factory()->create(); @@ -73,12 +86,12 @@ it('posts to entry-flags.undoNoShow and has a remove button if the entry is alre it('shows a box with scores if the entry is scored', function () { // Arrange $judges = User::factory()->count(3)->create(); - $room = Room::factory()->create(); - $judges->each(fn ($judge) => $room->addJudge($judge->id)); + $room = Room::factory()->create(); + $judges->each(fn($judge) => $room->addJudge($judge->id)); $scoringGuide = ScoringGuide::factory()->create(); SubscoreDefinition::factory()->count(5)->create(['scoring_guide_id' => $scoringGuide->id]); $audition = Audition::factory()->create(['room_id' => $room->id, 'scoring_guide_id' => $scoringGuide->id]); - $entry = Entry::factory()->create(['audition_id' => $audition->id]); + $entry = Entry::factory()->create(['audition_id' => $audition->id]); // Run the ScoreAllAuditions seeder Artisan::call('db:seed', ['--class' => 'ScoreAllAuditions']); // Act & Assert @@ -87,6 +100,6 @@ it('shows a box with scores if the entry is scored', function () { $response->assertOk() ->assertSee('WARNING') ->assertSee('existing scores'); - $judges->each(fn ($judge) => $response->assertSee($judge->full_name())); + $judges->each(fn($judge) => $response->assertSee($judge->full_name())); }); diff --git a/tests/Feature/Pages/Tabulation/noShowEnterAndCancelTest.php b/tests/Feature/Pages/Tabulation/noShowEnterAndCancelTest.php new file mode 100644 index 0000000..51d3bac --- /dev/null +++ b/tests/Feature/Pages/Tabulation/noShowEnterAndCancelTest.php @@ -0,0 +1,60 @@ +create(); + post(route('entry-flags.enterNoShow', $entry)) + ->assertRedirect(route('home')); + actAsAdmin(); + post(route('entry-flags.enterNoShow', $entry)) + ->assertSessionHasNoErrors() + ->assertSessionHas('success', + 'No Show has been entered for '.$entry->audition->name.' #'.$entry->draw_number.' (ID: '.$entry->id.').') + ->assertRedirect(route('entry-flags.noShowSelect')); + actAsTab(); + post(route('entry-flags.enterNoShow', $entry)) + ->assertSessionHasNoErrors() + ->assertSessionHas('success', + 'No Show has been entered for '.$entry->audition->name.' #'.$entry->draw_number.' (ID: '.$entry->id.').') + ->assertRedirect(route('entry-flags.noShowSelect')); +}); +it('will not record an entry in a published audition as a no show', function () { + $entry1 = Entry::factory()->create(); + $entry2 = Entry::factory()->create(); + $entry1->audition->addFlag('seats_published'); + $entry2->audition->addFlag('advance_published'); + actAsAdmin(); + post(route('entry-flags.enterNoShow', $entry1)) + ->assertRedirect(route('entry-flags.noShowSelect')) + ->assertSessionHas('error', 'Cannot enter a no-show for an entry in an audition where seats are published'); + post(route('entry-flags.enterNoShow', $entry2)) + ->assertRedirect(route('entry-flags.noShowSelect')) + ->assertSessionHas('error', + 'Cannot enter a no-show for an entry in an audition where advancement is published'); +}); +it('deletes all scores for an entry when a no-show is entered', function () { + $entry = Entry::factory()->create(); + $judges = User::factory()->count(3)->create(); + $entry->scoreSheets()->create(['user_id' => $judges[0]->id, 'subscores' => 10]); + $entry->scoreSheets()->create(['user_id' => $judges[1]->id, 'subscores' => 10]); + $entry->scoreSheets()->create(['user_id' => $judges[2]->id, 'subscores' => 10]); + expect($entry->scoreSheets()->count())->toBe(3); + actAsAdmin(); + post(route('entry-flags.enterNoShow', $entry)); + expect($entry->scoreSheets()->count())->toBe(0); +}); +it('adds a no_show flag to the entry', function () { + // Arrange + $entry = Entry::factory()->create(); + // Act & Assert + actAsAdmin(); + post(route('entry-flags.enterNoShow', $entry)); + expect(Entry::find($entry->id)->hasFlag('no_show'))->toBeTrue(); +}); -- 2.39.5 From e3a0627ef24de5fd82385c84aa0ee0a5edc4ea64 Mon Sep 17 00:00:00 2001 From: Matt Young Date: Sun, 7 Jul 2024 23:31:53 -0500 Subject: [PATCH 6/6] Removing no-show flags working --- .../Controllers/Admin/StudentController.php | 10 ++-- .../Tabulation/EntryFlagController.php | 16 +++++- database/factories/EnsembleFactory.php | 8 ++- ...rAndCancelTest.php => noShowEnterTest.php} | 2 + .../Pages/Tabulation/noShowUndoTest.php | 55 +++++++++++++++++++ 5 files changed, 80 insertions(+), 11 deletions(-) rename tests/Feature/Pages/Tabulation/{noShowEnterAndCancelTest.php => noShowEnterTest.php} (95%) create mode 100644 tests/Feature/Pages/Tabulation/noShowUndoTest.php diff --git a/app/Http/Controllers/Admin/StudentController.php b/app/Http/Controllers/Admin/StudentController.php index 24d29a5..9916f64 100644 --- a/app/Http/Controllers/Admin/StudentController.php +++ b/app/Http/Controllers/Admin/StudentController.php @@ -6,10 +6,8 @@ use App\Http\Controllers\Controller; use App\Models\Audition; use App\Models\School; use App\Models\Student; -use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; -use Illuminate\Support\Facades\Log; use function abort; use function to_route; use function view; @@ -50,7 +48,7 @@ class StudentController extends Controller 'school_id' => ['required', 'exists:schools,id'], ]); - $student = Student::create([ + Student::create([ 'first_name' => request('first_name'), 'last_name' => request('last_name'), 'grade' => request('grade'), @@ -69,11 +67,12 @@ class StudentController extends Controller $maxGrade = Audition::max('maximum_grade'); $schools = School::orderBy('name')->get(); $student->loadCount('entries'); + return view('admin.students.edit', ['student' => $student, 'schools' => $schools, 'minGrade' => $minGrade, 'maxGrade' => $maxGrade]); } - public function update(Request $request, Student $student) + public function update(Student $student) { if (! Auth::user()->is_admin) { abort(403); @@ -105,8 +104,7 @@ class StudentController extends Controller public function destroy(Student $student) { - Log::debug('Deleting student '.$student->id); - if($student->entries()->count() > 0) { + if ($student->entries()->count() > 0) { return to_route('admin.students.index')->with('error', 'You cannot delete a student with entries.'); } $name = $student->full_name(); diff --git a/app/Http/Controllers/Tabulation/EntryFlagController.php b/app/Http/Controllers/Tabulation/EntryFlagController.php index 3a9b1b1..32da870 100644 --- a/app/Http/Controllers/Tabulation/EntryFlagController.php +++ b/app/Http/Controllers/Tabulation/EntryFlagController.php @@ -76,7 +76,7 @@ class EntryFlagController extends Controller 'Cannot enter a no-show for an entry in an audition where advancement is published'); } DB::table('score_sheets')->where('entry_id', $entry->id)->delete(); - + $entry->addFlag('no_show'); $msg = 'No Show has been entered for '.$entry->audition->name.' #'.$entry->draw_number.' (ID: '.$entry->id.').'; @@ -86,6 +86,18 @@ class EntryFlagController extends Controller public function undoNoShow(Entry $entry) { - // + if ($entry->audition->hasFlag('seats_published')) { + return to_route('entry-flags.noShowSelect')->with('error', + 'Cannot undo a no-show for an entry in an audition where seats are published'); + } + if ($entry->audition->hasFlag('advance_published')) { + return to_route('entry-flags.noShowSelect')->with('error', + 'Cannot undo a no-show for an entry in an audition where advancement is published'); + } + + $entry->removeFlag('no_show'); + + return to_route('entry-flags.noShowSelect')->with('success', + 'No Show status has been removed for '.$entry->audition->name.' #'.$entry->draw_number.' (ID: '.$entry->id.').'); } } diff --git a/database/factories/EnsembleFactory.php b/database/factories/EnsembleFactory.php index a0b950c..4e85c3f 100644 --- a/database/factories/EnsembleFactory.php +++ b/database/factories/EnsembleFactory.php @@ -2,10 +2,12 @@ namespace Database\Factories; +use App\Models\Ensemble; +use App\Models\Event; use Illuminate\Database\Eloquent\Factories\Factory; /** - * @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Ensemble> + * @extends Factory */ class EnsembleFactory extends Factory { @@ -17,8 +19,8 @@ class EnsembleFactory extends Factory public function definition(): array { return [ - 'name' => $this->faker->name, - 'event_id' => \App\Models\Event::factory(), + 'name' => $this->faker->words(4, true), + 'event_id' => Event::factory(), 'code' => $this->faker->randomLetter().$this->faker->randomLetter, 'rank' => $this->faker->numberBetween(1, 10), ]; diff --git a/tests/Feature/Pages/Tabulation/noShowEnterAndCancelTest.php b/tests/Feature/Pages/Tabulation/noShowEnterTest.php similarity index 95% rename from tests/Feature/Pages/Tabulation/noShowEnterAndCancelTest.php rename to tests/Feature/Pages/Tabulation/noShowEnterTest.php index 51d3bac..fd16205 100644 --- a/tests/Feature/Pages/Tabulation/noShowEnterAndCancelTest.php +++ b/tests/Feature/Pages/Tabulation/noShowEnterTest.php @@ -13,12 +13,14 @@ it('only allows an admin or tab user to enter a no-show', function () { post(route('entry-flags.enterNoShow', $entry)) ->assertRedirect(route('home')); actAsAdmin(); + /** @noinspection PhpUnhandledExceptionInspection */ post(route('entry-flags.enterNoShow', $entry)) ->assertSessionHasNoErrors() ->assertSessionHas('success', 'No Show has been entered for '.$entry->audition->name.' #'.$entry->draw_number.' (ID: '.$entry->id.').') ->assertRedirect(route('entry-flags.noShowSelect')); actAsTab(); + /** @noinspection PhpUnhandledExceptionInspection */ post(route('entry-flags.enterNoShow', $entry)) ->assertSessionHasNoErrors() ->assertSessionHas('success', diff --git a/tests/Feature/Pages/Tabulation/noShowUndoTest.php b/tests/Feature/Pages/Tabulation/noShowUndoTest.php new file mode 100644 index 0000000..cd5723c --- /dev/null +++ b/tests/Feature/Pages/Tabulation/noShowUndoTest.php @@ -0,0 +1,55 @@ +create(); + $entry->addFlag('no_show'); + delete(route('entry-flags.undoNoShow', $entry)) + ->assertRedirect(route('home')); + actAsAdmin(); + /** @noinspection PhpUnhandledExceptionInspection */ + delete(route('entry-flags.undoNoShow', $entry)) + ->assertSessionHasNoErrors() + ->assertSessionHas('success', + 'No Show status has been removed for '.$entry->audition->name.' #'.$entry->draw_number.' (ID: '.$entry->id.').') + ->assertRedirect(route('entry-flags.noShowSelect')); + actAsTab(); + /** @noinspection PhpUnhandledExceptionInspection */ + delete(route('entry-flags.undoNoShow', $entry)) + ->assertSessionHasNoErrors() + ->assertSessionHas('success', + 'No Show status has been removed for '.$entry->audition->name.' #'.$entry->draw_number.' (ID: '.$entry->id.').') + ->assertRedirect(route('entry-flags.noShowSelect')); +}); +it('will not undo a no-show flag for an entry in a published audition', function () { + $entry1 = Entry::factory()->create(); + $entry2 = Entry::factory()->create(); + $entry1->addFlag('no_show'); + $entry2->addFlag('no_show'); + $entry1->audition->addFlag('seats_published'); + $entry2->audition->addFlag('advance_published'); + actAsAdmin(); + delete(route('entry-flags.undoNoShow', $entry1)) + ->assertRedirect(route('entry-flags.noShowSelect')) + ->assertSessionHas('error', 'Cannot undo a no-show for an entry in an audition where seats are published'); + delete(route('entry-flags.undoNoShow', $entry2)) + ->assertRedirect(route('entry-flags.noShowSelect')) + ->assertSessionHas('error', + 'Cannot undo a no-show for an entry in an audition where advancement is published'); +}); + +it('removes a no_show flag to the entry', function () { + // Arrange + $entry = Entry::factory()->create(); + $entry->addFlag('no_show'); + // Act & Assert + actAsAdmin(); + delete(route('entry-flags.undoNoShow', $entry)); + expect(Entry::find($entry->id)->hasFlag('no_show'))->toBeFalse(); +}); -- 2.39.5