Bonus Score Entry

#20 Implement bonus scores
Judges entry list screen correctly show scored entries.
This commit is contained in:
Matt Young 2024-07-15 22:32:47 -05:00
parent 475ac181c6
commit 7e908024d7
8 changed files with 156 additions and 16 deletions

View File

@ -0,0 +1,32 @@
<?php
namespace App\Http\Controllers\Judging;
use App\Http\Controllers\Controller;
use App\Models\Audition;
use App\Models\BonusScore;
use App\Models\BonusScoreDefinition;
use Illuminate\Support\Facades\Auth;
class BonusScoreEntryListController extends Controller
{
public function __invoke(Audition $audition)
{
/** @var BonusScoreDefinition $bonusScore */
$bonusScore = $audition->bonusScore()->first();
if (! $bonusScore->judges->contains(auth()->id())) {
return redirect()->route('dashboard')->with('error', 'You are not assigned to judge this bonus score');
}
$entries = $audition->entries()->orderBy('draw_number')->get();
$entries = $entries->reject(fn ($entry) => $entry->hasFlag('no_show'));
$entries->each(fn ($entry) => $entry->audition = $audition);
$scores = BonusScore::where('user_id', Auth::user()->id)
->with('entry.audition')
->with('originallyScoredEntry.audition')
->get()
->keyBy('entry_id');
return view('judging.bonus_score_entry_list', compact('audition', 'entries', 'scores'));
}
}

View File

@ -1,10 +1,11 @@
<?php
namespace App\Http\Controllers;
namespace App\Http\Controllers\Judging;
use App\Actions\Tabulation\EnterScore;
use App\Exceptions\AuditionServiceException;
use App\Exceptions\ScoreEntryException;
use App\Http\Controllers\Controller;
use App\Models\Audition;
use App\Models\Entry;
use App\Models\JudgeAdvancementVote;

View File

@ -3,8 +3,24 @@
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class BonusScore extends Model
{
protected $guarded = [];
public function entry(): BelongsTo
{
return $this->belongsTo(Entry::class);
}
public function judge(): BelongsTo
{
return $this->belongsTo(User::class, 'user_id');
}
public function originallyScoredEntry(): BelongsTo
{
return $this->belongsTo(Entry::class, 'originally_scored_entry');
}
}

View File

@ -0,0 +1,30 @@
@php use Carbon\Carbon; @endphp
<x-layout.app>
<x-slot:page_title>Judging Dashboard - Bonus Scores</x-slot:page_title>
<x-table.table>
<x-slot:title>{{ $audition->name }} - Bonus Score</x-slot:title>
<x-table.body>
<thead>
<tr>
<x-table.th>Entry</x-table.th>
<x-table.th>Score</x-table.th>
<x-table.th>Scored On</x-table.th>
<x-table.th>Score Timestamp</x-table.th>
</tr>
</thead>
<x-table.body>
@foreach($entries as $entry)
<tr>
<x-table.td>{{ $audition->name }} {{ $entry->draw_number }}</x-table.td>
@if($scores->has($entry->id))
<x-table.td>{{ $scores[$entry->id]->score }}</x-table.td>
<x-table.td>{{ $scores[$entry->id]->originallyScoredEntry->audition->name }}</x-table.td>
<x-table.td>{{ Carbon::create($scores[$entry->id]->created_at)->setTimezone('America/Chicago')->format('m/d/y H:i') }}</x-table.td>
@endif
</tr>
@endforeach
</x-table.body>
</x-table.body>
</x-table.table>
</x-layout.app>

View File

@ -22,7 +22,7 @@
<x-card.heading>{{ $bonusScore->name }}</x-card.heading>
<x-card.list.body>
@foreach($bonusScore->auditions as $audition)
<a href="#">
<a href="{{ route('judging.bonusScore.EntryList', $audition) }}">
<x-card.list.row class="!py-3 ml-3">
{{ $audition->name }}
</x-card.list.row>

View File

