Rewrite tabulation #14

Merged
okorpheus merged 43 commits from rewrite-tabulation into master 2024-07-14 05:36:29 +00:00
5 changed files with 182 additions and 8 deletions
Showing only changes of commit d45ebf4eec - Show all commits

View File

@ -0,0 +1,17 @@
<?php
namespace App\Http\Controllers\Tabulation;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
class SeatAuditionController extends Controller
{
/**
* Handle the incoming request.
*/
public function __invoke(Request $request)
{
//
}
}

View File

@ -16,7 +16,7 @@ class SeatingStatusController extends Controller
$auditions = Audition::forSeating()->withCount(['entries', 'unscoredEntries'])->with('flags')->get();
$auditionData = [];
foreach ($auditions as $audition) {
$auditionData[] = [
$auditionData[$audition->id] = [
'id' => $audition->id,
'name' => $audition->name,
'scoredEntriesCount' => $audition->entries_count - $audition->unscored_entries_count,

View File

@ -29,7 +29,7 @@ class Audition extends Model
return $this->hasMany(Entry::class);
}
public function unscoredEntries()
public function unscoredEntries(): HasMany
{
return $this->hasMany(Entry::class)
->whereDoesntHave('scoreSheets');

View File

@ -1,23 +1,30 @@
<?php
// Tabulation Routes
use App\Http\Controllers\Tabulation\AdvancementController;
use App\Http\Controllers\Tabulation\DoublerDecisionController;
use App\Http\Controllers\Tabulation\EntryFlagController;
use App\Http\Controllers\Tabulation\ScoreController;
use App\Http\Controllers\Tabulation\SeatAuditionController;
use App\Http\Controllers\Tabulation\SeatingStatusController;
use App\Http\Controllers\Tabulation\TabulationController;
use App\Http\Middleware\CheckIfCanTab;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Route;
Route::middleware(['auth', 'verified', CheckIfCanTab::class])->group(function () {
// Score Management
Route::prefix('scores/')->controller(\App\Http\Controllers\Tabulation\ScoreController::class)->group(function () {
Route::prefix('scores/')->controller(ScoreController::class)->group(function () {
Route::get('/choose_entry', 'chooseEntry')->name('scores.chooseEntry');
Route::get('/entry', 'entryScoreSheet')->name('scores.entryScoreSheet');
Route::post('/entry/{entry}', 'saveEntryScoreSheet')->name('scores.saveEntryScoreSheet');
Route::delete('/{score}',
[\App\Http\Controllers\Tabulation\ScoreController::class, 'destroyScore'])->name('scores.destroy');
[ScoreController::class, 'destroyScore'])->name('scores.destroy');
});
// Entry Flagging
Route::prefix('entry-flags/')->controller(\App\Http\Controllers\Tabulation\EntryFlagController::class)->group(function (
Route::prefix('entry-flags/')->controller(EntryFlagController::class)->group(function (
) {
Route::get('/choose_no_show', 'noShowSelect')->name('entry-flags.noShowSelect');
Route::get('/propose-no-show', 'noShowConfirm')->name('entry-flags.confirmNoShow');
@ -27,11 +34,12 @@ Route::middleware(['auth', 'verified', CheckIfCanTab::class])->group(function ()
// Seating Routes
Route::prefix('seating/')->group(function () {
Route::get('/', App\Http\Controllers\Tabulation\SeatingStatusController::class)->name('seating.status');
Route::get('/', SeatingStatusController::class)->name('seating.status');
Route::get('/{audition}', SeatAuditionController::class)->name('seating.audition');
});
// Generic Tabulation Routes (TO BE REPLACED)
Route::prefix('tabulation/')->controller(\App\Http\Controllers\Tabulation\TabulationController::class)->group(function (
Route::prefix('tabulation/')->controller(TabulationController::class)->group(function (
) {
Route::get('/status', 'status')->name('tabulation.status');
Route::match(['get', 'post'], '/auditions/{audition}', 'auditionSeating')->name('tabulation.audition.seat');
@ -40,7 +48,7 @@ Route::middleware(['auth', 'verified', CheckIfCanTab::class])->group(function ()
});
// Advancement Routes
Route::prefix('advancement/')->controller(\App\Http\Controllers\Tabulation\AdvancementController::class)->group(function (
Route::prefix('advancement/')->controller(AdvancementController::class)->group(function (
) {
Route::get('/status', 'status')->name('advancement.status');
Route::get('/{audition}', 'ranking')->name('advancement.ranking');

View File

@ -0,0 +1,149 @@
<?php
use App\Models\Audition;
use App\Models\Entry;
use App\Models\ScoreSheet;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use function Pest\Laravel\get;
uses(RefreshDatabase::class);
it('will not answer to guest or normal user', function () {
get(route('seating.status'))
->assertRedirect(route('home'));
actAsNormal();
get(route('seating.status'))
->assertRedirect(route('dashboard'))
->assertSessionHas('error', 'You are not authorized to perform this action');
});
it('responds to an admin', function () {
// Arrange
actAsAdmin();
// Act & Assert
get(route('seating.status'))
->assertOk();
});
it('responds to a tabulator', function () {
// Arrange
actAsTab();
// Act & Assert
get(route('seating.status'))
->assertOk();
});
it('sends the view a collection of audition data that includes data needed by the view', function () {
// Arrange
$auditions = Audition::factory()->count(5)->create();
actAsAdmin();
// Act
$response = get(route('seating.status'));
// Assert
foreach ($auditions as $audition) {
$response->assertOk()
->assertViewHas('auditionData', function ($viewAuditionData) use ($audition) {
return $viewAuditionData[$audition->id]['id'] === $audition->id;
});
$response->assertOk()
->assertViewHas('auditionData', function ($viewAuditionData) use ($audition) {
return $viewAuditionData[$audition->id]['name'] === $audition->name;
});
}
});
it('has correct count info for an audition with 5 entries none scored', function () {
$audition = Audition::factory()->create();
Entry::factory()->count(5)->create(['audition_id' => $audition->id]);
actAsAdmin();
$response = get(route('seating.status'));
$response->assertOk();
$response->assertViewHas('auditionData', function ($viewAuditionData) use ($audition) {
return $viewAuditionData[$audition->id]['scoredEntriesCount'] === 0;
});
$response->assertViewHas('auditionData', function ($viewAuditionData) use ($audition) {
return $viewAuditionData[$audition->id]['totalEntriesCount'] === 5;
});
$response->assertViewHas('auditionData', function ($viewAuditionData) use ($audition) {
return $viewAuditionData[$audition->id]['scoredPercentage'] === 0;
});
$response->assertViewHas('auditionData', function ($viewAuditionData) use ($audition) {
return $viewAuditionData[$audition->id]['scoringComplete'] === false;
});
});
it('has correct count info for an audition with 8 entries 2 scored', function () {
$judge = User::factory()->create();
$audition = Audition::factory()->create();
$entries = Entry::factory()->count(2)->create(['audition_id' => $audition->id]);
$entries->each(fn ($entry) => ScoreSheet::create([
'user_id' => $judge->id,
'entry_id' => $entry->id,
'subscores' => 7,
]));
Entry::factory()->count(6)->create(['audition_id' => $audition->id]);
actAsAdmin();
$response = get(route('seating.status'));
$response->assertOk();
$response->assertViewHas('auditionData', function ($viewAuditionData) use ($audition) {
return $viewAuditionData[$audition->id]['scoredEntriesCount'] === 2;
});
$response->assertViewHas('auditionData', function ($viewAuditionData) use ($audition) {
return $viewAuditionData[$audition->id]['totalEntriesCount'] === 8;
});
$response->assertViewHas('auditionData', function ($viewAuditionData) use ($audition) {
return $viewAuditionData[$audition->id]['scoredPercentage'] == 25;
});
$response->assertViewHas('auditionData', function ($viewAuditionData) use ($audition) {
return $viewAuditionData[$audition->id]['scoringComplete'] === false;
});
});
it('has correct count info for an audition with 1 entries 1 scored', function () {
$judge = User::factory()->create();
$audition = Audition::factory()->create();
$entry = Entry::factory()->create(['audition_id' => $audition->id]);
ScoreSheet::create([
'user_id' => $judge->id,
'entry_id' => $entry->id,
'subscores' => 3,
]);
actAsAdmin();
$response = get(route('seating.status'));
$response->assertOk();
$response->assertViewHas('auditionData', function ($viewAuditionData) use ($audition) {
return $viewAuditionData[$audition->id]['scoredEntriesCount'] === 1;
});
$response->assertViewHas('auditionData', function ($viewAuditionData) use ($audition) {
return $viewAuditionData[$audition->id]['totalEntriesCount'] === 1;
});
$response->assertViewHas('auditionData', function ($viewAuditionData) use ($audition) {
return $viewAuditionData[$audition->id]['scoredPercentage'] == 100;
});
$response->assertViewHas('auditionData', function ($viewAuditionData) use ($audition) {
return $viewAuditionData[$audition->id]['scoringComplete'] === true;
});
});
it('correctly shows a flag when the audition is flagged as seated', function () {
$audition = Audition::factory()->create();
actAsAdmin();
$response = get(route('seating.status'));
$response->assertOk();
$response->assertViewHas('auditionData', function ($viewAuditionData) use ($audition) {
return $viewAuditionData[$audition->id]['seatsPublished'] === false;
});
$audition->addFlag('seats_published');
$response = get(route('seating.status'));
$response->assertOk();
$response->assertViewHas('auditionData', function ($viewAuditionData) use ($audition) {
return $viewAuditionData[$audition->id]['seatsPublished'] === true;
});
});