Rewrite tabulation #14

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

View File

@ -36,7 +36,6 @@ class DoublerDecisionController extends Controller
}
}
$this->doublerService->refreshDoublerCache();
$returnMessage = $entry->student->full_name().' accepted seating in '.$entry->audition->name;
@ -52,8 +51,6 @@ class DoublerDecisionController extends Controller
$entry->addFlag('declined');
$this->doublerService->refreshDoublerCache();
$returnMessage = $entry->student->full_name().' declined seating in '.$entry->audition->name;
return redirect()->back()->with('success', $returnMessage);

View File

@ -10,7 +10,6 @@ use App\Services\AuditionService;
use App\Services\DoublerService;
use App\Services\EntryService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
class SeatAuditionController extends Controller
{
@ -46,28 +45,6 @@ class SeatAuditionController extends Controller
$entries->load('student.school');
foreach ($entries as $entry) {
$isDoubler = false;
if (array_key_exists($entry->student->id, $doublers)) {
$isDoubler = true;
$doubleData = [];
$doublerEntries = $doublers[$entry->student->id]['entries'];
foreach ($doublerEntries as $doublerEntry) {
$limits = $this->auditionService->getSeatingLimits($doublerEntry->audition);
$limits = $limits->reject(function ($lim) {
return $lim['limit'] === 0;
});
$doubleData[] = [
'audition' => $doublerEntry->audition,
'name' => $doublerEntry->audition->name,
'entryId' => $doublerEntry->id,
'rank' => $this->entryService->rankOfEntry('seating', $doublerEntry),
'limits' => $limits,
'status' => 'undecided', // Will be undecided, accepted, or declined
'unscored_in_audition' => $doublerEntry->audition->unscoredEntries()->count(),
];
}
}
$totalScoreColumn = 'No Score';
$fullyScored = false;
if ($entry->score_totals) {
@ -82,8 +59,7 @@ class SeatAuditionController extends Controller
'drawNumber' => $entry->draw_number,
'totalScore' => $totalScoreColumn,
'fullyScored' => $fullyScored,
'isDoubler' => $isDoubler,
'doubleData' => $doubleData ?? [],
'doubleData' => $this->doublerService->entryDoublerData($entry),
];
}

View File

@ -98,15 +98,15 @@ class AuditionService
$limits = SeatingLimit::all();
foreach ($limits as $limit) {
$lims[$limit->audition_id][$limit->ensemble_id] = collect([
$lims[$limit->audition_id][$limit->ensemble_id] = [
'ensemble' => $ensembles->find($limit->ensemble_id),
'limit' =>$limit->maximum_accepted,
]);
];
}
return collect($lims);
return $lims;
});
return collect($allLimits[$audition->id]) ?? [];
return $allLimits[$audition->id] ?? [];
}
protected function validateAudition($audition)

View File

