Seating Publication Working

This commit is contained in:
Matt Young 2025-06-26 03:44:34 -05:00
parent 14b6bb61c7
commit 98378c6182
3 changed files with 200 additions and 19 deletions

View File

@ -8,16 +8,33 @@ use App\Exceptions\AuditionAdminException;
use App\Http\Controllers\Controller;
use App\Models\Audition;
use App\Models\Doubler;
use App\Models\Ensemble;
use App\Models\Entry;
use App\Models\Seat;
use Debugbar;
use Illuminate\Http\Request;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Cache;
use function PHPUnit\Framework\isNull;
use function redirect;
class SeatAuditionFormController extends Controller
{
public function showForm(Request $request, Audition $audition)
{
$seatingProposal = (session('proposedSeatingArray-'.$audition->id));
if ($audition->hasFlag('seats_published')) {
$publishedSeats = Seat::where('audition_id', $audition->id)
->join('ensembles', 'seats.ensemble_id', '=', 'ensembles.id')
->orderBy('ensembles.rank')
->orderBy('seats.seat')
->select('seats.*')
->with(['ensemble', 'entry.student.school'])
->get();
} else {
$publishedSeats = false;
}
$ranker = app(RankAuditionEntries::class);
// Get scored entries in order
$scored_entries = $ranker($audition, 'seating');
@ -63,7 +80,7 @@ class SeatAuditionFormController extends Controller
$auditionHasUnresolvedDoublers = false;
foreach ($doublerData as $doubler) {
if (! isNull($doubler->accepted_entry)) {
if (! is_null($doubler->accepted_entry)) {
continue;
}
foreach ($doubler->entries() as $entry) {
@ -75,6 +92,7 @@ class SeatAuditionFormController extends Controller
}
$canSeat = ! $auditionHasUnresolvedDoublers && $unscored_entries->count() === 0;
Debugbar::info($seatingProposal);
return view('tabulation.auditionSeating',
compact('audition',
@ -85,6 +103,8 @@ class SeatAuditionFormController extends Controller
'doublerData',
'auditionHasUnresolvedDoublers',
'canSeat',
'seatingProposal',
'publishedSeats',
)
);
}
@ -130,6 +150,90 @@ class SeatAuditionFormController extends Controller
return redirect()->route('seating.audition', [$audition])->with('success', $msg);
}
public function draftSeats(Audition $audition, Request $request)
{
$ranker = app(RankAuditionEntries::class);
$validated = $request->validate([
'ensemble' => ['required', 'array'],
'ensemble.*' => ['required', 'integer', 'min:0'],
]);
$proposedSeatingArray = [];
$rankedEntries = $ranker($audition, 'seating');
$rankedEntries = $rankedEntries->reject(function ($entry) {
return $entry->hasFlag('declined');
});
$rankedEntries->load(['student.school']);
$rankedEnembles = Ensemble::orderBy('rank')->where('event_id', $audition->event_id)->get();
$ensembleRankOn = 1;
foreach ($rankedEnembles as $ensemble) {
if (! Arr::has($validated['ensemble'], $ensemble->id)) {
continue;
}
$proposedSeatingArray[$ensembleRankOn]['ensemble_id'] = $ensemble->id;
$proposedSeatingArray[$ensembleRankOn]['ensemble_name'] = $ensemble->name;
$proposedSeatingArray[$ensembleRankOn]['accept_count'] = $validated['ensemble'][$ensemble->id];
for ($n = 1; $n <= $validated['ensemble'][$ensemble->id]; $n++) {
// Escape the loop if we're out of entries
if ($rankedEntries->isEmpty()) {
break;
}
$thisEntry = $rankedEntries->shift();
$proposedSeatingArray[$ensembleRankOn]['seats'][$n]['seat'] = $n;
$proposedSeatingArray[$ensembleRankOn]['seats'][$n]['entry_id'] = $thisEntry->id;
$proposedSeatingArray[$ensembleRankOn]['seats'][$n]['entry_name'] = $thisEntry->student->full_name();
$proposedSeatingArray[$ensembleRankOn]['seats'][$n]['entry_school'] = $thisEntry->student->school->name;
}
$ensembleRankOn++;
}
$sessionKeyName = 'proposedSeatingArray-'.$audition->id;
$request->session()->put($sessionKeyName, $proposedSeatingArray, 10);
return redirect()->route('seating.audition', ['audition' => $audition->id]);
}
public function clearDraft(Audition $audition)
{
session()->forget('proposedSeatingArray-'.$audition->id);
return redirect()->route('seating.audition', ['audition' => $audition->id]);
}
public function publishSeats(Audition $audition)
{
$publisher = app('App\Actions\Tabulation\PublishSeats');
$seatingProposal = (session('proposedSeatingArray-'.$audition->id));
$proposal = [];
foreach ($seatingProposal as $ensemble) {
$ensembleId = $ensemble['ensemble_id'];
if (isset($ensemble['seats'])) {
foreach ($ensemble['seats'] as $seat) {
$proposal[] = [
'ensemble_id' => $ensembleId,
'audition_id' => $audition->id,
'seat' => $seat['seat'],
'entry_id' => $seat['entry_id'],
];
}
}
}
$publisher($audition, $proposal);
session()->forget('proposedSeatingArray-'.$audition->id);
return redirect()->route('seating.audition', [$audition]);
}
public function unpublishSeats(Audition $audition)
{
$unpublisher = app('App\Actions\Tabulation\UnpublishSeats');
$unpublisher($audition);
session()->forget('proposedSeatingArray-'.$audition->id);
return redirect()->route('seating.audition', [$audition]);
}
protected function pickRightPanel(Audition $audition, array $seatable)
{
if ($audition->hasFlag('seats_published')) {

View File

@ -1,7 +1,6 @@
<x-layout.app>
<x-slot:page_title>Audition Seating - {{ $audition->name }}</x-slot:page_title>
<div class="grid grid-cols-4"></div>
<div class="grid grid-cols-4">
<div class="grid grid-cols-4 gap-4">
<div class="col-span-3"> {{-- Entry Ranking Table --}}
<x-card.card class="px-3"> {{-- Scored Entries --}}
@ -94,7 +93,8 @@
<span class="text-xs text-gray-400">{{ $entry->student->school->name }}</span>
</x-table.td>
<x-table.td>
<x-form.form method="POST" action="{{ route('seating.audition.noshow',[$audition, $entry]) }}">
<x-form.form method="POST"
action="{{ route('seating.audition.noshow',[$audition, $entry]) }}">
<x-form.button>Record No Show</x-form.button>
</x-form.form>
</x-table.td>
@ -157,21 +157,97 @@
</div>
<div class="ml-4">
{{-- TODO: Add in bulk delince doubler option --}}
@if($unscored_entries->count() > 0)
<x-card.card class="p-3 text-red-500 mb-3">
Cannot seat the audition while entries are unscored.
<div> {{-- Right Column Wrapper --}}
@if($audition->hasFlag('seats_published'))
<x-card.card>
<x-card.heading>Published Results</x-card.heading>
<x-card.list.body>
@php($previousEnsemble = '')
@foreach($publishedSeats as $seat)
@if($previousEnsemble !== $seat->ensemble->name)
@php($previousEnsemble = $seat->ensemble->name)
<x-card.list.row class="font-semibold">{{ $seat->ensemble->name }}</x-card.list.row>
@endif
<x-card.list.row>
<div>
<p>{{ $seat->seat }}. {{ $seat->student->full_name() }}</p>
<p class="ml-5 text-xs">{{ $seat->student->school->name }}</p>
</div>
</x-card.list.row>
@endforeach
</x-card.list.body>
</x-card.card>
@endif
@if($auditionHasUnresolvedDoublers)
<x-card.card class="p-3 text-red-500">
Cannot seat the audition while there are unresolved doublers.
</x-card.card>
<x-form.form method="POST" action="{{ route('seating.audition.unpublishSeats',[$audition]) }}">
<x-form.button class="mt-3">Unpublish Results</x-form.button>
</x-form.form>
@else
@if($canSeat)
@if($seatingProposal)
<x-card.card>
<x-card.heading>
Seating Proposal
<x-slot:subheading>Results are not yet published</x-slot:subheading>
</x-card.heading>
@foreach($seatingProposal as $proposedEnsemble)
<h3 class="m-3 font-semibold">{{ $proposedEnsemble['ensemble_name'] }}</h3>
<x-card.list.body>
@if(isset($proposedEnsemble['seats']))
@foreach($proposedEnsemble['seats'] as $seat)
<x-card.list.row>{{ $seat['seat'] }}
. {{ $seat['entry_name'] }}</x-card.list.row>
@endforeach
@endif
</x-card.list.body>
@endforeach
<x-form.form method="POST" action="{{ route('seating.audition.clearDraft',[$audition]) }}">
<x-form.button class="mb-3">Clear Draft</x-form.button>
</x-form.form>
<x-form.form method="POST"
action="{{ route('seating.audition.publishSeats',[$audition]) }}">
<x-form.button class="mb-3">Publish</x-form.button>
</x-form.form>
</x-card.card>
@else
<x-card.card class="p-3">
<x-form.form metohd="POST" action="{{ route('seating.audition.draftSeats',[$audition]) }}">
<x-card.heading class="-ml-5">Seat Audition
<x-slot:subheading>Choose how many entries to seat in each ensemble
</x-slot:subheading>
</x-card.heading>
@foreach($audition->SeatingLimits()->where('maximum_accepted','>',0)->get() as $limit)
<x-form.select name="ensemble[{{ $limit->ensemble_id }}]">
<x-slot:label>{{$limit->ensemble->name}}</x-slot:label>
@for($n = 0; $n< $limit->maximum_accepted; $n++)
<option value="{{$n}}">{{$n}}</option>
@endfor
<option value="{{$n}}" SELECTED>{{$n}}</option>
</x-form.select>
@endforeach
<x-form.button class="mt-3">Draft Seats</x-form.button>
</x-form.form>
</x-card.card>
@endif
@else
<div class="ml-4">
{{-- TODO: Add in bulk decline doubler option --}}
@if($unscored_entries->count() > 0)
<x-card.card class="p-3 text-red-500 mb-3">
Cannot seat the audition while entries are unscored.
</x-card.card>
@endif
@if($auditionHasUnresolvedDoublers)
<x-card.card class="p-3 text-red-500">
Cannot seat the audition while there are unresolved doublers.
</x-card.card>
@endif
</div>
@endif
@endif
</div>
</div>

View File

@ -7,7 +7,6 @@ use App\Http\Controllers\Tabulation\DoublerDecisionController;
use App\Http\Controllers\Tabulation\EntryFlagController;
use App\Http\Controllers\Tabulation\ScoreController;
use App\Http\Controllers\Tabulation\SeatAuditionFormController;
use App\Http\Controllers\Tabulation\SeatingPublicationController;
use App\Http\Controllers\Tabulation\SeatingStatusController;
use App\Http\Middleware\CheckIfCanTab;
use Illuminate\Support\Facades\Route;
@ -43,13 +42,15 @@ Route::middleware(['auth', 'verified', CheckIfCanTab::class])->group(function ()
Route::prefix('seating/')->group(function () {
Route::get('/', SeatingStatusController::class)->name('seating.status');
Route::get('/{audition}', [SeatAuditionFormController::class, 'showForm'])->name('seating.audition');
Route::post('/{audition}/draftSeats', [SeatAuditionFormController::class, 'draftSeats'])->name('seating.audition.draftSeats');
Route::post('/{audition}/clearDraft', [SeatAuditionFormController::class, 'clearDraft'])->name('seating.audition.clearDraft');
Route::post('/{audition}/{entry}/decline', [SeatAuditionFormController::class, 'declineSeat'])->name('seating.audition.decline');
Route::post('/{audition}/{entry}/accept', [SeatAuditionFormController::class, 'acceptSeat'])->name('seating.audition.accept');
Route::post('/{audition}/{entry}/noshow', [SeatAuditionFormController::class, 'noshow'])->name('seating.audition.noshow');
Route::post('/{audition}/publish',
[SeatingPublicationController::class, 'publishSeats'])->name('seating.audition.publish');
[SeatAuditionFormController::class, 'publishSeats'])->name('seating.audition.publishSeats');
Route::post('/{audition}/unpublish',
[SeatingPublicationController::class, 'unpublishSeats'])->name('seating.audition.unpublish');
[SeatAuditionFormController::class, 'unpublishSeats'])->name('seating.audition.unpublishSeats');
});
// Advancement Routes