auditionadmin/app/Http/Controllers/Tabulation/SeatAuditionFormController.php

308 lines
12 KiB
PHP

<?php
namespace App\Http\Controllers\Tabulation;
use App\Actions\Entries\DoublerDecision;
use App\Actions\Tabulation\RankAuditionEntries;
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 redirect;
class SeatAuditionFormController extends Controller
{
public function showForm(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', 'student.school'])
->get();
} else {
$publishedSeats = false;
}
$ranker = app(RankAuditionEntries::class);
// Get scored entries in order
try {
$scored_entries = $ranker($audition, 'seating');
} catch (AuditionAdminException $e) {
return redirect()->route('seating.audition', ['audition' => $audition->id])
->with('error', $e->getMessage());
}
$scored_entries->load(['student.doublers', 'student.school']);
// Get unscored entries sorted by draw number
$unscored_entries = $audition->entries()
->whereDoesntHave('totalScore')
->whereDoesntHave('flags', function ($query) {
$query->where('flag_name', 'no_show');
})
->whereDoesntHave('flags', function ($query) {
$query->where('flag_name', 'failed_prelim');
})
->with('student.school')
->withCount('scoreSheets')
->orderBy('draw_number')
->get();
// Get no show entries sorted by draw number
$noshow_entries = $audition->entries()
->whereDoesntHave('totalScore')
->whereHas('flags', function ($query) {
$query->where('flag_name', 'no_show');
})
->with('student.school')
->orderBy('draw_number')
->get();
// Get failed prelim entries sorted by draw number
$failed_prelim_entries = $audition->entries()
->whereDoesntHave('totalScore')
->whereHas('flags', function ($query) {
$query->where('flag_name', 'failed_prelim');
})
->with('student.school')
->orderBy('draw_number')
->get();
// Get Doublers
$doublerData = Doubler::where('event_id', $audition->event_id)
->whereIn('student_id', $scored_entries->pluck('student_id'))
->get()
->keyBy('student_id');
$auditionHasUnresolvedDoublers = false;
foreach ($doublerData as $doubler) {
if (! is_null($doubler->accepted_entry)) {
continue;
}
foreach ($doubler->entries() as $entry) {
if ($entry->audition_id === $audition->id && $entry->hasFlag('declined')) {
continue 2;
}
}
$auditionHasUnresolvedDoublers = true;
}
$canSeat = ! $auditionHasUnresolvedDoublers && $unscored_entries->count() === 0;
return view('tabulation.auditionSeating',
compact('audition',
'scored_entries',
'unscored_entries',
'noshow_entries',
'failed_prelim_entries',
'doublerData',
'auditionHasUnresolvedDoublers',
'canSeat',
'seatingProposal',
'publishedSeats',
)
);
}
public function declineSeat(Audition $audition, Entry $entry)
{
$decider = app(DoublerDecision::class);
try {
$decider->decline($entry);
} catch (AuditionAdminException $e) {
return redirect()->route('seating.audition', ['audition' => $audition->id])
->with('error', $e->getMessage());
}
return redirect()->route('seating.audition', ['audition' => $audition->id])->with('success',
$entry->student->full_name().' has declined '.$audition->name);
}
public function massDecline(Audition $audition)
{
$decider = app(DoublerDecision::class);
$validData = request()->validate([
'decline-below' => ['required', 'integer', 'min:0'],
]);
$ranker = app(RankAuditionEntries::class);
// Get scored entries in order
try {
$scored_entries = $ranker($audition, 'seating');
} catch (AuditionAdminException $e) {
return redirect()->route('seating.audition', ['audition' => $audition->id])
->with('error', $e->getMessage());
}
$scored_entries->load(['student.doublers', 'student.school']);
foreach ($scored_entries as $entry) {
Debugbar::info('Starting entry '.$entry->student->full_name());
if ($entry->seatingRank < $validData['decline-below']) {
Debugbar::info('Skipping '.$entry->student->full_name().' because they are ranked above decline threshold');
continue;
}
if ($entry->hasFlag('declined')) {
Debugbar::info('Skipping '.$entry->student->full_name().' because they have already been declined');
continue;
}
if (! $entry->student->isDoublerInEvent($audition->event_id)) {
Debugbar::info('Skipping '.$entry->student->full_name().' because they are not a doubler');
continue;
}
if ($entry->student->doublers->where('event_id', $audition->event_id)->first()->accepted_entry) {
Debugbar::info('Skipping '.$entry->student->full_name().' because they have already accepted a seat');
continue;
}
try {
$decider->decline($entry);
} catch (AuditionAdminException $e) {
return redirect()->route('seating.audition', ['audition' => $audition->id])
->with('error', $e->getMessage());
}
}
Cache::forget('rank_seating_'.$audition->id);
return redirect()->route('seating.audition', ['audition' => $audition->id]);
}
public function acceptSeat(Audition $audition, Entry $entry)
{
$decider = app(DoublerDecision::class);
try {
$decider->accept($entry);
} catch (AuditionAdminException $e) {
return redirect()->route('seating.audition', ['audition' => $audition->id])
->with('error', $e->getMessage());
}
return redirect()->route('seating.audition', ['audition' => $audition->id])->with('success',
$entry->student->full_name().' has accepted '.$audition->name);
}
public function noshow(
Audition $audition,
Entry $entry
) {
$recorder = app('App\Actions\Tabulation\EnterNoShow');
try {
$msg = $recorder($entry);
} catch (AuditionAdminException $e) {
return redirect()->back()->with('error', $e->getMessage());
}
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 = [];
try {
$rankedEntries = $ranker($audition, 'seating');
} catch (AuditionAdminException $e) {
return redirect()->route('seating.audition', ['audition' => $audition->id])
->with('error', $e->getMessage());
}
$rankedEntries = $rankedEntries->reject(function ($entry) {
return $entry->hasFlag('declined');
});
$rankedEntries->load(['student.school']);
$rankedEnsembles = Ensemble::orderBy('rank')->where('event_id', $audition->event_id)->get();
$ensembleRankOn = 1;
foreach ($rankedEnsembles 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);
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'],
];
}
}
}
try {
$publisher($audition, $proposal);
} catch (AuditionAdminException $e) {
return redirect()->route('seating.audition', [$audition])->with('error', $e->getMessage());
}
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]);
}
}