@ -6,17 +6,29 @@ use App\Exceptions\TabulationException;
use App\Models\Entry;
use App\Models\Event;
use App\Models\Student;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Cache;
/**
class DoublerService
{
protected EntryService $entryService;
protected AuditionService $auditionService;
public function __construct(EntryService $entryService, AuditionService $auditionService)
{
$this->entryService = $entryService;
$this->auditionService = $auditionService;
}
/**
* returns a collection of doublers for the event in the form of
* [studentId => [student=>student, entries=>[entries]]
*/
class DoublerService
{
public function doublersForEvent(Event $event, string $mode = 'seating')
{
$cacheKey = 'event'.$event->id.'doublers-'.$mode;
return Cache::remember($cacheKey, 60, function () use ($event, $mode) {
return $this->findDoublersForEvent($event, $mode);
});
@ -25,7 +37,7 @@ class DoublerService
/**
* @throws TabulationException
*/
protected function findDoublersForEvent(Event $event, string $mode): array
protected function findDoublersForEvent(Event $event, string $mode = 'seating'): array
{
$this->validateEvent($event);
$entries = $event->entries;
@ -49,6 +61,59 @@ class DoublerService
return $doubler_array;
}
public function entryDoublerData(Entry $primaryEntry)
{
if (! isset($this->findDoublersForEvent($primaryEntry->audition->event)[$primaryEntry->student_id])) {
return false;
}
$entries = $this->findDoublersForEvent($primaryEntry->audition->event)[$primaryEntry->student_id]['entries'];
$entryData = collect([]);
/** @var Collection $entries */
foreach ($entries as $entry) {
$status = 'undecided';
if ($entry->hasFlag('declined')) {
$status = 'declined';
}
if ($entry->hasFlag('no_show')) {
$status = 'no_show';
}
$lims = $this->auditionService->getSeatingLimits($entry->audition);
$limits = [];
foreach ($lims as $lim) {
$limits[] = [
'ensemble_name' => $lim['ensemble']->name,
'accepts' => $lim['limit'],
'ensemble' => $lim['ensemble'],
];
}
$entryData[$entry->id] = [
'entry' => $entry,
'audition' => $entry->audition,
'auditionName' => $entry->audition->name,
'status' => $status,
'rank' => $this->entryService->rankOfEntry('seating', $entry),
'unscored_entries' => $entry->audition->unscoredEntries()->count(),
'seating_limits' => $limits,
];
}
// find out how many items in the collection $entryData have a status of 'undecided'
$undecided_count = $entryData->filter(fn ($entry) => $entry['status'] === 'undecided')->count();
// if $undecided_count is 1 set the item where status is 'undecided' to 'accepted'
if ($undecided_count === 1) {
$entryData->transform(function ($entry) {
if ($entry['status'] === 'undecided') {
$entry['status'] = 'accepted';
}
return $entry;
});
}
return $entryData;
}
/**
* @throws TabulationException
*/

View File

@ -40,6 +40,10 @@ class EntryService
{
$ranker = App::make(RankAuditionEntries::class);
$rankings = $ranker->rank($mode, $entry->audition);
$rankedEntry = $rankings->find($entry->id);
if (isset($rankedEntry->score_message)) {
return $rankedEntry->score_message;
}
return $rankings->find($entry->id)->rank ?? 'No Rank';
}
}

View File

@ -6,9 +6,8 @@
<li class="pb-2 pt-0 px-0 my-2 rounded-xl border border-gray-200 max-w-xs" x-data="{ open: {{ $isopen ? 'true':'false' }} }">
<div class="flex items-start gap-x-3 bg-gray-100 px-3 py-2 rounded-t-xl" >
<p class="text-sm font-semibold leading-6 text-gray-900">
{{-- TODO put in link --}}
<a href="{{ route('seating.audition', $double['audition']) }}">
{{ $double['name'] }} - {{ $double['status'] }}
{{ $double['auditionName'] }} - {{ $double['status'] }}
</a>
</p>
<div class="w-full flex justify-end" >
@ -21,26 +20,23 @@
<div class="grid grid-cols-4" x-show="open">
<div class="mt-1 px-3 text-xs leading-5 text-gray-500 col-span-3">
<ul>
<li class="flex items-center gap-x-2">
<li class="">
<p class="whitespace-nowrap">Ranked {{ $double['rank'] }}</p>
<svg viewBox="0 0 2 2" class="h-0.5 w-0.5 fill-current">
<circle cx="1" cy="1" r="1" />
</svg>
<p class="truncate">{{ $double['unscored_in_audition'] }} Unscored</p>
<p class="truncate">{{ $double['unscored_entries'] }} Unscored</p>
</li>
@foreach($double['limits'] as $limit)
<li>{{$limit['ensemble']->name}} accepts {{ $limit['limit'] }}</li>
@foreach($double['seating_limits'] as $limit)
<li>{{$limit['ensemble_name']}} accepts {{ $limit['accepts'] }}</li>
@endforeach
</ul>
</div>
<div class="flex flex-col justify-end gap-y-1 pt-1">
@if ($double['status'] == 'undecided')
<form method="POST" action="{{ route('doubler.accept',['entry'=>$double['entryId']]) }}">
<form method="POST" action="{{ route('doubler.accept',['entry'=>$double['entry']]) }}">
@csrf
<button class="{{ $doublerButtonClasses }}">Accept</button>
</form>
<form method="POST" action="{{ route('doubler.decline',['entry'=>$double['entryId']]) }}">
<form method="POST" action="{{ route('doubler.decline',['entry'=>$double['entry']]) }}">
@csrf
<button class="{{ $doublerButtonClasses }}">Decline</button>
</form>

View File

@ -26,7 +26,7 @@
<span class="text-xs text-gray-400">{{ $entry['schoolName'] }}</span>
</x-table.td>
<x-table.td class="!py-0">
@if($entry['isDoubler'])
@if($entry['doubleData'])
@include('tabulation.auditionSeating-doubler-block')
{{-- DOUBLER<br>--}}
{{-- @foreach($entry['doubleData'] as $double)--}}

View File

@ -23,38 +23,37 @@ it('throws an error if an invalid event is provided', function () {
it('returns doublers for an event', function () {
$concertEvent = Event::factory()->create(['name' => 'Concert Band', 'id' => 1000]);
$jazzEvent = Event::factory()->create(['name' => 'Jazz Band', 'id' => 1001]);
$auditionAS = Audition::factory()->create([
Audition::factory()->create([
'event_id' => 1000, 'name' => 'Alto Sax', 'minimum_grade' => 7, 'maximum_grade' => 12, 'id' => 1000,
]);
$auditionTS = Audition::factory()->create([
Audition::factory()->create([
'event_id' => 1000, 'name' => 'Tenor Sax', 'minimum_grade' => 7, 'maximum_grade' => 12, 'id' => 1001,
]);
$auditionBS = Audition::factory()->create([
Audition::factory()->create([
'event_id' => 1000, 'name' => 'Baritone Sax', 'minimum_grade' => 7, 'maximum_grade' => 12, 'id' => 1002,
]);
$auditionCL = Audition::factory()->create([
Audition::factory()->create([
'event_id' => 1000, 'name' => 'Clarinet', 'minimum_grade' => 7, 'maximum_grade' => 12, 'id' => 1003,
]);
$auditionBCL = Audition::factory()->create([
Audition::factory()->create([
'event_id' => 1000, 'name' => 'Bass Clarinet',
'minimum_grade' => 7, 'maximum_grade' => 12, 'id' => 1004,
]);
$auditionJAS = Audition::factory()->create([
Audition::factory()->create([
'event_id' => 1001, 'name' => 'Jazz Alto', 'minimum_grade' => 7,
'maximum_grade' => 12, 'id' => 1005,
]);
$auditionJTS = Audition::factory()->create([
Audition::factory()->create([
'event_id' => 1001, 'name' => 'Jazz Tenor', 'minimum_grade' => 7,
'maximum_grade' => 12, 'id' => 1006,
]);
$auditionJBS = Audition::factory()->create([
Audition::factory()->create([
'event_id' => 1001, 'name' => 'Jazz Baritone',
'minimum_grade' => 7, 'maximum_grade' => 12, 'id' => 1007,
]);
$allSaxDude = Student::factory()->create(['grade' => 11, 'id' => 1000]);
$clarinetGal = Student::factory()->create(['grade' => 9, 'id' => 1001]);
$justAlto = Student::factory()->create(['grade' => 9, 'id' => 1002]);
Student::factory()->create(['grade' => 9, 'id' => 1001]);
Student::factory()->create(['grade' => 9, 'id' => 1002]);
Entry::create(['student_id' => 1000, 'audition_id' => 1000]);
Entry::create(['student_id' => 1000, 'audition_id' => 1001]);
Entry::create(['student_id' => 1000, 'audition_id' => 1002]);
@ -67,13 +66,13 @@ it('returns doublers for an event', function () {
Entry::create(['student_id' => 1002, 'audition_id' => 1005]);
$return = $this->doublerService->doublersForEvent($concertEvent);
expect(count($return))->toBe(2);
expect($return[1000]['student']->id)->toBe($allSaxDude->id);
expect($return[1000]['entries']->count())->toBe(3);
expect($return[1001]['entries']->count())->toBe(2);
expect(count($return))->toBe(2)
->and($return[1000]['student']->id)->toBe($allSaxDude->id)
->and($return[1000]['entries']->count())->toBe(3)
->and($return[1001]['entries']->count())->toBe(2);
assertArrayNotHasKey(1002, $return);
$return = $this->doublerService->doublersForEvent($jazzEvent);
expect(count($return))->toBe(1);
expect($return[1000]['student']->id)->toBe($allSaxDude->id);
expect($return[1000]['entries']->count())->toBe(3);
expect(count($return))->toBe(1)
->and($return[1000]['student']->id)->toBe($allSaxDude->id)
->and($return[1000]['entries']->count())->toBe(3);
});