@ -1,6 +1,8 @@
<?php
// Judging Routes
use App\Http\Controllers\JudgingController;
use App\Http\Controllers\Judging\BonusScoreEntryListController;
use App\Http\Controllers\Judging\JudgingController;
use App\Http\Middleware\CheckIfCanJudge;
use Illuminate\Support\Facades\Route;
@ -11,3 +13,8 @@ Route::middleware(['auth', 'verified', CheckIfCanJudge::class])->prefix('judging
Route::post('/entry/{entry}', 'saveScoreSheet')->name('judging.saveScoreSheet');
Route::patch('/entry/{entry}', 'updateScoreSheet')->name('judging.updateScoreSheet');
});
// Bonus score judging routes
Route::middleware(['auth', 'verified', CheckIfCanJudge::class])->prefix('judging/bonus_scores')->group(function () {
Route::get('/{audition}', BonusScoreEntryListController::class)->name('judging.bonusScore.EntryList');
});

View File

@ -1,19 +1,7 @@
<?php
use App\Http\Controllers\DashboardController;
use App\Http\Controllers\EntryController;
use App\Http\Controllers\FilterController;
use App\Http\Controllers\JudgingController;
use App\Http\Controllers\PdfInvoiceController;
use App\Http\Controllers\SchoolController;
use App\Http\Controllers\StudentController;
use App\Http\Controllers\Tabulation\DoublerDecisionController;
use App\Http\Controllers\TestController;
use App\Http\Controllers\UserController;
use App\Http\Middleware\CheckIfAdmin;
use App\Http\Middleware\CheckIfCanJudge;
use App\Http\Middleware\CheckIfCanTab;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Route;
require __DIR__.'/admin.php';
@ -23,7 +11,6 @@ require __DIR__.'/user.php';
Route::get('/test', [TestController::class, 'flashTest'])->middleware('auth', 'verified');
Route::view('/', 'welcome')->middleware('guest')->name('home');
Route::get('/results', [App\Http\Controllers\ResultsPage::class, '__invoke'])->name('results');

View File

@ -0,0 +1,67 @@
<?php
use App\Models\Audition;
use App\Models\BonusScore;
use App\Models\BonusScoreDefinition;
use App\Models\Entry;
use App\Models\Room;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use function Pest\Laravel\actingAs;
uses(RefreshDatabase::class);
it('denies access go a guest', function () {
$response = $this->get(route('judging.bonusScore.EntryList', 1));
$response->assertRedirect(route('home'));
});
it('denies access to a user not assigned to judge this bonus score', function () {
$bonusScore = BonusScoreDefinition::factory()->create();
$audition = Audition::factory()->create();
$audition->bonusScore()->attach($bonusScore->id);
$room = Room::factory()->create();
$user = User::factory()->create();
$room->addJudge($user->id);
actingAs($user);
$this->get(route('judging.bonusScore.EntryList', $audition->id))
->assertRedirect(route('dashboard'))
->assertSessionHas('error', 'You are not assigned to judge this bonus score');
});
it('shows all entries to an authorized judge', function () {
// Arrange
$bonusScore = BonusScoreDefinition::factory()->create();
$audition = Audition::factory()->create();
$audition->bonusScore()->attach($bonusScore->id);
$entries[1] = Entry::factory()->create(['audition_id' => $audition->id, 'draw_number' => 1]);
$entries[2] = Entry::factory()->create(['audition_id' => $audition->id, 'draw_number' => 2]);
$entries[3] = Entry::factory()->create(['audition_id' => $audition->id, 'draw_number' => 3]);
$entries[4] = Entry::factory()->create(['audition_id' => $audition->id, 'draw_number' => 4]);
$entries = collect($entries);
$judge = User::factory()->create();
$bonusScore->judges()->attach($judge->id);
actingAs($judge);
// Act & Assert
$response = $this->get(route('judging.bonusScore.EntryList', $audition->id));
$response->assertOk();
$entries->each(fn ($entry) => $response->assertSee($entry->audition->name.' '.$entry->draw_number));
});
it('shows existing scores for an entry', function () {
$bonusScore = BonusScoreDefinition::factory()->create(['max_score' => 100]);
$audition = Audition::factory()->create();
$audition->bonusScore()->attach($bonusScore->id);
$entry = Entry::factory()->create(['audition_id' => $audition->id, 'draw_number' => 1]);
$judge = User::factory()->create();
$bonusScore->judges()->attach($judge->id);
BonusScore::create([
'entry_id' => $entry->id,
'user_id' => $judge->id,
'originally_scored_entry' => $entry->id,
'score' => 42,
]);
actingAs($judge);
// Act
$response = $this->get(route('judging.bonusScore.EntryList', $audition));
$response->assertOk()
->assertSeeInOrder(['<tr>', e($audition->name), $entry->draw_number, 42, '</tr>'], false);
});