Compare commits
No commits in common. "master" and "meobda_nomination_ensemble" have entirely different histories.
master
...
meobda_nom
|
|
@ -20,6 +20,3 @@ yarn-error.log
|
||||||
/.vscode
|
/.vscode
|
||||||
/app/Http/Controllers/TestController.php
|
/app/Http/Controllers/TestController.php
|
||||||
/resources/views/test.blade.php
|
/resources/views/test.blade.php
|
||||||
/reports
|
|
||||||
/--cache-directory
|
|
||||||
/storage/debug.html
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
<component name="ProjectRunConfigurationManager">
|
<component name="ProjectRunConfigurationManager">
|
||||||
<configuration default="false" name="tests - parallel" type="PestRunConfigurationType">
|
<configuration default="false" name="tests - paralell" type="PestRunConfigurationType">
|
||||||
<option name="pestRunnerSettings">
|
<option name="pestRunnerSettings">
|
||||||
<PestRunner directory="$PROJECT_DIR$/tests" method="" options="--parallel --recreate-databases --coverage" />
|
<PestRunner directory="$PROJECT_DIR$/tests" method="" options="--parallel --recreate-databases" />
|
||||||
</option>
|
</option>
|
||||||
<option name="runnerSettings">
|
<option name="runnerSettings">
|
||||||
<PhpTestRunnerSettings directory="$PROJECT_DIR$/tests" method="" options="--parallel --recreate-databases --coverage" />
|
<PhpTestRunnerSettings directory="$PROJECT_DIR$/tests" method="" options="--parallel --recreate-databases" />
|
||||||
</option>
|
</option>
|
||||||
<method v="2" />
|
<method v="2" />
|
||||||
</configuration>
|
</configuration>
|
||||||
</component>
|
</component>
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Actions\Development;
|
|
||||||
|
|
||||||
use App\Actions\Tabulation\EnterScore;
|
|
||||||
use App\Models\Entry;
|
|
||||||
|
|
||||||
class FakeScoresForEntry
|
|
||||||
{
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public function __invoke(Entry $entry): void
|
|
||||||
{
|
|
||||||
$scoreScribe = app(EnterScore::class);
|
|
||||||
$scoringGuide = $entry->audition->scoringGuide;
|
|
||||||
$subscores = $scoringGuide->subscores;
|
|
||||||
$judges = $entry->audition->judges;
|
|
||||||
foreach ($judges as $judge) {
|
|
||||||
$scoringArray = [];
|
|
||||||
foreach ($subscores as $subscore) {
|
|
||||||
$scoringArray[$subscore->id] = mt_rand(0, $subscore->max_score);
|
|
||||||
}
|
|
||||||
$scoreScribe($judge, $entry, $scoringArray);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,39 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Actions\Draw;
|
|
||||||
|
|
||||||
use App\Models\Audition;
|
|
||||||
use Illuminate\Support\Collection;
|
|
||||||
use Illuminate\Support\Facades\DB;
|
|
||||||
|
|
||||||
use function auditionLog;
|
|
||||||
|
|
||||||
class ClearDraw
|
|
||||||
{
|
|
||||||
/** @codeCoverageIgnore */
|
|
||||||
public function __invoke(Audition|collection $auditions): void
|
|
||||||
{
|
|
||||||
if ($auditions instanceof Audition) {
|
|
||||||
$this->clearDraw($auditions);
|
|
||||||
}
|
|
||||||
if ($auditions instanceof Collection) {
|
|
||||||
$this->clearDraws($auditions);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function clearDraw(Audition $audition): void
|
|
||||||
{
|
|
||||||
$audition->removeFlag('drawn');
|
|
||||||
DB::table('entries')->where('audition_id', $audition->id)->update(['draw_number' => null]);
|
|
||||||
$message = 'Cleared draw for audition #'.$audition->id.' '.$audition->name;
|
|
||||||
$affected['auditions'] = [$audition->id];
|
|
||||||
auditionLog($message, $affected);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function clearDraws(Collection $auditions): void
|
|
||||||
{
|
|
||||||
foreach ($auditions as $audition) {
|
|
||||||
$this->clearDraw($audition);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,54 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Actions\Draw;
|
|
||||||
|
|
||||||
use App\Models\Audition;
|
|
||||||
use Illuminate\Support\Collection;
|
|
||||||
use Illuminate\Support\Facades\DB;
|
|
||||||
|
|
||||||
class RunDraw
|
|
||||||
{
|
|
||||||
public function __invoke(Audition|Collection $auditions): void
|
|
||||||
{
|
|
||||||
if ($auditions instanceof Audition) {
|
|
||||||
// Single audition, run draw directly
|
|
||||||
$this->runDraw($auditions);
|
|
||||||
|
|
||||||
return;
|
|
||||||
} elseif ($auditions instanceof Collection) {
|
|
||||||
$this->runDrawMultiple($auditions);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function runDraw(Audition $audition): void
|
|
||||||
{
|
|
||||||
// start off by clearing any existing draw numbers in the audition
|
|
||||||
DB::table('entries')->where('audition_id', $audition->id)->update(['draw_number' => null]);
|
|
||||||
|
|
||||||
$randomizedEntries = $audition->entries->shuffle();
|
|
||||||
|
|
||||||
// Move entries flagged as no show to the end
|
|
||||||
[$noShowEntries, $otherEntries] = $randomizedEntries->partition(function ($entry) {
|
|
||||||
return $entry->hasFlag('no_show');
|
|
||||||
});
|
|
||||||
$randomizedEntries = $otherEntries->merge($noShowEntries);
|
|
||||||
|
|
||||||
// Save draw numbers back to the entries\
|
|
||||||
$nextNumber = 1;
|
|
||||||
foreach ($randomizedEntries as $index => $entry) {
|
|
||||||
$entry->update(['draw_number' => $nextNumber]);
|
|
||||||
$nextNumber++;
|
|
||||||
}
|
|
||||||
$audition->addFlag('drawn');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function runDrawMultiple(Collection $auditions): void
|
|
||||||
{
|
|
||||||
// Eager load the 'entries' relationship on all auditions if not already loaded
|
|
||||||
$auditions->loadMissing('entries');
|
|
||||||
|
|
||||||
$auditions->each(fn ($audition) => $this->runDraw($audition));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -2,12 +2,14 @@
|
||||||
|
|
||||||
namespace App\Actions\Entries;
|
namespace App\Actions\Entries;
|
||||||
|
|
||||||
use App\Exceptions\AuditionAdminException;
|
|
||||||
use App\Exceptions\ManageEntryException;
|
use App\Exceptions\ManageEntryException;
|
||||||
use App\Models\Audition;
|
use App\Models\Audition;
|
||||||
|
use App\Models\AuditLogEntry;
|
||||||
use App\Models\Entry;
|
use App\Models\Entry;
|
||||||
use App\Models\Student;
|
use App\Models\Student;
|
||||||
|
|
||||||
|
use function auth;
|
||||||
|
|
||||||
class CreateEntry
|
class CreateEntry
|
||||||
{
|
{
|
||||||
public function __construct()
|
public function __construct()
|
||||||
|
|
@ -17,49 +19,49 @@ class CreateEntry
|
||||||
/**
|
/**
|
||||||
* @throws ManageEntryException
|
* @throws ManageEntryException
|
||||||
*/
|
*/
|
||||||
public function __invoke(
|
public function __invoke(Student|int $student, Audition|int $audition, string|array|null $entry_for = null)
|
||||||
Student|int $student,
|
{
|
||||||
Audition|int $audition,
|
return $this->createEntry($student, $audition, $entry_for);
|
||||||
$for_seating = false,
|
|
||||||
$for_advancement = false,
|
|
||||||
$late_fee_waived = false
|
|
||||||
) {
|
|
||||||
return $this->createEntry($student, $audition, $for_seating, $for_advancement, $late_fee_waived);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws ManageEntryException
|
* @throws ManageEntryException
|
||||||
*/
|
*/
|
||||||
public function createEntry(
|
public function createEntry(Student|int $student, Audition|int $audition, string|array|null $entry_for = null)
|
||||||
Student|int $student,
|
{
|
||||||
Audition|int $audition,
|
|
||||||
$for_seating = false,
|
|
||||||
$for_advancement = false,
|
|
||||||
$late_fee_waived = false
|
|
||||||
): Entry {
|
|
||||||
if (is_int($student)) {
|
if (is_int($student)) {
|
||||||
$student = Student::find($student);
|
$student = Student::find($student);
|
||||||
}
|
}
|
||||||
if (is_int($audition)) {
|
if (is_int($audition)) {
|
||||||
$audition = Audition::find($audition);
|
$audition = Audition::find($audition);
|
||||||
}
|
}
|
||||||
$this->verifySubmission($student, $audition);
|
|
||||||
if (! $for_advancement && ! $for_seating) {
|
if (! $entry_for) {
|
||||||
$for_seating = true;
|
$entry_for = ['seating', 'advancement'];
|
||||||
$for_advancement = true;
|
|
||||||
}
|
}
|
||||||
|
$entry_for = collect($entry_for);
|
||||||
|
$this->verifySubmission($student, $audition);
|
||||||
$entry = Entry::make([
|
$entry = Entry::make([
|
||||||
'student_id' => $student->id,
|
'student_id' => $student->id,
|
||||||
'audition_id' => $audition->id,
|
'audition_id' => $audition->id,
|
||||||
'draw_number' => $this->checkDraw($audition),
|
'draw_number' => $this->checkDraw($audition),
|
||||||
'for_seating' => $for_seating,
|
'for_seating' => $entry_for->contains('seating'),
|
||||||
'for_advancement' => $for_advancement,
|
'for_advancement' => $entry_for->contains('advancement'),
|
||||||
]);
|
]);
|
||||||
$entry->save();
|
$entry->save();
|
||||||
|
if (auth()->user()) {
|
||||||
if ($late_fee_waived) {
|
$message = 'Entered '.$entry->student->full_name().' from '.$entry->student->school->name.' in '.$entry->audition->name.'.';
|
||||||
$entry->addFlag('late_fee_waived');
|
AuditLogEntry::create([
|
||||||
$entry->refresh();
|
'user' => auth()->user()->email,
|
||||||
|
'ip_address' => request()->ip(),
|
||||||
|
'message' => $message,
|
||||||
|
'affected' => [
|
||||||
|
'entries' => [$entry->id],
|
||||||
|
'students' => [$entry->student_id],
|
||||||
|
'auditions' => [$entry->audition_id],
|
||||||
|
'schools' => [$entry->student->school_id],
|
||||||
|
],
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $entry;
|
return $entry;
|
||||||
|
|
@ -81,29 +83,29 @@ class CreateEntry
|
||||||
{
|
{
|
||||||
// Make sure it's a valid student
|
// Make sure it's a valid student
|
||||||
if (! $student || ! $student->exists()) {
|
if (! $student || ! $student->exists()) {
|
||||||
throw new AuditionAdminException('Invalid student provided');
|
throw new ManageEntryException('Invalid student provided');
|
||||||
}
|
}
|
||||||
// Make sure the audition is valid
|
// Make sure the audition is valid
|
||||||
if (! $audition || ! $audition->exists()) {
|
if (! $audition || ! $audition->exists()) {
|
||||||
throw new AuditionAdminException('Invalid audition provided');
|
throw new ManageEntryException('Invalid audition provided');
|
||||||
}
|
}
|
||||||
// A student can't enter the same audition twice
|
// A student can't enter the same audition twice
|
||||||
if (Entry::where('student_id', $student->id)->where('audition_id', $audition->id)->exists()) {
|
if (Entry::where('student_id', $student->id)->where('audition_id', $audition->id)->exists()) {
|
||||||
throw new AuditionAdminException('That student is already entered in that audition');
|
throw new ManageEntryException('That student is already entered in that audition');
|
||||||
}
|
}
|
||||||
// Can't enter a published audition
|
// Can't enter a published audition
|
||||||
if ($audition->hasFlag('seats_published')) {
|
if ($audition->hasFlag('seats_published')) {
|
||||||
throw new AuditionAdminException('Cannot add an entry to an audition where seats are published');
|
throw new ManageEntryException('Cannot add an entry to an audition where seats are published');
|
||||||
}
|
}
|
||||||
if ($audition->hasFlag('advancement_published')) {
|
if ($audition->hasFlag('advancement_published')) {
|
||||||
throw new AuditionAdminException('Cannot add an entry to an audition where advancement is published');
|
throw new ManageEntryException('Cannot add an entry to an audition where advancement is published');
|
||||||
}
|
}
|
||||||
// Verify the grade of the student is in range for the audition
|
// Verify the grade of the student is in range for the audition
|
||||||
if ($student->grade > $audition->maximum_grade) {
|
if ($student->grade > $audition->maximum_grade) {
|
||||||
throw new AuditionAdminException('The grade of the student exceeds the maximum for that audition');
|
throw new ManageEntryException('The grade of the student exceeds the maximum for that audition');
|
||||||
}
|
}
|
||||||
if ($student->grade < $audition->minimum_grade) {
|
if ($student->grade < $audition->minimum_grade) {
|
||||||
throw new AuditionAdminException('The grade of the student does not meet the minimum for that audition');
|
throw new ManageEntryException('The grade of the student does not meet the minimum for that audition');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,19 @@
|
||||||
namespace App\Actions\Entries;
|
namespace App\Actions\Entries;
|
||||||
|
|
||||||
use App\Exceptions\AuditionAdminException;
|
use App\Exceptions\AuditionAdminException;
|
||||||
use App\Models\Doubler;
|
|
||||||
use App\Models\Entry;
|
use App\Models\Entry;
|
||||||
|
use App\Services\DoublerService;
|
||||||
use Illuminate\Support\Facades\Cache;
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
|
||||||
class DoublerDecision
|
class DoublerDecision
|
||||||
{
|
{
|
||||||
|
protected DoublerService $doublerService;
|
||||||
|
|
||||||
|
public function __construct(DoublerService $doublerService)
|
||||||
|
{
|
||||||
|
$this->doublerService = $doublerService;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws AuditionAdminException
|
* @throws AuditionAdminException
|
||||||
*/
|
*/
|
||||||
|
|
@ -27,45 +34,25 @@ class DoublerDecision
|
||||||
'decline' => $this->decline($entry),
|
'decline' => $this->decline($entry),
|
||||||
default => throw new AuditionAdminException('Invalid decision specified')
|
default => throw new AuditionAdminException('Invalid decision specified')
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if ($decision != 'accept' && $decision != 'decline') {
|
||||||
|
throw new AuditionAdminException('Invalid decision specified');
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Accepts an entry for the given audition.
|
|
||||||
*
|
|
||||||
* This method ensures the entry is not already declined, and that the
|
|
||||||
* audition is not in a state where seats or advancement are published.
|
|
||||||
* If the entry is already declined, this method does nothing.
|
|
||||||
* If the audition is in a state where seats or advancement are published,
|
|
||||||
* this method throws an exception.
|
|
||||||
*
|
|
||||||
* This method also declines all other entries in the same audition,
|
|
||||||
* clearing the rank cache for the audition.
|
|
||||||
*
|
|
||||||
* @throws AuditionAdminException
|
|
||||||
*/
|
|
||||||
public function accept(Entry $entry): void
|
public function accept(Entry $entry): void
|
||||||
{
|
{
|
||||||
if ($entry->hasFlag('declined')) {
|
Cache::forget('audition'.$entry->audition_id.'seating');
|
||||||
throw new AuditionAdminException('Entry '.$entry->id.' is already declined');
|
Cache::forget('audition'.$entry->audition_id.'advancement');
|
||||||
}
|
|
||||||
if ($entry->audition->hasFlag('seats_published')) {
|
|
||||||
throw new AuditionAdminException('Cannot accept an entry in an audition where seats are published');
|
|
||||||
}
|
|
||||||
Cache::forget('rank_seating_'.$entry->audition_id);
|
|
||||||
|
|
||||||
// Process student entries
|
// Decline all other entries and clear rank cache
|
||||||
$doublerData = Doubler::findDoubler($entry->student_id, $entry->audition->event_id);
|
$doublerInfo = $this->doublerService->simpleDoubleInfo($entry);
|
||||||
// Check each entry and see if it is unscored. We can't accept this entry if that is the case.
|
foreach ($doublerInfo as $doublerEntry) {
|
||||||
foreach ($doublerData->entries() as $doublerEntry) {
|
Cache::forget('audition'.$doublerEntry->audition_id.'seating');
|
||||||
if (! $doublerEntry->totalScore && ! $doublerEntry->hasFlag('declined') && ! $doublerEntry->hasFlag('no_show') && ! $doublerEntry->hasFlag('failed_prelim')) {
|
/** @var Entry $doublerEntry */
|
||||||
throw new AuditionAdminException('Cannot accept seating for '.$entry->student->full_name().' because student has unscored entries');
|
if ($doublerEntry->id !== $entry->id) {
|
||||||
}
|
$doublerEntry->addFlag('declined');
|
||||||
}
|
|
||||||
// Decline all other entries
|
|
||||||
foreach ($doublerData->entries() as $doublerEntry) {
|
|
||||||
Cache::forget('rank_seating_'.$doublerEntry->audition_id);
|
|
||||||
if ($doublerEntry->id !== $entry->id && ! $doublerEntry->hasFlag('no_show') && ! $doublerEntry->hasFlag('failed_prelim') && ! $doublerEntry->hasFlag('declined')) {
|
|
||||||
$this->decline($doublerEntry);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -75,21 +62,12 @@ class DoublerDecision
|
||||||
*/
|
*/
|
||||||
public function decline($entry): void
|
public function decline($entry): void
|
||||||
{
|
{
|
||||||
// Entry cannot decline a seat twice
|
Cache::forget('audition'.$entry->audition_id.'seating');
|
||||||
|
Cache::forget('audition'.$entry->audition_id.'advancement');
|
||||||
if ($entry->hasFlag('declined')) {
|
if ($entry->hasFlag('declined')) {
|
||||||
throw new AuditionAdminException('Entry '.$entry->id.' is already declined');
|
throw new AuditionAdminException('Entry is already declined');
|
||||||
}
|
}
|
||||||
if (! $entry->totalScore) {
|
Cache::forget('audition'.$entry->audition_id.'seating');
|
||||||
throw new AuditionAdminException('Cannot decline an unscored entry');
|
|
||||||
}
|
|
||||||
if ($entry->audition->hasFlag('seats_published')) {
|
|
||||||
throw new AuditionAdminException('Cannot decline an entry in an audition where seats are published');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Flag this entry
|
|
||||||
$entry->addFlag('declined');
|
$entry->addFlag('declined');
|
||||||
|
|
||||||
// Clear rank cache
|
|
||||||
Cache::forget('rank_seating_'.$entry->audition_id);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions\Entries;
|
||||||
|
|
||||||
|
use App\Models\Entry;
|
||||||
|
use App\Models\Seat;
|
||||||
|
|
||||||
|
class GetEntrySeatingResult
|
||||||
|
{
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __invoke(Entry $entry): string
|
||||||
|
{
|
||||||
|
return $this->getResult($entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getResult(Entry $entry): string
|
||||||
|
{
|
||||||
|
if ($entry->hasFlag('failed_prelim')) {
|
||||||
|
return 'Failed Prelim';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($entry->hasFlag('no_show')) {
|
||||||
|
return 'No Show';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($entry->hasFlag('declined')) {
|
||||||
|
return 'Declined';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($entry->hasFlag('failed_prelim')) {
|
||||||
|
return 'Did not pass prelim';
|
||||||
|
}
|
||||||
|
|
||||||
|
$seat = Seat::where('entry_id', $entry->id)->first();
|
||||||
|
if ($seat) {
|
||||||
|
return $seat->ensemble->name.' '.$seat->seat;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'Entry not seated';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
namespace App\Actions\Entries;
|
namespace App\Actions\Entries;
|
||||||
|
|
||||||
use App\Exceptions\AuditionAdminException;
|
|
||||||
use App\Exceptions\ManageEntryException;
|
use App\Exceptions\ManageEntryException;
|
||||||
use App\Models\Audition;
|
use App\Models\Audition;
|
||||||
use App\Models\AuditLogEntry;
|
use App\Models\AuditLogEntry;
|
||||||
|
|
@ -28,7 +27,7 @@ class UpdateEntry
|
||||||
/**
|
/**
|
||||||
* @throws ManageEntryException
|
* @throws ManageEntryException
|
||||||
*/
|
*/
|
||||||
public function __invoke(Entry|int $entry, array $updateData): void
|
public function __invoke(Entry $entry, array $updateData): void
|
||||||
{
|
{
|
||||||
$this->updateEntry($entry, $updateData);
|
$this->updateEntry($entry, $updateData);
|
||||||
}
|
}
|
||||||
|
|
@ -42,7 +41,7 @@ class UpdateEntry
|
||||||
$entry = Entry::find($entry);
|
$entry = Entry::find($entry);
|
||||||
}
|
}
|
||||||
if (! $entry || ! $entry->exists) {
|
if (! $entry || ! $entry->exists) {
|
||||||
throw new AuditionAdminException('Invalid entry provided');
|
throw new ManageEntryException('Invalid entry provided');
|
||||||
}
|
}
|
||||||
$this->entry = $entry;
|
$this->entry = $entry;
|
||||||
if (array_key_exists('for_seating', $updateData)) {
|
if (array_key_exists('for_seating', $updateData)) {
|
||||||
|
|
@ -82,34 +81,34 @@ class UpdateEntry
|
||||||
$audition = Audition::find($audition);
|
$audition = Audition::find($audition);
|
||||||
}
|
}
|
||||||
if (! $audition || ! $audition->exists) {
|
if (! $audition || ! $audition->exists) {
|
||||||
throw new AuditionAdminException('Invalid audition provided');
|
throw new ManageEntryException('Invalid audition provided');
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->entry->audition->hasFlag('seats_published')) {
|
if ($this->entry->audition->hasFlag('seats_published')) {
|
||||||
throw new AuditionAdminException('Cannot change the audition for an entry where seating for that entry\'s current audition is published');
|
throw new ManageEntryException('Cannot change the audition for an entry where seating for that entry\'s current audition is published');
|
||||||
}
|
}
|
||||||
if ($this->entry->audition->hasFlag('advancement_published')) {
|
if ($this->entry->audition->hasFlag('advancement_published')) {
|
||||||
throw new AuditionAdminException('Cannot change the audition for an entry where advancement for that entry\'s current audition is published');
|
throw new ManageEntryException('Cannot change the audition for an entry where advancement for that entry\'s current audition is published');
|
||||||
}
|
}
|
||||||
if ($audition->hasFlag('seats_published')) {
|
if ($audition->hasFlag('seats_published')) {
|
||||||
throw new AuditionAdminException('Cannot change the entry to an audition with published seating');
|
throw new ManageEntryException('Cannot change the entry to an audition with published seating');
|
||||||
}
|
}
|
||||||
if ($audition->hasFlag('advancement_published')) {
|
if ($audition->hasFlag('advancement_published')) {
|
||||||
throw new AuditionAdminException('Cannot change the entry to an audition with published advancement');
|
throw new ManageEntryException('Cannot change the entry to an audition with published advancement');
|
||||||
}
|
}
|
||||||
if ($this->entry->student->grade > $audition->maximum_grade) {
|
if ($this->entry->student->grade > $audition->maximum_grade) {
|
||||||
throw new AuditionAdminException('The student is too old to enter that audition');
|
throw new ManageEntryException('The grade of the student exceeds the maximum for that audition');
|
||||||
}
|
}
|
||||||
if ($this->entry->student->grade < $audition->minimum_grade) {
|
if ($this->entry->student->grade < $audition->minimum_grade) {
|
||||||
throw new AuditionAdminException('The student is too young to enter that audition');
|
throw new ManageEntryException('The grade of the student does not meet the minimum for that audition');
|
||||||
}
|
}
|
||||||
if ($this->entry->scoreSheets()->count() > 0) {
|
if ($this->entry->scoreSheets()->count() > 0) {
|
||||||
throw new AuditionAdminException('Cannot change the audition for an entry with scores');
|
throw new ManageEntryException('Cannot change the audition for an entry with scores');
|
||||||
}
|
}
|
||||||
if ($audition->id !== $this->entry->audition_id &&
|
if ($audition->id !== $this->entry->audition_id &&
|
||||||
Entry::where('student_id', $this->entry->student_id)
|
Entry::where('student_id', $this->entry->student_id)
|
||||||
->where('audition_id', $audition->id)->exists()) {
|
->where('audition_id', $audition->id)->exists()) {
|
||||||
throw new AuditionAdminException('That student is already entered in that audition');
|
throw new ManageEntryException('That student is already entered in that audition');
|
||||||
}
|
}
|
||||||
// Escape if we're not actually making a change
|
// Escape if we're not actually making a change
|
||||||
if ($this->entry->audition_id == $audition->id) {
|
if ($this->entry->audition_id == $audition->id) {
|
||||||
|
|
@ -140,13 +139,13 @@ class UpdateEntry
|
||||||
}
|
}
|
||||||
if ($forSeating) {
|
if ($forSeating) {
|
||||||
if ($this->entry->audition->hasFlag('seats_published')) {
|
if ($this->entry->audition->hasFlag('seats_published')) {
|
||||||
throw new AuditionAdminException('Cannot add seating to an entry in an audition where seats are published');
|
throw new ManageEntryException('Cannot add seating to an entry in an audition where seats are published');
|
||||||
}
|
}
|
||||||
$this->entry->for_seating = 1;
|
$this->entry->for_seating = 1;
|
||||||
$this->log_message .= 'Entry '.$this->entry->id.' is entered for seating '.'<br>';
|
$this->log_message .= 'Entry '.$this->entry->id.' is entered for seating '.'<br>';
|
||||||
} else {
|
} else {
|
||||||
if ($this->entry->audition->hasFlag('seats_published')) {
|
if ($this->entry->audition->hasFlag('seats_published')) {
|
||||||
throw new AuditionAdminException('Cannot remove seating from an entry in an audition where seats are published');
|
throw new ManageEntryException('Cannot remove seating from an entry in an audition where seats are published');
|
||||||
}
|
}
|
||||||
$this->entry->for_seating = 0;
|
$this->entry->for_seating = 0;
|
||||||
$this->log_message .= 'Entry '.$this->entry->id.' is NOT entered for seating '.'<br>';
|
$this->log_message .= 'Entry '.$this->entry->id.' is NOT entered for seating '.'<br>';
|
||||||
|
|
@ -163,13 +162,13 @@ class UpdateEntry
|
||||||
}
|
}
|
||||||
if ($forAdvancement) {
|
if ($forAdvancement) {
|
||||||
if ($this->entry->audition->hasFlag('advancement_published')) {
|
if ($this->entry->audition->hasFlag('advancement_published')) {
|
||||||
throw new AuditionAdminException('Cannot add advancement to an entry in an audition where advancement is published');
|
throw new ManageEntryException('Cannot add advancement to an entry in an audition where advancement is published');
|
||||||
}
|
}
|
||||||
$this->entry->for_advancement = 1;
|
$this->entry->for_advancement = 1;
|
||||||
$this->log_message .= 'Entry '.$this->entry->id.' is entered for '.auditionSetting('advanceTo').'<br>';
|
$this->log_message .= 'Entry '.$this->entry->id.' is entered for '.auditionSetting('advanceTo').'<br>';
|
||||||
} else {
|
} else {
|
||||||
if ($this->entry->audition->hasFlag('advancement_published')) {
|
if ($this->entry->audition->hasFlag('advancement_published')) {
|
||||||
throw new AuditionAdminException('Cannot remove advancement from an entry in an audition where advancement is published');
|
throw new ManageEntryException('Cannot remove advancement from an entry in an audition where advancement is published');
|
||||||
}
|
}
|
||||||
$this->entry->for_advancement = 0;
|
$this->entry->for_advancement = 0;
|
||||||
$this->log_message .= 'Entry '.$this->entry->id.' is NOT entered for '.auditionSetting('advanceTo').'<br>';
|
$this->log_message .= 'Entry '.$this->entry->id.' is NOT entered for '.auditionSetting('advanceTo').'<br>';
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
namespace App\Actions\Fortify;
|
namespace App\Actions\Fortify;
|
||||||
|
|
||||||
|
use App\Models\AuditLogEntry;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use App\Rules\ValidRegistrationCode;
|
use App\Rules\ValidRegistrationCode;
|
||||||
use Illuminate\Support\Facades\Hash;
|
use Illuminate\Support\Facades\Hash;
|
||||||
|
|
@ -52,6 +53,17 @@ class CreateNewUser implements CreatesNewUsers
|
||||||
'password' => Hash::make($input['password']),
|
'password' => Hash::make($input['password']),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
$message = 'New User Registered - '.$input['email']
|
||||||
|
.'<br>Name: '.$input['first_name'].' '.$input['last_name']
|
||||||
|
.'<br>Judging Pref: '.$input['judging_preference']
|
||||||
|
.'<br>Cell Phone: '.$input['cell_phone'];
|
||||||
|
AuditLogEntry::create([
|
||||||
|
'user' => $input['email'],
|
||||||
|
'ip_address' => request()->ip(),
|
||||||
|
'message' => $message,
|
||||||
|
'affected' => ['users' => $user->id],
|
||||||
|
]);
|
||||||
|
|
||||||
return $user;
|
return $user;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
namespace App\Actions\Fortify;
|
namespace App\Actions\Fortify;
|
||||||
|
|
||||||
|
use App\Models\AuditLogEntry;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use Illuminate\Support\Facades\Hash;
|
use Illuminate\Support\Facades\Hash;
|
||||||
use Illuminate\Support\Facades\Validator;
|
use Illuminate\Support\Facades\Validator;
|
||||||
|
|
@ -25,6 +26,11 @@ class ResetUserPassword implements ResetsUserPasswords
|
||||||
$user->forceFill([
|
$user->forceFill([
|
||||||
'password' => Hash::make($input['password']),
|
'password' => Hash::make($input['password']),
|
||||||
])->save();
|
])->save();
|
||||||
|
AuditLogEntry::create([
|
||||||
|
'user' => $user->email,
|
||||||
|
'ip_address' => request()->ip(),
|
||||||
|
'message' => 'Reset Password',
|
||||||
|
'affected' => ['users' => [$user->id]],
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,55 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Actions\Fortify;
|
|
||||||
|
|
||||||
use App\Exceptions\AuditionAdminException;
|
|
||||||
use App\Models\User;
|
|
||||||
|
|
||||||
class UpdateUserPrivileges
|
|
||||||
{
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @throws AuditionAdminException
|
|
||||||
*/
|
|
||||||
public function __invoke(User|int $user, string $action, string $privilege): void
|
|
||||||
{
|
|
||||||
$this->setPrivilege($user, $action, $privilege);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @throws AuditionAdminException
|
|
||||||
*/
|
|
||||||
public function setPrivilege(User|int $user, string $action, string $privilege): void
|
|
||||||
{
|
|
||||||
if (is_int($user)) {
|
|
||||||
$user = User::findOrFail($user);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! User::where('id', $user->id)->exists()) {
|
|
||||||
throw new AuditionAdminException('User does not exist');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! in_array($action, ['grant', 'revoke'])) {
|
|
||||||
throw new AuditionAdminException('Invalid Action');
|
|
||||||
}
|
|
||||||
|
|
||||||
$field = match ($privilege) {
|
|
||||||
'admin' => 'is_admin',
|
|
||||||
'tab' => 'is_tab',
|
|
||||||
default => throw new AuditionAdminException('Invalid Privilege'),
|
|
||||||
};
|
|
||||||
|
|
||||||
if ($user->$field == 1 && $action == 'revoke') {
|
|
||||||
$user->$field = 0;
|
|
||||||
$user->save();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($user->$field == 0 && $action == 'grant') {
|
|
||||||
$user->$field = 1;
|
|
||||||
$user->save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
namespace App\Actions\Fortify;
|
namespace App\Actions\Fortify;
|
||||||
|
|
||||||
|
use App\Models\AuditLogEntry;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use Illuminate\Contracts\Auth\MustVerifyEmail;
|
use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||||
use Illuminate\Support\Facades\Validator;
|
use Illuminate\Support\Facades\Validator;
|
||||||
|
|
@ -44,7 +45,16 @@ class UpdateUserProfileInformation implements UpdatesUserProfileInformation
|
||||||
'email' => $input['email'],
|
'email' => $input['email'],
|
||||||
])->save();
|
])->save();
|
||||||
}
|
}
|
||||||
|
$message = 'Updated user #'.$user->id.' - '.$user->email
|
||||||
|
.'<br>Name: '.$user->full_name()
|
||||||
|
.'<br>Judging Pref: '.$user->judging_preference
|
||||||
|
.'<br>Cell Phone: '.$user->cell_phone;
|
||||||
|
AuditLogEntry::create([
|
||||||
|
'user' => auth()->user()->email,
|
||||||
|
'ip_address' => request()->ip(),
|
||||||
|
'message' => $message,
|
||||||
|
'affected' => ['users' => [$user->id]],
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -64,6 +74,17 @@ class UpdateUserProfileInformation implements UpdatesUserProfileInformation
|
||||||
'email_verified_at' => null,
|
'email_verified_at' => null,
|
||||||
])->save();
|
])->save();
|
||||||
$user->refresh();
|
$user->refresh();
|
||||||
|
$message = 'Updated user #'.$user->id.' - '.$oldEmail
|
||||||
|
.'<br>Name: '.$user->full_name()
|
||||||
|
.'<br>Email: '.$user->email
|
||||||
|
.'<br>Judging Pref: '.$user->judging_preference
|
||||||
|
.'<br>Cell Phone: '.$user->cell_phone;
|
||||||
|
AuditLogEntry::create([
|
||||||
|
'user' => auth()->user()->email,
|
||||||
|
'ip_address' => request()->ip(),
|
||||||
|
'message' => $message,
|
||||||
|
'affected' => ['users' => [$user->id]],
|
||||||
|
]);
|
||||||
|
|
||||||
$user->sendEmailVerificationNotification();
|
$user->sendEmailVerificationNotification();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,6 @@ use App\Models\Entry;
|
||||||
use App\Models\Room;
|
use App\Models\Room;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
|
|
||||||
/**
|
|
||||||
* @codeCoverageIgnore
|
|
||||||
*/
|
|
||||||
// TODO figure out testing for PrintSignInSheets
|
|
||||||
class PrintSignInSheets
|
class PrintSignInSheets
|
||||||
{
|
{
|
||||||
protected $pdf;
|
protected $pdf;
|
||||||
|
|
@ -78,15 +74,11 @@ class PrintSignInSheets
|
||||||
|
|
||||||
public function addEntryRow(Entry $entry)
|
public function addEntryRow(Entry $entry)
|
||||||
{
|
{
|
||||||
$nameLine = $entry->student->full_name();
|
|
||||||
if ($entry->student->isDoublerInEvent($entry->audition->event_id)) {
|
|
||||||
$nameLine .= ' (D)';
|
|
||||||
}
|
|
||||||
$this->pdf->Cell($this->columnWidth['id'], $this->bodyRowHeight, $entry->id, 1, 0, 'L');
|
$this->pdf->Cell($this->columnWidth['id'], $this->bodyRowHeight, $entry->id, 1, 0, 'L');
|
||||||
$this->pdf->Cell($this->columnWidth['instrument'], $this->bodyRowHeight, $entry->audition->name, 1, 0,
|
$this->pdf->Cell($this->columnWidth['instrument'], $this->bodyRowHeight, $entry->audition->name, 1, 0,
|
||||||
'L');
|
'L');
|
||||||
$this->pdf->Cell($this->columnWidth['drawNumber'], $this->bodyRowHeight, $entry->draw_number, 1, 0, 'L');
|
$this->pdf->Cell($this->columnWidth['drawNumber'], $this->bodyRowHeight, $entry->draw_number, 1, 0, 'L');
|
||||||
$this->pdf->Cell($this->columnWidth['name'], $this->bodyRowHeight, $nameLine, 1, 0, 'L');
|
$this->pdf->Cell($this->columnWidth['name'], $this->bodyRowHeight, $entry->student->full_name(), 1, 0, 'L');
|
||||||
$this->pdf->Cell($this->columnWidth['school'], $this->bodyRowHeight, $entry->student->school->name, 1, 0, 'L');
|
$this->pdf->Cell($this->columnWidth['school'], $this->bodyRowHeight, $entry->student->school->name, 1, 0, 'L');
|
||||||
$this->pdf->Cell(0, $this->bodyRowHeight, ' ', 1, 1, 'L');
|
$this->pdf->Cell(0, $this->bodyRowHeight, ' ', 1, 1, 'L');
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,6 @@ namespace App\Actions\Print;
|
||||||
use App\Models\Ensemble;
|
use App\Models\Ensemble;
|
||||||
use Codedge\Fpdf\Fpdf\Fpdf;
|
use Codedge\Fpdf\Fpdf\Fpdf;
|
||||||
|
|
||||||
/**
|
|
||||||
* @codeCoverageIgnore
|
|
||||||
*/
|
|
||||||
// TODO figure out testing for PrintStandNameTags
|
|
||||||
class PrintStandNameTags
|
class PrintStandNameTags
|
||||||
{
|
{
|
||||||
public function __construct()
|
public function __construct()
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,6 @@ use Illuminate\Support\Collection;
|
||||||
|
|
||||||
use function auditionSetting;
|
use function auditionSetting;
|
||||||
|
|
||||||
/**
|
|
||||||
* @codeCoverageIgnore
|
|
||||||
*/
|
|
||||||
// TODO figure out testing for QuarterPageCards
|
|
||||||
class QuarterPageCards implements PrintCards
|
class QuarterPageCards implements PrintCards
|
||||||
{
|
{
|
||||||
protected $pdf;
|
protected $pdf;
|
||||||
|
|
@ -58,37 +54,17 @@ class QuarterPageCards implements PrintCards
|
||||||
$this->pdf->Cell(4.5, .5, $entry->audition->name.' #'.$entry->draw_number);
|
$this->pdf->Cell(4.5, .5, $entry->audition->name.' #'.$entry->draw_number);
|
||||||
|
|
||||||
// Fill in student information
|
// Fill in student information
|
||||||
$nameLine = $entry->student->full_name();
|
|
||||||
if ($entry->student->isDoublerInEvent($entry->audition->event_id)) {
|
|
||||||
$nameLine .= ' (D)';
|
|
||||||
}
|
|
||||||
$this->pdf->SetFont('Arial', '', 10);
|
$this->pdf->SetFont('Arial', '', 10);
|
||||||
$xLoc = $this->offset[$this->quadOn][0] + 1;
|
$xLoc = $this->offset[$this->quadOn][0] + 1;
|
||||||
$yLoc = $this->offset[$this->quadOn][1] + 3.1;
|
$yLoc = $this->offset[$this->quadOn][1] + 3.1;
|
||||||
$this->pdf->setXY($xLoc, $yLoc);
|
$this->pdf->setXY($xLoc, $yLoc);
|
||||||
$this->pdf->Cell(4.5, .25, $nameLine);
|
$this->pdf->Cell(4.5, .25, $entry->student->full_name());
|
||||||
$this->pdf->setXY($xLoc, $yLoc + .25);
|
$this->pdf->setXY($xLoc, $yLoc + .25);
|
||||||
$this->pdf->Cell(4.5, .25, $entry->student->school->name);
|
$this->pdf->Cell(4.5, .25, $entry->student->school->name);
|
||||||
$this->pdf->setXY($xLoc, $yLoc + .5);
|
$this->pdf->setXY($xLoc, $yLoc + .5);
|
||||||
if (! is_null($entry->audition->room_id)) {
|
if (! is_null($entry->audition->room_id)) {
|
||||||
$this->pdf->Cell(4.5, .25, $entry->audition->room->name);
|
$this->pdf->Cell(4.5, .25, $entry->audition->room->name);
|
||||||
}
|
}
|
||||||
if (auditionSetting('advanceTo')) {
|
|
||||||
$as = false;
|
|
||||||
$this->pdf->setXY($xLoc, $yLoc - 1);
|
|
||||||
$auditioningFor = 'Auditioning for: ';
|
|
||||||
if ($entry->for_seating) {
|
|
||||||
$auditioningFor .= auditionSetting('auditionAbbreviation');
|
|
||||||
$as = true;
|
|
||||||
}
|
|
||||||
if ($entry->for_advancement) {
|
|
||||||
if ($as) {
|
|
||||||
$auditioningFor .= ' / ';
|
|
||||||
}
|
|
||||||
$auditioningFor .= auditionSetting('advanceTo');
|
|
||||||
}
|
|
||||||
$this->pdf->Cell(4.5, .25, $auditioningFor);
|
|
||||||
}
|
|
||||||
$this->quadOn++;
|
$this->quadOn++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,6 @@ namespace App\Actions\Print;
|
||||||
|
|
||||||
use Codedge\Fpdf\Fpdf\Fpdf;
|
use Codedge\Fpdf\Fpdf\Fpdf;
|
||||||
|
|
||||||
/**
|
|
||||||
* @codeCoverageIgnore
|
|
||||||
*/
|
|
||||||
// TODO figure out testing for signInPDF
|
|
||||||
class signInPDF extends Fpdf
|
class signInPDF extends Fpdf
|
||||||
{
|
{
|
||||||
public $roomOn;
|
public $roomOn;
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,6 @@ use App\Actions\Tabulation\RankAuditionEntries;
|
||||||
use App\Models\Room;
|
use App\Models\Room;
|
||||||
use Illuminate\Support\Facades\App;
|
use Illuminate\Support\Facades\App;
|
||||||
|
|
||||||
/**
|
|
||||||
* @codeCoverageIgnore
|
|
||||||
*/
|
|
||||||
// TODO figure out testing for ExportEntryData
|
|
||||||
class ExportEntryData
|
class ExportEntryData
|
||||||
{
|
{
|
||||||
public function __construct()
|
public function __construct()
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,6 @@ use App\Models\Event;
|
||||||
use App\Models\Seat;
|
use App\Models\Seat;
|
||||||
use Illuminate\Support\Facades\App;
|
use Illuminate\Support\Facades\App;
|
||||||
|
|
||||||
/**
|
|
||||||
* @codeCoverageIgnore
|
|
||||||
*/
|
|
||||||
// TODO figure out testing for GetExportData
|
|
||||||
class GetExportData
|
class GetExportData
|
||||||
{
|
{
|
||||||
public function __construct()
|
public function __construct()
|
||||||
|
|
@ -33,7 +29,7 @@ class GetExportData
|
||||||
foreach ($events as $event) {
|
foreach ($events as $event) {
|
||||||
$auditions = $event->auditions;
|
$auditions = $event->auditions;
|
||||||
foreach ($auditions as $audition) {
|
foreach ($auditions as $audition) {
|
||||||
$entries = $ranker($audition, 'seating');
|
$entries = $ranker->rank('seating', $audition);
|
||||||
foreach ($entries as $entry) {
|
foreach ($entries as $entry) {
|
||||||
$thisRow = $audition->name.',';
|
$thisRow = $audition->name.',';
|
||||||
$thisRow .= $entry->raw_rank ?? '';
|
$thisRow .= $entry->raw_rank ?? '';
|
||||||
|
|
@ -41,7 +37,7 @@ class GetExportData
|
||||||
$thisRow .= $entry->student->full_name().',';
|
$thisRow .= $entry->student->full_name().',';
|
||||||
$thisRow .= $entry->student->school->name.',';
|
$thisRow .= $entry->student->school->name.',';
|
||||||
$thisRow .= $entry->student->grade.',';
|
$thisRow .= $entry->student->grade.',';
|
||||||
$thisRow .= $entry->totalScore->seating_total ?? '';
|
$thisRow .= $entry->score_totals[0] ?? '';
|
||||||
$thisRow .= ',';
|
$thisRow .= ',';
|
||||||
if ($entry->hasFlag('failed_prelim')) {
|
if ($entry->hasFlag('failed_prelim')) {
|
||||||
$thisRow .= 'Failed Prelim';
|
$thisRow .= 'Failed Prelim';
|
||||||
|
|
|
||||||
|
|
@ -1,35 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Actions\Schools;
|
|
||||||
|
|
||||||
use App\Exceptions\AuditionAdminException;
|
|
||||||
use App\Models\School;
|
|
||||||
use App\Models\SchoolEmailDomain;
|
|
||||||
|
|
||||||
class AddSchoolEmailDomain
|
|
||||||
{
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public function __invoke(School $school, string $domain): void
|
|
||||||
{
|
|
||||||
$this->addDomain($school, $domain);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function addDomain(School $school, string $domain): void
|
|
||||||
{
|
|
||||||
if (! School::where('id', $school->id)->exists()) {
|
|
||||||
throw new AuditionAdminException('School does not exist');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (SchoolEmailDomain::where('domain', $domain)->where('school_id', $school->id)->exists()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
SchoolEmailDomain::create([
|
|
||||||
'domain' => $domain,
|
|
||||||
'school_id' => $school->id,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,47 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Actions\Schools;
|
|
||||||
|
|
||||||
use App\Exceptions\AuditionAdminException;
|
|
||||||
use App\Models\School;
|
|
||||||
use App\Models\User;
|
|
||||||
|
|
||||||
class AssignUserToSchool
|
|
||||||
{
|
|
||||||
public function __invoke(User $user, School|int|null $school): void
|
|
||||||
{
|
|
||||||
$this->assign($user, $school);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function assign(User $user, School|int|null $school, bool $addDomainToSchool = true): void
|
|
||||||
{
|
|
||||||
if (! User::where('id', $user->id)->exists()) {
|
|
||||||
throw new AuditionAdminException('User does not exist');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_int($school)) {
|
|
||||||
$school = School::find($school);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_null($school)) {
|
|
||||||
$user->update([
|
|
||||||
'school_id' => null,
|
|
||||||
]);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_null($school) || ! School::where('id', $school->id)->exists()) {
|
|
||||||
throw new AuditionAdminException('School does not exist');
|
|
||||||
}
|
|
||||||
|
|
||||||
$domainRecorder = app(AddSchoolEmailDomain::class);
|
|
||||||
|
|
||||||
if ($addDomainToSchool) {
|
|
||||||
$domainRecorder($school, $user->emailDomain());
|
|
||||||
}
|
|
||||||
$user->update([
|
|
||||||
'school_id' => $school->id,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,48 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Actions\Schools;
|
|
||||||
|
|
||||||
use App\Exceptions\AuditionAdminException;
|
|
||||||
use App\Models\School;
|
|
||||||
|
|
||||||
class CreateSchool
|
|
||||||
{
|
|
||||||
public function __invoke(
|
|
||||||
string $name,
|
|
||||||
?string $address = null,
|
|
||||||
?string $city = null,
|
|
||||||
?string $state = null,
|
|
||||||
?string $zip = null
|
|
||||||
): School {
|
|
||||||
return $this->create($name, $address, $city, $state, $zip);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function create(
|
|
||||||
string $name,
|
|
||||||
?string $address = null,
|
|
||||||
?string $city = null,
|
|
||||||
?string $state = null,
|
|
||||||
?string $zip = null
|
|
||||||
): School {
|
|
||||||
|
|
||||||
if (School::where('name', $name)->exists()) {
|
|
||||||
throw new AuditionAdminException('The school '.$name.' already exists');
|
|
||||||
}
|
|
||||||
|
|
||||||
$newSchool = School::create([
|
|
||||||
'name' => $name,
|
|
||||||
'address' => $address,
|
|
||||||
'city' => $city,
|
|
||||||
'state' => $state,
|
|
||||||
'zip' => $zip,
|
|
||||||
]);
|
|
||||||
|
|
||||||
if (auth()->user()) {
|
|
||||||
$message = 'Created school '.$newSchool->name;
|
|
||||||
$affects = ['schools' => [$newSchool->id]];
|
|
||||||
auditionLog($message, $affects);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $newSchool;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
namespace App\Actions\Schools;
|
namespace App\Actions\Schools;
|
||||||
|
|
||||||
use App\Exceptions\AuditionAdminException;
|
use App\Exceptions\AuditionAdminException;
|
||||||
|
use App\Models\School;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
|
|
||||||
use function auditionLog;
|
use function auditionLog;
|
||||||
|
|
@ -14,9 +15,9 @@ class SetHeadDirector
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __invoke(User $user): void
|
public function __invoke(User $user, School $school): void
|
||||||
{
|
{
|
||||||
$this->setHeadDirector($user);
|
$this->setHeadDirector($user, $school);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -24,14 +25,6 @@ class SetHeadDirector
|
||||||
*/
|
*/
|
||||||
public function setHeadDirector(User $user): void
|
public function setHeadDirector(User $user): void
|
||||||
{
|
{
|
||||||
if (! User::where('id', $user->id)->exists()) {
|
|
||||||
throw new AuditionAdminException('User does not exist');
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($user->hasFlag('head_director')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_null($user->school_id)) {
|
if (is_null($user->school_id)) {
|
||||||
throw new AuditionAdminException('User is not associated with a school');
|
throw new AuditionAdminException('User is not associated with a school');
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,44 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Actions\Students;
|
|
||||||
|
|
||||||
use App\Exceptions\AuditionAdminException;
|
|
||||||
use App\Models\Student;
|
|
||||||
use Arr;
|
|
||||||
|
|
||||||
class CreateStudent
|
|
||||||
{
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @throws AuditionAdminException
|
|
||||||
*/
|
|
||||||
public function __invoke(array $newData): Student
|
|
||||||
{
|
|
||||||
|
|
||||||
// $newData[] must include keys first_name, last_name, grade - throw an exception if it does not
|
|
||||||
foreach (['first_name', 'last_name', 'grade'] as $key) {
|
|
||||||
if (! Arr::has($newData, $key)) {
|
|
||||||
throw new AuditionAdminException('Missing required data');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! Arr::has($newData, 'school_id')) {
|
|
||||||
$newData['school_id'] = auth()->user()->school_id;
|
|
||||||
}
|
|
||||||
if (Student::where('first_name', $newData['first_name'])->where('last_name', $newData['last_name'])
|
|
||||||
->where('school_id', $newData['school_id'])->exists()) {
|
|
||||||
throw new AuditionAdminException('Student already exists');
|
|
||||||
}
|
|
||||||
|
|
||||||
return Student::create([
|
|
||||||
'first_name' => $newData['first_name'],
|
|
||||||
'last_name' => $newData['last_name'],
|
|
||||||
'grade' => $newData['grade'],
|
|
||||||
'school_id' => $newData['school_id'],
|
|
||||||
'optional_data' => $newData['optional_data'] ?? null,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,48 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Actions\Students;
|
|
||||||
|
|
||||||
use App\Exceptions\AuditionAdminException;
|
|
||||||
use App\Models\Student;
|
|
||||||
use Arr;
|
|
||||||
|
|
||||||
class UpdateStudent
|
|
||||||
{
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @throws AuditionAdminException
|
|
||||||
*/
|
|
||||||
public function __invoke(Student $student, array $newData): bool
|
|
||||||
{
|
|
||||||
|
|
||||||
// $newData[] must include keys first_name, last_name, grade - throw an exception if it does not
|
|
||||||
foreach (['first_name', 'last_name', 'grade'] as $key) {
|
|
||||||
if (! Arr::has($newData, $key)) {
|
|
||||||
throw new AuditionAdminException('Missing required data');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! Arr::has($newData, 'school_id')) {
|
|
||||||
$newData['school_id'] = auth()->user()->school_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Student::where('first_name', $newData['first_name'])
|
|
||||||
->where('last_name', $newData['last_name'])
|
|
||||||
->where('school_id', $newData['school_id'])
|
|
||||||
->where('id', '!=', $student->id)
|
|
||||||
->exists()) {
|
|
||||||
throw new AuditionAdminException('Student already exists');
|
|
||||||
}
|
|
||||||
|
|
||||||
return $student->update([
|
|
||||||
'first_name' => $newData['first_name'],
|
|
||||||
'last_name' => $newData['last_name'],
|
|
||||||
'grade' => $newData['grade'],
|
|
||||||
'school_id' => $newData['school_id'],
|
|
||||||
'optional_data' => $newData['optional_data'] ?? null,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,99 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/** @noinspection PhpUnhandledExceptionInspection */
|
||||||
|
|
||||||
|
namespace App\Actions\Tabulation;
|
||||||
|
|
||||||
|
use App\Exceptions\TabulationException;
|
||||||
|
use App\Models\Entry;
|
||||||
|
use App\Services\AuditionService;
|
||||||
|
use App\Services\EntryService;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
|
||||||
|
class AllJudgesCount implements CalculateEntryScore
|
||||||
|
{
|
||||||
|
protected CalculateScoreSheetTotal $calculator;
|
||||||
|
|
||||||
|
protected AuditionService $auditionService;
|
||||||
|
|
||||||
|
protected EntryService $entryService;
|
||||||
|
|
||||||
|
public function __construct(CalculateScoreSheetTotal $calculator, AuditionService $auditionService, EntryService $entryService)
|
||||||
|
{
|
||||||
|
$this->calculator = $calculator;
|
||||||
|
$this->auditionService = $auditionService;
|
||||||
|
$this->entryService = $entryService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function calculate(string $mode, Entry $entry): array
|
||||||
|
{
|
||||||
|
|
||||||
|
$cacheKey = 'entryScore-'.$entry->id.'-'.$mode;
|
||||||
|
|
||||||
|
return Cache::remember($cacheKey, 300, function () use ($mode, $entry) {
|
||||||
|
$this->isEntryANoShow($entry);
|
||||||
|
$this->basicValidation($mode, $entry);
|
||||||
|
$this->areAllJudgesIn($entry);
|
||||||
|
$this->areAllJudgesValid($entry);
|
||||||
|
|
||||||
|
return $this->getJudgeTotals($mode, $entry);
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getJudgeTotals($mode, Entry $entry)
|
||||||
|
{
|
||||||
|
|
||||||
|
$scores = [];
|
||||||
|
foreach ($this->auditionService->getJudges($entry->audition) as $judge) {
|
||||||
|
$scores[] = $this->calculator->__invoke($mode, $entry, $judge);
|
||||||
|
}
|
||||||
|
$sums = [];
|
||||||
|
// Sum each subscore from the judges
|
||||||
|
foreach ($scores as $score) {
|
||||||
|
$index = 0;
|
||||||
|
foreach ($score as $value) {
|
||||||
|
$sums[$index] = $sums[$index] ?? 0;
|
||||||
|
$sums[$index] += $value;
|
||||||
|
$index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $sums;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function basicValidation($mode, $entry): void
|
||||||
|
{
|
||||||
|
if ($mode !== 'seating' && $mode !== 'advancement') {
|
||||||
|
throw new TabulationException('Mode must be seating or advancement');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! $this->entryService->entryExists($entry)) {
|
||||||
|
throw new TabulationException('Invalid entry specified');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function areAllJudgesIn(Entry $entry): void
|
||||||
|
{
|
||||||
|
$assignedJudgeCount = $this->auditionService->getJudges($entry->audition)->count();
|
||||||
|
if ($entry->scoreSheets->count() !== $assignedJudgeCount) {
|
||||||
|
throw new TabulationException('Not all score sheets are in');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function areAllJudgesValid(Entry $entry): void
|
||||||
|
{
|
||||||
|
$validJudgeIds = $this->auditionService->getJudges($entry->audition)->pluck('id')->sort()->toArray();
|
||||||
|
$existingJudgeIds = $entry->scoreSheets->pluck('user_id')->sort()->toArray();
|
||||||
|
if ($validJudgeIds !== $existingJudgeIds) {
|
||||||
|
throw new TabulationException('Score exists from a judge not assigned to this audition');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function isEntryANoShow(Entry $entry): void
|
||||||
|
{
|
||||||
|
if ($entry->hasFlag('no_show')) {
|
||||||
|
throw new TabulationException('No Show');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,161 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/** @noinspection PhpUnhandledExceptionInspection */
|
||||||
|
|
||||||
|
namespace App\Actions\Tabulation;
|
||||||
|
|
||||||
|
use App\Exceptions\TabulationException;
|
||||||
|
use App\Models\BonusScore;
|
||||||
|
use App\Models\CalculatedScore;
|
||||||
|
use App\Models\Entry;
|
||||||
|
use App\Services\AuditionService;
|
||||||
|
use App\Services\EntryService;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
use function auditionSetting;
|
||||||
|
|
||||||
|
class AllowForOlympicScoring implements CalculateEntryScore
|
||||||
|
{
|
||||||
|
protected CalculateScoreSheetTotal $calculator;
|
||||||
|
|
||||||
|
protected AuditionService $auditionService;
|
||||||
|
|
||||||
|
protected EntryService $entryService;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
CalculateScoreSheetTotal $calculator,
|
||||||
|
AuditionService $auditionService,
|
||||||
|
EntryService $entryService
|
||||||
|
) {
|
||||||
|
$this->calculator = $calculator;
|
||||||
|
$this->auditionService = $auditionService;
|
||||||
|
$this->entryService = $entryService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function calculate(string $mode, Entry $entry): array
|
||||||
|
{
|
||||||
|
$calculated = CalculatedScore::where('entry_id', $entry->id)->where('mode', $mode)->first();
|
||||||
|
if ($calculated) {
|
||||||
|
return $calculated->calculatedScore;
|
||||||
|
}
|
||||||
|
|
||||||
|
$cacheKey = 'entryScore-'.$entry->id.'-'.$mode;
|
||||||
|
|
||||||
|
return Cache::remember($cacheKey, 300, function () use ($mode, $entry) {
|
||||||
|
$this->basicValidation($mode, $entry);
|
||||||
|
$this->isEntryANoShow($entry);
|
||||||
|
$this->areAllJudgesIn($entry);
|
||||||
|
$this->areAllJudgesValid($entry);
|
||||||
|
$calculatedScores = $this->getJudgeTotals($mode, $entry);
|
||||||
|
CalculatedScore::create([
|
||||||
|
'entry_id' => $entry->id,
|
||||||
|
'mode' => $mode,
|
||||||
|
'calculatedScore' => $calculatedScores,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return $calculatedScores;
|
||||||
|
// return $this->getJudgeTotals($mode, $entry);
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getJudgeTotals($mode, Entry $entry): array
|
||||||
|
{
|
||||||
|
|
||||||
|
$scores = [];
|
||||||
|
foreach ($this->auditionService->getJudges($entry->audition) as $judge) {
|
||||||
|
$scores[] = $this->calculator->__invoke($mode, $entry, $judge);
|
||||||
|
}
|
||||||
|
// sort the scores array by the total score
|
||||||
|
usort($scores, function ($a, $b) {
|
||||||
|
return $a[0] <=> $b[0];
|
||||||
|
});
|
||||||
|
|
||||||
|
// we can only really do olympic scoring if there are at least 3 scores
|
||||||
|
if (count($scores) >= 3 && auditionSetting('olympic_scoring')) {
|
||||||
|
// remove the highest and lowest scores
|
||||||
|
array_pop($scores);
|
||||||
|
array_shift($scores);
|
||||||
|
}
|
||||||
|
$sums = [];
|
||||||
|
// Sum each subscore from the judges
|
||||||
|
foreach ($scores as $score) {
|
||||||
|
$index = 0;
|
||||||
|
foreach ($score as $value) {
|
||||||
|
$sums[$index] = $sums[$index] ?? 0;
|
||||||
|
$sums[$index] += $value;
|
||||||
|
$index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// add the bonus points for a seating mode
|
||||||
|
if ($mode === 'seating' && $sums) {
|
||||||
|
|
||||||
|
$sums[0] += $this->getBonusPoints($entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $sums;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getBonusPoints(Entry $entry)
|
||||||
|
{
|
||||||
|
|
||||||
|
$bonusScoreDefinition = $entry->audition->bonusScore()->first();
|
||||||
|
if (! $bonusScoreDefinition) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
/** @noinspection PhpPossiblePolymorphicInvocationInspection */
|
||||||
|
$bonusJudges = $bonusScoreDefinition->judges;
|
||||||
|
$bonusScoreSheets = BonusScore::where('entry_id', $entry->id)->get();
|
||||||
|
foreach ($bonusScoreSheets as $sheet) {
|
||||||
|
if (! $bonusJudges->contains($sheet->user_id)) {
|
||||||
|
throw new TabulationException('Entry has a bonus score from unassigned judge');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// sum the score property of the $bonusScoreSheets
|
||||||
|
return $bonusScoreSheets->sum('score');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function basicValidation($mode, $entry): void
|
||||||
|
{
|
||||||
|
if ($mode !== 'seating' && $mode !== 'advancement') {
|
||||||
|
throw new TabulationException('Mode must be seating or advancement');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! $this->entryService->entryExists($entry)) {
|
||||||
|
throw new TabulationException('Invalid entry specified');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function areAllJudgesIn(Entry $entry): void
|
||||||
|
{
|
||||||
|
$assignedJudgeCount = $this->auditionService->getJudges($entry->audition)->count();
|
||||||
|
if ($entry->scoreSheets->count() !== $assignedJudgeCount) {
|
||||||
|
throw new TabulationException('Not all score sheets are in');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function areAllJudgesValid(Entry $entry): void
|
||||||
|
{
|
||||||
|
$validJudgeIds = $this->auditionService->getJudges($entry->audition)->pluck('id')->toArray();
|
||||||
|
$existingJudgeIds = $entry->scoreSheets->pluck('user_id')->toArray();
|
||||||
|
if (array_diff($existingJudgeIds, $validJudgeIds)) {
|
||||||
|
Log::debug('EntryID: '.$entry->id);
|
||||||
|
Log::debug('Valid judge ids: ('.gettype($validJudgeIds).') '.json_encode($validJudgeIds));
|
||||||
|
Log::debug('Existing judge ids: ('.gettype($existingJudgeIds).') '.json_encode($existingJudgeIds));
|
||||||
|
throw new TabulationException('Score exists from a judge not assigned to this audition');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function isEntryANoShow(Entry $entry): void
|
||||||
|
{
|
||||||
|
if ($entry->hasFlag('failed_prelim')) {
|
||||||
|
throw new TabulationException('Failed Prelim');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($entry->hasFlag('no_show')) {
|
||||||
|
throw new TabulationException('No Show');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Actions\Tabulation;
|
|
||||||
|
|
||||||
use App\Models\Audition;
|
|
||||||
use App\Models\Entry;
|
|
||||||
|
|
||||||
class CalculateAuditionScores
|
|
||||||
{
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public function __invoke(Audition $audition): void
|
|
||||||
{
|
|
||||||
$totaler = app(TotalEntryScores::class);
|
|
||||||
$scores_required = $audition->judges->count();
|
|
||||||
$pending_entries = Entry::where('audition_id', $audition->id)
|
|
||||||
->has('scoreSheets', '=', $scores_required)
|
|
||||||
->whereDoesntHave('totalScore')
|
|
||||||
->with('audition.scoringGuide.subscores')
|
|
||||||
->get();
|
|
||||||
foreach ($pending_entries as $entry) {
|
|
||||||
$totaler->__invoke($entry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions\Tabulation;
|
||||||
|
|
||||||
|
use App\Models\Entry;
|
||||||
|
|
||||||
|
interface CalculateEntryScore
|
||||||
|
{
|
||||||
|
|
||||||
|
public function calculate(string $mode, Entry $entry): array;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions\Tabulation;
|
||||||
|
|
||||||
|
use App\Models\Entry;
|
||||||
|
use App\Models\User;
|
||||||
|
|
||||||
|
interface CalculateScoreSheetTotal
|
||||||
|
{
|
||||||
|
public function __invoke(string $mode, Entry $entry, User $judge): array;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,67 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/** @noinspection PhpUnhandledExceptionInspection */
|
||||||
|
|
||||||
|
namespace App\Actions\Tabulation;
|
||||||
|
|
||||||
|
use App\Exceptions\TabulationException;
|
||||||
|
use App\Models\Entry;
|
||||||
|
use App\Models\ScoreSheet;
|
||||||
|
use App\Models\User;
|
||||||
|
use App\Services\AuditionService;
|
||||||
|
use App\Services\EntryService;
|
||||||
|
use App\Services\UserService;
|
||||||
|
|
||||||
|
class CalculateScoreSheetTotalDivideByTotalWeights implements CalculateScoreSheetTotal
|
||||||
|
{
|
||||||
|
protected AuditionService $auditionService;
|
||||||
|
|
||||||
|
protected EntryService $entryService;
|
||||||
|
|
||||||
|
protected UserService $userService;
|
||||||
|
|
||||||
|
public function __construct(AuditionService $auditionService, EntryService $entryService, UserService $userService)
|
||||||
|
{
|
||||||
|
$this->auditionService = $auditionService;
|
||||||
|
$this->entryService = $entryService;
|
||||||
|
$this->userService = $userService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __invoke(string $mode, Entry $entry, User $judge): array
|
||||||
|
{
|
||||||
|
$this->basicValidations($mode, $entry, $judge);
|
||||||
|
$scoreSheet = ScoreSheet::where('entry_id', $entry->id)->where('user_id', $judge->id)->first();
|
||||||
|
if (! $scoreSheet) {
|
||||||
|
throw new TabulationException('No score sheet by that judge for that entry');
|
||||||
|
}
|
||||||
|
$subscores = $this->auditionService->getSubscores($entry->audition, $mode);
|
||||||
|
$scoreTotal = 0;
|
||||||
|
$weightsTotal = 0;
|
||||||
|
$scoreArray = [];
|
||||||
|
foreach ($subscores as $subscore) {
|
||||||
|
$weight = $subscore['weight'];
|
||||||
|
$score = $scoreSheet->subscores[$subscore->id]['score'];
|
||||||
|
$scoreArray[] = $score;
|
||||||
|
$scoreTotal += ($score * $weight);
|
||||||
|
$weightsTotal += $weight;
|
||||||
|
}
|
||||||
|
$finalScore = $scoreTotal / $weightsTotal;
|
||||||
|
// put $final score at the beginning of the $ScoreArray
|
||||||
|
array_unshift($scoreArray, $finalScore);
|
||||||
|
|
||||||
|
return $scoreArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function basicValidations($mode, $entry, $judge): void
|
||||||
|
{
|
||||||
|
if ($mode !== 'seating' and $mode !== 'advancement') {
|
||||||
|
throw new TabulationException('Invalid mode requested. Mode must be seating or advancement');
|
||||||
|
}
|
||||||
|
if (! $this->entryService->entryExists($entry)) {
|
||||||
|
throw new TabulationException('Invalid entry provided');
|
||||||
|
}
|
||||||
|
if (! $this->userService->userExists($judge)) {
|
||||||
|
throw new TabulationException('Invalid judge provided');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,74 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/** @noinspection PhpUnhandledExceptionInspection */
|
||||||
|
|
||||||
|
namespace App\Actions\Tabulation;
|
||||||
|
|
||||||
|
use App\Exceptions\TabulationException;
|
||||||
|
use App\Models\Entry;
|
||||||
|
use App\Models\ScoreSheet;
|
||||||
|
use App\Models\User;
|
||||||
|
use App\Services\AuditionService;
|
||||||
|
use App\Services\EntryService;
|
||||||
|
use App\Services\UserService;
|
||||||
|
|
||||||
|
class CalculateScoreSheetTotalDivideByWeightedPossible implements CalculateScoreSheetTotal
|
||||||
|
{
|
||||||
|
protected AuditionService $auditionService;
|
||||||
|
|
||||||
|
protected EntryService $entryService;
|
||||||
|
|
||||||
|
protected UserService $userService;
|
||||||
|
|
||||||
|
public function __construct(AuditionService $auditionService, EntryService $entryService, UserService $userService)
|
||||||
|
{
|
||||||
|
$this->auditionService = $auditionService;
|
||||||
|
$this->entryService = $entryService;
|
||||||
|
$this->userService = $userService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __invoke(string $mode, Entry $entry, User $judge): array
|
||||||
|
{
|
||||||
|
$this->basicValidations($mode, $entry, $judge);
|
||||||
|
$scoreSheet = ScoreSheet::where('entry_id', $entry->id)->where('user_id', $judge->id)->first();
|
||||||
|
if (! $scoreSheet) {
|
||||||
|
throw new TabulationException('No score sheet by that judge for that entry');
|
||||||
|
}
|
||||||
|
$subscores = $this->auditionService->getSubscores($entry->audition, $mode);
|
||||||
|
$scoreTotal = 0;
|
||||||
|
$weightsTotal = 0;
|
||||||
|
$weightedMaxPossible = 0;
|
||||||
|
$scoreArray = [];
|
||||||
|
foreach ($subscores as $subscore) {
|
||||||
|
$weight = $subscore['weight'];
|
||||||
|
$score = $scoreSheet->subscores[$subscore->id]['score'];
|
||||||
|
$maxPossible = $subscore['max_score'];
|
||||||
|
$scoreArray[] = $score;
|
||||||
|
$scoreTotal += ($score * $weight);
|
||||||
|
$weightsTotal += $weight;
|
||||||
|
$weightedMaxPossible += $maxPossible;
|
||||||
|
}
|
||||||
|
if ($weightedMaxPossible > 0) {
|
||||||
|
$finalScore = ($scoreTotal / $weightedMaxPossible) * 100;
|
||||||
|
} else {
|
||||||
|
$finalScore = 0;
|
||||||
|
}
|
||||||
|
// put $final score at the beginning of the $ScoreArray
|
||||||
|
array_unshift($scoreArray, $finalScore);
|
||||||
|
|
||||||
|
return $scoreArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function basicValidations($mode, $entry, $judge): void
|
||||||
|
{
|
||||||
|
if ($mode !== 'seating' and $mode !== 'advancement') {
|
||||||
|
throw new TabulationException('Invalid mode requested. Mode must be seating or advancement');
|
||||||
|
}
|
||||||
|
if (! $this->entryService->entryExists($entry)) {
|
||||||
|
throw new TabulationException('Invalid entry provided');
|
||||||
|
}
|
||||||
|
if (! $this->userService->userExists($judge)) {
|
||||||
|
throw new TabulationException('Invalid judge provided');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,61 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Actions\Tabulation;
|
|
||||||
|
|
||||||
use App\Exceptions\AuditionAdminException;
|
|
||||||
use App\Models\Entry;
|
|
||||||
|
|
||||||
class CheckPrelimResult
|
|
||||||
{
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @throws AuditionAdminException
|
|
||||||
*/
|
|
||||||
public function __invoke(Entry $entry, bool $recalc = false): string
|
|
||||||
{
|
|
||||||
if ($recalc) {
|
|
||||||
$entry->removeFlag('passed_prelim');
|
|
||||||
$entry->removeFlag('failed_prelim');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! $entry->exists) {
|
|
||||||
throw new AuditionAdminException('Entry does not exist');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! $entry->audition->prelimDefinition) {
|
|
||||||
throw new AuditionAdminException('Entry does not have a prelim');
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($entry->hasFlag('failed_prelim') || $entry->hasFlag('passed_prelim')) {
|
|
||||||
return 'noChange';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! $entry->audition->prelimDefinition->room || $entry->audition->prelimDefinition->room->judges()->count() == 0) {
|
|
||||||
return 'noJudgesAssigned';
|
|
||||||
}
|
|
||||||
|
|
||||||
$scoresRequired = $entry->audition->prelimDefinition->room->judges()->count();
|
|
||||||
$scoresAssigned = $entry->prelimScoreSheets()->count();
|
|
||||||
if ($scoresAssigned < $scoresRequired) {
|
|
||||||
return 'missing'.$scoresRequired - $scoresAssigned.'scores';
|
|
||||||
}
|
|
||||||
|
|
||||||
$totalScore = 0;
|
|
||||||
foreach ($entry->prelimScoreSheets as $scoreSheet) {
|
|
||||||
$totalScore += $scoreSheet->total;
|
|
||||||
}
|
|
||||||
$averageScore = $totalScore / $scoresAssigned;
|
|
||||||
if ($averageScore >= $entry->audition->prelimDefinition->passing_score) {
|
|
||||||
$entry->addFlag('passed_prelim');
|
|
||||||
|
|
||||||
return 'markedPassed';
|
|
||||||
} else {
|
|
||||||
$entry->addFlag('failed_prelim');
|
|
||||||
|
|
||||||
return 'markedFailed';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,85 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Actions\Tabulation;
|
|
||||||
|
|
||||||
use App\Models\Doubler;
|
|
||||||
use App\Models\Event;
|
|
||||||
use App\Models\Student;
|
|
||||||
|
|
||||||
use function collect;
|
|
||||||
|
|
||||||
class DoublerSync
|
|
||||||
{
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sync the Doubler records for the given event. If no event is provided, sync Doubler records for all events.
|
|
||||||
*/
|
|
||||||
public function __invoke(Event|int|null $event = null): void
|
|
||||||
{
|
|
||||||
if ($event) {
|
|
||||||
$this->syncForEvent($event);
|
|
||||||
} else {
|
|
||||||
$this->syncAllDoublers();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function syncForEvent(Event|int $eventId): void
|
|
||||||
{
|
|
||||||
if ($eventId instanceof Event) {
|
|
||||||
$eventId = $eventId->id;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get students with multiple entries in this event's auditions
|
|
||||||
$studentsWithMultipleEntries = Student::query()
|
|
||||||
->select('students.id')
|
|
||||||
->join('entries', 'students.id', '=', 'entries.student_id')
|
|
||||||
->join('auditions', 'entries.audition_id', '=', 'auditions.id')
|
|
||||||
->where('auditions.event_id', $eventId)
|
|
||||||
->groupBy('students.id')
|
|
||||||
->havingRaw('COUNT(entries.id) > 1')
|
|
||||||
->with('entries')
|
|
||||||
->get();
|
|
||||||
Doubler::where('event_id', $eventId)->delete();
|
|
||||||
foreach ($studentsWithMultipleEntries as $student) {
|
|
||||||
// Get entries that are not declined. If only one, they're our accepted entry.
|
|
||||||
$entryList = collect(); // List of entry ids for th is student in this event
|
|
||||||
$undecidedEntries = collect(); // List of entry ids that are not declined, no-show, or failed prelim
|
|
||||||
$entryList = $student->entriesForEvent($eventId)->pluck('id');
|
|
||||||
$undecidedEntries = $student->entriesForEvent($eventId)->filter(function ($entry) {
|
|
||||||
return ! $entry->hasFlag('declined')
|
|
||||||
&& ! $entry->hasFlag('no_show')
|
|
||||||
&& ! $entry->hasFlag('failed_prelim');
|
|
||||||
})->pluck('id');
|
|
||||||
if ($undecidedEntries->count() < 2) {
|
|
||||||
$acceptedEntryId = $undecidedEntries->first();
|
|
||||||
} else {
|
|
||||||
$acceptedEntryId = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create or update the doubler record
|
|
||||||
Doubler::create([
|
|
||||||
'student_id' => $student->id,
|
|
||||||
'event_id' => $eventId,
|
|
||||||
'entries' => $entryList,
|
|
||||||
'accepted_entry' => $acceptedEntryId,
|
|
||||||
]);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove doubler records for students who no longer have multiple entries
|
|
||||||
Doubler::where('event_id', $eventId)
|
|
||||||
->whereNotIn('student_id', $studentsWithMultipleEntries->pluck('id'))
|
|
||||||
->delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function syncAllDoublers(): void
|
|
||||||
{
|
|
||||||
$events = Event::all();
|
|
||||||
foreach ($events as $event) {
|
|
||||||
$this->syncForEvent($event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -4,27 +4,32 @@
|
||||||
|
|
||||||
namespace App\Actions\Tabulation;
|
namespace App\Actions\Tabulation;
|
||||||
|
|
||||||
use App\Exceptions\AuditionAdminException;
|
use App\Exceptions\ScoreEntryException;
|
||||||
use App\Models\BonusScore;
|
use App\Models\BonusScore;
|
||||||
|
use App\Models\CalculatedScore;
|
||||||
use App\Models\Entry;
|
use App\Models\Entry;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
|
use Illuminate\Database\Eloquent\Collection;
|
||||||
use Illuminate\Support\Facades\App;
|
use Illuminate\Support\Facades\App;
|
||||||
|
|
||||||
class EnterBonusScore
|
class EnterBonusScore
|
||||||
{
|
{
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public function __invoke(User $judge, Entry $entry, int $score): void
|
public function __invoke(User $judge, Entry $entry, int $score): void
|
||||||
{
|
{
|
||||||
|
|
||||||
$getRelatedEntries = App::make(GetBonusScoreRelatedEntries::class);
|
$getRelatedEntries = App::make(GetBonusScoreRelatedEntries::class);
|
||||||
// Verify there is a need for a bonus score
|
$this->basicValidations($judge, $entry);
|
||||||
if ($entry->audition->bonusScore->count() === 0) {
|
|
||||||
throw new AuditionAdminException('The entries audition does not accept bonus scores');
|
|
||||||
}
|
|
||||||
$this->validateJudgeValidity($judge, $entry, $score);
|
$this->validateJudgeValidity($judge, $entry, $score);
|
||||||
$entries = $getRelatedEntries($entry);
|
$entries = $getRelatedEntries($entry);
|
||||||
|
|
||||||
// Create the score for each related entry
|
// Create the score for each related entry
|
||||||
foreach ($entries as $relatedEntry) {
|
foreach ($entries as $relatedEntry) {
|
||||||
// Also delete any cached scores
|
// Also delete any cached scores
|
||||||
|
CalculatedScore::where('entry_id', $relatedEntry->id)->delete();
|
||||||
BonusScore::create([
|
BonusScore::create([
|
||||||
'entry_id' => $relatedEntry->id,
|
'entry_id' => $relatedEntry->id,
|
||||||
'user_id' => $judge->id,
|
'user_id' => $judge->id,
|
||||||
|
|
@ -35,18 +40,43 @@ class EnterBonusScore
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function getRelatedEntries(Entry $entry): Collection
|
||||||
|
{
|
||||||
|
$bonusScore = $entry->audition->bonusScore->first();
|
||||||
|
$relatedAuditions = $bonusScore->auditions;
|
||||||
|
|
||||||
|
// Get all entries that have a student_id equal to that of entry and an audition_id in the related auditions
|
||||||
|
return Entry::where('student_id', $entry->student_id)
|
||||||
|
->whereIn('audition_id', $relatedAuditions->pluck('id'))
|
||||||
|
->get();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function basicValidations(User $judge, Entry $entry): void
|
||||||
|
{
|
||||||
|
if (! $judge->exists) {
|
||||||
|
throw new ScoreEntryException('Invalid judge provided');
|
||||||
|
}
|
||||||
|
if (! $entry->exists) {
|
||||||
|
throw new ScoreEntryException('Invalid entry provided');
|
||||||
|
}
|
||||||
|
if ($entry->audition->bonusScore->count() === 0) {
|
||||||
|
throw new ScoreEntryException('Entry does not have a bonus score');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
protected function validateJudgeValidity(User $judge, Entry $entry, $score): void
|
protected function validateJudgeValidity(User $judge, Entry $entry, $score): void
|
||||||
{
|
{
|
||||||
if (BonusScore::where('entry_id', $entry->id)->where('user_id', $judge->id)->exists()) {
|
if (BonusScore::where('entry_id', $entry->id)->where('user_id', $judge->id)->exists()) {
|
||||||
throw new AuditionAdminException('That judge has already scored that entry');
|
throw new ScoreEntryException('That judge has already scored that entry');
|
||||||
}
|
}
|
||||||
|
|
||||||
$bonusScore = $entry->audition->bonusScore->first();
|
$bonusScore = $entry->audition->bonusScore->first();
|
||||||
if (! $bonusScore->judges->contains($judge)) {
|
if (! $bonusScore->judges->contains($judge)) {
|
||||||
throw new AuditionAdminException('That judge is not assigned to judge that bonus score');
|
throw new ScoreEntryException('That judge is not assigned to judge that bonus score');
|
||||||
}
|
}
|
||||||
if ($score > $bonusScore->max_score) {
|
if ($score > $bonusScore->max_score) {
|
||||||
throw new AuditionAdminException('That score exceeds the maximum');
|
throw new ScoreEntryException('That score exceeds the maximum');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,65 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Actions\Tabulation;
|
|
||||||
|
|
||||||
use App\Exceptions\AuditionAdminException;
|
|
||||||
use App\Models\BonusScore;
|
|
||||||
use App\Models\Entry;
|
|
||||||
use App\Models\EntryTotalScore;
|
|
||||||
use App\Models\ScoreSheet;
|
|
||||||
use Illuminate\Support\Facades\DB;
|
|
||||||
|
|
||||||
use function auditionLog;
|
|
||||||
|
|
||||||
class EnterNoShow
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Handles the no-show or failed-prelim flagging for a given entry.
|
|
||||||
*
|
|
||||||
* This method ensures the specified flag type is valid and validates
|
|
||||||
* that the action can be performed based on the associated audition's state.
|
|
||||||
* Deletes related score records and applies the specified flag ('no_show'
|
|
||||||
* or 'failed_prelim') to the entry, returning a success message.
|
|
||||||
*
|
|
||||||
* @param Entry $entry The entry being flagged.
|
|
||||||
* @param string $flagType The type of flag to apply ('no-show' or 'failed-prelim').
|
|
||||||
* @return string A confirmation message about the flagging operation.
|
|
||||||
*
|
|
||||||
* @throws AuditionAdminException If an invalid flag type is provided,
|
|
||||||
* or the action violates business rules.
|
|
||||||
*/
|
|
||||||
public function __invoke(Entry $entry, string $flagType = 'noshow'): string
|
|
||||||
{
|
|
||||||
if ($flagType !== 'noshow' && $flagType !== 'failprelim') {
|
|
||||||
throw new AuditionAdminException('Invalid flag type');
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($entry->audition->hasFlag('seats_published')) {
|
|
||||||
throw new AuditionAdminException('Cannot enter a no-show for an entry in an audition where seats are published');
|
|
||||||
}
|
|
||||||
if ($entry->audition->hasFlag('advancement_published')) {
|
|
||||||
throw new AuditionAdminException('Cannot enter a no-show for an entry in an audition where advancement is published');
|
|
||||||
}
|
|
||||||
DB::table('score_sheets')->where('entry_id', $entry->id)->delete();
|
|
||||||
|
|
||||||
ScoreSheet::where('entry_id', $entry->id)->delete();
|
|
||||||
BonusScore::where('entry_id', $entry->id)->delete();
|
|
||||||
EntryTotalScore::where('entry_id', $entry->id)->delete();
|
|
||||||
if ($flagType == 'failprelim') {
|
|
||||||
$msg = 'Failed prelim has been entered for '.$entry->audition->name.' #'.$entry->draw_number.' (ID: '.$entry->id.').';
|
|
||||||
$entry->addFlag('failed_prelim');
|
|
||||||
} else {
|
|
||||||
$entry->addFlag('no_show');
|
|
||||||
$msg = 'No Show has been entered for '.$entry->audition->name.' #'.$entry->draw_number.' (ID: '.$entry->id.').';
|
|
||||||
}
|
|
||||||
|
|
||||||
$affected = [
|
|
||||||
['entries', [$entry->id]],
|
|
||||||
['auditions', [$entry->audition_id]],
|
|
||||||
['students', [$entry->student_id]],
|
|
||||||
];
|
|
||||||
auditionLog($msg, $affected);
|
|
||||||
|
|
||||||
return $msg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,137 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Actions\Tabulation;
|
|
||||||
|
|
||||||
use App\Exceptions\AuditionAdminException;
|
|
||||||
use App\Models\Entry;
|
|
||||||
use App\Models\PrelimDefinition;
|
|
||||||
use App\Models\PrelimScoreSheet;
|
|
||||||
use App\Models\User;
|
|
||||||
use DB;
|
|
||||||
|
|
||||||
use function auditionLog;
|
|
||||||
|
|
||||||
class EnterPrelimScore
|
|
||||||
{
|
|
||||||
public function __invoke(
|
|
||||||
User $user,
|
|
||||||
Entry $entry,
|
|
||||||
array $scores,
|
|
||||||
PrelimScoreSheet|false $prelimScoreSheet = false
|
|
||||||
): PrelimScoreSheet {
|
|
||||||
$scores = collect($scores);
|
|
||||||
|
|
||||||
// Basic Validity Checks
|
|
||||||
if (! User::where('id', $user->id)->exists()) {
|
|
||||||
throw new AuditionAdminException('User does not exist');
|
|
||||||
}
|
|
||||||
if (! Entry::where('id', $entry->id)->exists()) {
|
|
||||||
throw new AuditionAdminException('Entry does not exist');
|
|
||||||
}
|
|
||||||
if ($entry->audition->hasFlag('seats_published')) {
|
|
||||||
throw new AuditionAdminException('Cannot score an entry in an audition where seats are published');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the entries audition has a prelim definition
|
|
||||||
if (! PrelimDefinition::where('audition_id', $entry->audition->id)->exists()) {
|
|
||||||
throw new AuditionAdminException('The entries audition does not have a prelim');
|
|
||||||
}
|
|
||||||
$prelimDefinition = PrelimDefinition::where('audition_id', $entry->audition->id)->first();
|
|
||||||
|
|
||||||
// Don't allow changes to prelims scores if the entry has a finals score
|
|
||||||
if ($entry->scoreSheets()->count() > 0) {
|
|
||||||
throw new AuditionAdminException('Cannot change prelims scores for an entry that has finals scores');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check that the specified user is assigned to judge this entry in prelims
|
|
||||||
$check = DB::table('room_user')
|
|
||||||
->where('user_id', $user->id)
|
|
||||||
->where('room_id', $prelimDefinition->room_id)->exists();
|
|
||||||
if (! $check) {
|
|
||||||
throw new AuditionAdminException('This judge is not assigned to judge this entry in prelims');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if a score already exists
|
|
||||||
if (! $prelimScoreSheet) {
|
|
||||||
if (PrelimScoreSheet::where('user_id', $user->id)->where('entry_id', $entry->id)->exists()) {
|
|
||||||
throw new AuditionAdminException('That judge has already entered a prelim score for that entry');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if ($prelimScoreSheet->user_id != $user->id) {
|
|
||||||
throw new AuditionAdminException('Existing score sheet is from a different judge');
|
|
||||||
}
|
|
||||||
if ($prelimScoreSheet->entry_id != $entry->id) {
|
|
||||||
throw new AuditionAdminException('Existing score sheet is for a different entry');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check the validity of submitted subscores, format array for storage, and sum score
|
|
||||||
$subscoresRequired = $prelimDefinition->scoringGuide->subscores;
|
|
||||||
$subscoresStorageArray = [];
|
|
||||||
$totalScore = 0;
|
|
||||||
$maxPossibleTotal = 0;
|
|
||||||
if ($scores->count() !== $subscoresRequired->count()) {
|
|
||||||
throw new AuditionAdminException('Invalid number of scores');
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($subscoresRequired as $subscore) {
|
|
||||||
// check that there is an element in the $scores collection with the key = $subscore->id
|
|
||||||
if (! $scores->keys()->contains($subscore->id)) {
|
|
||||||
throw new AuditionAdminException('Invalid Score Submission');
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($scores[$subscore->id] > $subscore->max_score) {
|
|
||||||
throw new AuditionAdminException('Supplied subscore exceeds maximum allowed');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add subscore to the storage array
|
|
||||||
$subscoresStorageArray[$subscore->id] = [
|
|
||||||
'score' => $scores[$subscore->id],
|
|
||||||
'subscore_id' => $subscore->id,
|
|
||||||
'subscore_name' => $subscore->name,
|
|
||||||
];
|
|
||||||
|
|
||||||
// Multiply subscore by weight then add to total
|
|
||||||
$totalScore += ($subscore->weight * $scores[$subscore->id]);
|
|
||||||
$maxPossibleTotal += ($subscore->weight * $subscore->max_score);
|
|
||||||
}
|
|
||||||
$finalTotalScore = ($maxPossibleTotal === 0) ? 0 : (($totalScore / $maxPossibleTotal) * 100);
|
|
||||||
|
|
||||||
$entry->removeFlag('no_show');
|
|
||||||
if ($prelimScoreSheet instanceof PrelimScoreSheet) {
|
|
||||||
$prelimScoreSheet->update([
|
|
||||||
'user_id' => $user->id,
|
|
||||||
'entry_id' => $entry->id,
|
|
||||||
'subscores' => $subscoresStorageArray,
|
|
||||||
'total' => $finalTotalScore,
|
|
||||||
]);
|
|
||||||
} else {
|
|
||||||
$prelimScoreSheet = PrelimScoreSheet::create([
|
|
||||||
'user_id' => $user->id,
|
|
||||||
'entry_id' => $entry->id,
|
|
||||||
'subscores' => $subscoresStorageArray,
|
|
||||||
'total' => $finalTotalScore,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log the prelim score entry
|
|
||||||
$log_message = 'Entered prelim score for entry id '.$entry->id.'.<br />';
|
|
||||||
$log_message .= 'Judge: '.$user->full_name().'<br />';
|
|
||||||
foreach ($prelimScoreSheet->subscores as $subscore) {
|
|
||||||
$log_message .= $subscore['subscore_name'].': '.$subscore['score'].'<br />';
|
|
||||||
}
|
|
||||||
$log_message .= 'Total :'.$prelimScoreSheet->total.'<br />';
|
|
||||||
auditionLog($log_message, [
|
|
||||||
'entries' => [$entry->id],
|
|
||||||
'users' => [$user->id],
|
|
||||||
'auditions' => [$entry->audition_id],
|
|
||||||
'students' => [$entry->student_id],
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Check if we can make a status decision
|
|
||||||
$checker = app(CheckPrelimResult::class);
|
|
||||||
$checker($entry, true);
|
|
||||||
|
|
||||||
return $prelimScoreSheet;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -6,151 +6,121 @@
|
||||||
|
|
||||||
namespace App\Actions\Tabulation;
|
namespace App\Actions\Tabulation;
|
||||||
|
|
||||||
use App\Exceptions\AuditionAdminException;
|
|
||||||
use App\Exceptions\ScoreEntryException;
|
use App\Exceptions\ScoreEntryException;
|
||||||
use App\Models\AuditLogEntry;
|
use App\Models\CalculatedScore;
|
||||||
use App\Models\Entry;
|
use App\Models\Entry;
|
||||||
use App\Models\EntryTotalScore;
|
|
||||||
use App\Models\ScoreSheet;
|
use App\Models\ScoreSheet;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
use function auth;
|
|
||||||
|
|
||||||
class EnterScore
|
class EnterScore
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @param User $user A user acting as the judge for this sheet
|
* @param User $user A user acting as the judge for this sheet
|
||||||
* @param Entry $entry An entry to which this score should be assigned
|
* @param Entry $entry An entry to which this score should be assigned
|
||||||
* @param array $scores Scores to be entered in the form of SubscoreID => score
|
* @param array $scores Scores to be entered in the form of SubscoreID => score
|
||||||
* @param ScoreSheet|false $scoreSheet If this is an update to an existing scoresheet, pass it here
|
|
||||||
* @return ScoreSheet The scoresheet that was created or updated
|
|
||||||
*
|
*
|
||||||
* @throws ScoreEntryException
|
* @throws ScoreEntryException
|
||||||
*/
|
*/
|
||||||
public function __invoke(User $user, Entry $entry, array $scores, ScoreSheet|false $scoreSheet = false): ScoreSheet
|
public function __invoke(User $user, Entry $entry, array $scores, ScoreSheet|false $scoreSheet = false): ScoreSheet
|
||||||
{
|
{
|
||||||
EntryTotalScore::where('entry_id', $entry->id)->delete();
|
CalculatedScore::where('entry_id', $entry->id)->delete();
|
||||||
$scores = collect($scores);
|
$scores = collect($scores);
|
||||||
|
$this->basicChecks($user, $entry, $scores);
|
||||||
// Basic Validity Checks
|
$this->checkJudgeAssignment($user, $entry);
|
||||||
if (! User::where('id', $user->id)->exists()) {
|
$this->checkForExistingScore($user, $entry, $scoreSheet);
|
||||||
throw new AuditionAdminException('User does not exist');
|
$this->validateScoresSubmitted($entry, $scores);
|
||||||
}
|
|
||||||
if (! Entry::where('id', $entry->id)->exists()) {
|
|
||||||
throw new AuditionAdminException('Entry does not exist');
|
|
||||||
}
|
|
||||||
if ($entry->audition->hasFlag('seats_published')) {
|
|
||||||
throw new AuditionAdminException('Cannot score an entry in an audition where seats are published');
|
|
||||||
}
|
|
||||||
if ($entry->audition->hasFlag('advancement_published')) {
|
|
||||||
throw new AuditionAdminException('Cannot score an entry in an audition where advancement is published');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check that the specified user is assigned to judge this entry
|
|
||||||
$check = DB::table('room_user')
|
|
||||||
->where('room_id', $entry->audition->room_id)
|
|
||||||
->where('user_id', $user->id)->exists();
|
|
||||||
if (! $check) {
|
|
||||||
throw new AuditionAdminException('This judge is not assigned to judge this entry');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if a score already exists
|
|
||||||
if (! $scoreSheet) {
|
|
||||||
if (ScoreSheet::where('user_id', $user->id)->where('entry_id', $entry->id)->exists()) {
|
|
||||||
throw new AuditionAdminException('That judge has already entered scores for that entry');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if ($scoreSheet->user_id !== $user->id) {
|
|
||||||
throw new AuditionAdminException('Existing score sheet is from a different judge');
|
|
||||||
}
|
|
||||||
if ($scoreSheet->entry_id !== $entry->id) {
|
|
||||||
throw new AuditionAdminException('Existing score sheet is for a different entry');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check the validity of submitted subscores, format array for storage, and sum score
|
|
||||||
$subscoresRequired = $entry->audition->scoringGuide->subscores;
|
|
||||||
$subscoresStorageArray = [];
|
|
||||||
$seatingTotal = 0;
|
|
||||||
$seatingMaxPossible = 0;
|
|
||||||
$advancementTotal = 0;
|
|
||||||
$advancementMaxPossible = 0;
|
|
||||||
if ($scores->count() !== $subscoresRequired->count()) {
|
|
||||||
throw new AuditionAdminException('Invalid number of scores');
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($subscoresRequired as $subscore) {
|
|
||||||
// check that there is an element in the $scores collection with the key = $subscore->id
|
|
||||||
if (! $scores->keys()->contains($subscore->id)) {
|
|
||||||
throw new AuditionAdminException('Invalid Score Submission');
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($scores[$subscore->id] > $subscore->max_score) {
|
|
||||||
throw new AuditionAdminException('Supplied subscore exceeds maximum allowed');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add subscore to the storage array
|
|
||||||
$subscoresStorageArray[$subscore->id] = [
|
|
||||||
'score' => $scores[$subscore->id],
|
|
||||||
'subscore_id' => $subscore->id,
|
|
||||||
'subscore_name' => $subscore->name,
|
|
||||||
];
|
|
||||||
|
|
||||||
// If included in seating, multiply by weight and add to the total and max possible
|
|
||||||
if ($subscore->for_seating) {
|
|
||||||
$seatingTotal += ($subscore->weight * $scores[$subscore->id]);
|
|
||||||
$seatingMaxPossible += ($subscore->weight * $subscore->max_score);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If included in advancement, multiply by weight and add to the total and max possible
|
|
||||||
if ($subscore->for_advance) {
|
|
||||||
$advancementTotal += ($subscore->weight * $scores[$subscore->id]);
|
|
||||||
$advancementMaxPossible += ($subscore->weight * $subscore->max_score);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$finalSeatingTotal = ($seatingMaxPossible === 0) ? 0 : (($seatingTotal / $seatingMaxPossible) * 100);
|
|
||||||
$finalAdvancementTotal = ($advancementMaxPossible === 0) ? 0 : (($advancementTotal / $advancementMaxPossible) * 100);
|
|
||||||
|
|
||||||
$entry->removeFlag('no_show');
|
$entry->removeFlag('no_show');
|
||||||
if ($scoreSheet instanceof ScoreSheet) {
|
if ($scoreSheet instanceof ScoreSheet) {
|
||||||
$scoreSheet->update([
|
$scoreSheet->update([
|
||||||
'user_id' => $user->id,
|
'user_id' => $user->id,
|
||||||
'entry_id' => $entry->id,
|
'entry_id' => $entry->id,
|
||||||
'subscores' => $subscoresStorageArray,
|
'subscores' => $this->subscoresForStorage($entry, $scores),
|
||||||
'seating_total' => $finalSeatingTotal,
|
|
||||||
'advancement_total' => $finalAdvancementTotal,
|
|
||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
$scoreSheet = ScoreSheet::create([
|
$scoreSheet = ScoreSheet::create([
|
||||||
'user_id' => $user->id,
|
'user_id' => $user->id,
|
||||||
'entry_id' => $entry->id,
|
'entry_id' => $entry->id,
|
||||||
'subscores' => $subscoresStorageArray,
|
'subscores' => $this->subscoresForStorage($entry, $scores),
|
||||||
'seating_total' => $finalSeatingTotal,
|
|
||||||
'advancement_total' => $finalAdvancementTotal,
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log the score entry
|
|
||||||
$log_message = 'Entered Score for entry id '.$entry->id.'.<br />';
|
|
||||||
$log_message .= 'Judge: '.$user->full_name().'<br />';
|
|
||||||
foreach ($scoreSheet->subscores as $subscore) {
|
|
||||||
$log_message .= $subscore['subscore_name'].': '.$subscore['score'].'<br />';
|
|
||||||
}
|
|
||||||
$log_message .= 'Seating Total: '.$scoreSheet->seating_total.'<br />';
|
|
||||||
$log_message .= 'Advancement Total: '.$scoreSheet->advancement_total.'<br />';
|
|
||||||
AuditLogEntry::create([
|
|
||||||
'user' => auth()->user()->email ?? 'no user',
|
|
||||||
'ip_address' => request()->ip(),
|
|
||||||
'message' => $log_message,
|
|
||||||
'affected' => [
|
|
||||||
'entries' => [$entry->id],
|
|
||||||
'users' => [$user->id],
|
|
||||||
'auditions' => [$entry->audition_id],
|
|
||||||
'students' => [$entry->student_id],
|
|
||||||
],
|
|
||||||
]);
|
|
||||||
|
|
||||||
return $scoreSheet;
|
return $scoreSheet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function subscoresForStorage(Entry $entry, Collection $scores)
|
||||||
|
{
|
||||||
|
$subscores = [];
|
||||||
|
foreach ($entry->audition->scoringGuide->subscores as $subscore) {
|
||||||
|
$subscores[$subscore->id] = [
|
||||||
|
'score' => $scores[$subscore->id],
|
||||||
|
'subscore_id' => $subscore->id,
|
||||||
|
'subscore_name' => $subscore->name,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $subscores;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function checkForExistingScore(User $user, Entry $entry, $existingScoreSheet)
|
||||||
|
{
|
||||||
|
if (! $existingScoreSheet) {
|
||||||
|
if (ScoreSheet::where('user_id', $user->id)->where('entry_id', $entry->id)->exists()) {
|
||||||
|
throw new ScoreEntryException('That judge has already entered scores for that entry');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ($existingScoreSheet->user_id !== $user->id) {
|
||||||
|
throw new ScoreEntryException('Existing score sheet is from a different judge');
|
||||||
|
}
|
||||||
|
if ($existingScoreSheet->entry_id !== $entry->id) {
|
||||||
|
throw new ScoreEntryException('Existing score sheet is for a different entry');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function validateScoresSubmitted(Entry $entry, Collection $scores)
|
||||||
|
{
|
||||||
|
$subscoresRequired = $entry->audition->scoringGuide->subscores;
|
||||||
|
|
||||||
|
foreach ($subscoresRequired as $subscore) {
|
||||||
|
// check that there is an element in the $scores collection with the key = $subscore->id
|
||||||
|
if (! $scores->keys()->contains($subscore->id)) {
|
||||||
|
throw new ScoreEntryException('Invalid Score Submission');
|
||||||
|
}
|
||||||
|
if ($scores[$subscore->id] > $subscore->max_score) {
|
||||||
|
throw new ScoreEntryException('Supplied subscore exceeds maximum allowed');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function checkJudgeAssignment(User $user, Entry $entry)
|
||||||
|
{
|
||||||
|
$check = DB::table('room_user')
|
||||||
|
->where('room_id', $entry->audition->room_id)
|
||||||
|
->where('user_id', $user->id)->exists();
|
||||||
|
if (! $check) {
|
||||||
|
throw new ScoreEntryException('This judge is not assigned to judge this entry');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function basicChecks(User $user, Entry $entry, Collection $scores)
|
||||||
|
{
|
||||||
|
if (! $user->exists()) {
|
||||||
|
throw new ScoreEntryException('User does not exist');
|
||||||
|
}
|
||||||
|
if (! $entry->exists()) {
|
||||||
|
throw new ScoreEntryException('Entry does not exist');
|
||||||
|
}
|
||||||
|
if ($entry->audition->hasFlag('seats_published')) {
|
||||||
|
throw new ScoreEntryException('Cannot score an entry in an audition with published seats');
|
||||||
|
}
|
||||||
|
if ($entry->audition->hasFlag('advancement_published')) {
|
||||||
|
throw new ScoreEntryException('Cannot score an entry in an audition with published advancement');
|
||||||
|
}
|
||||||
|
$requiredScores = $entry->audition->scoringGuide->subscores()->count();
|
||||||
|
if ($scores->count() !== $requiredScores) {
|
||||||
|
throw new ScoreEntryException('Invalid number of scores');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Actions\Tabulation;
|
|
||||||
|
|
||||||
use App\Models\Entry;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @codeCoverageIgnore
|
|
||||||
*/
|
|
||||||
class ForceRecalculateTotalScores
|
|
||||||
{
|
|
||||||
public function __invoke(): void
|
|
||||||
{
|
|
||||||
$calculator = app(TotalEntryScores::class);
|
|
||||||
foreach (Entry::all() as $entry) {
|
|
||||||
$calculator($entry, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -2,15 +2,11 @@
|
||||||
|
|
||||||
namespace App\Actions\Tabulation;
|
namespace App\Actions\Tabulation;
|
||||||
|
|
||||||
use App\Exceptions\AuditionAdminException;
|
|
||||||
use App\Models\Audition;
|
use App\Models\Audition;
|
||||||
use App\Models\Ensemble;
|
use App\Models\Ensemble;
|
||||||
use App\Models\Seat;
|
use App\Models\Seat;
|
||||||
|
use function dd;
|
||||||
|
|
||||||
/**
|
|
||||||
* @codeCoverageIgnore
|
|
||||||
*/
|
|
||||||
// TODO delete if truly depricated
|
|
||||||
class GetAuditionSeats
|
class GetAuditionSeats
|
||||||
{
|
{
|
||||||
public function __construct()
|
public function __construct()
|
||||||
|
|
@ -24,7 +20,6 @@ class GetAuditionSeats
|
||||||
|
|
||||||
protected function getSeats(Audition $audition)
|
protected function getSeats(Audition $audition)
|
||||||
{
|
{
|
||||||
throw new AuditionAdminException('This method is being considered for deletion.');
|
|
||||||
$ensembles = Ensemble::where('event_id', $audition->event_id)->orderBy('rank')->get();
|
$ensembles = Ensemble::where('event_id', $audition->event_id)->orderBy('rank')->get();
|
||||||
$seats = Seat::with('student.school')->where('audition_id', $audition->id)->orderBy('seat')->get();
|
$seats = Seat::with('student.school')->where('audition_id', $audition->id)->orderBy('seat')->get();
|
||||||
$return = [];
|
$return = [];
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
namespace App\Actions\Tabulation;
|
namespace App\Actions\Tabulation;
|
||||||
|
|
||||||
use App\Exceptions\AuditionAdminException;
|
|
||||||
use App\Models\Audition;
|
use App\Models\Audition;
|
||||||
use App\Models\Seat;
|
use App\Models\Seat;
|
||||||
use Illuminate\Support\Facades\Cache;
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
|
@ -14,32 +13,8 @@ class PublishSeats
|
||||||
//
|
//
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Publishes the given audition with the provided seats.
|
|
||||||
*
|
|
||||||
* This method first validates that the seats array is not empty. If the array is empty,
|
|
||||||
* an AuditionAdminException is thrown.
|
|
||||||
*
|
|
||||||
* Next, it deletes existing records in the `seats` table associated with the provided audition
|
|
||||||
* using the `audition_id`.
|
|
||||||
*
|
|
||||||
* Then, it iterates through the provided seats array to create new records in the `seats` table
|
|
||||||
* with the specified `ensemble_id`, `audition_id`, `seat`, and `entry_id`.
|
|
||||||
*
|
|
||||||
* Finally, it marks the audition as having its seats published by adding a relevant flag
|
|
||||||
* to the audition, and clears cached data associated with the results seat list and
|
|
||||||
* public results page entries in the cache store.
|
|
||||||
*
|
|
||||||
* @param Audition $audition The audition instance to be published.
|
|
||||||
* @param array $seats An array of seat data to be associated with the audition.
|
|
||||||
*
|
|
||||||
* @throws AuditionAdminException If the provided seats array is empty.
|
|
||||||
*/
|
|
||||||
public function __invoke(Audition $audition, array $seats): void
|
public function __invoke(Audition $audition, array $seats): void
|
||||||
{
|
{
|
||||||
if (count($seats) === 0) {
|
|
||||||
throw new AuditionAdminException('Cannot publish an audition with no seats.');
|
|
||||||
}
|
|
||||||
// Delete from the seats table where audition_id = $audition->id
|
// Delete from the seats table where audition_id = $audition->id
|
||||||
Seat::where('audition_id', $audition->id)->delete();
|
Seat::where('audition_id', $audition->id)->delete();
|
||||||
foreach ($seats as $seat) {
|
foreach ($seats as $seat) {
|
||||||
|
|
|
||||||
|
|
@ -4,115 +4,105 @@
|
||||||
|
|
||||||
namespace App\Actions\Tabulation;
|
namespace App\Actions\Tabulation;
|
||||||
|
|
||||||
use App\Exceptions\AuditionAdminException;
|
use App\Exceptions\TabulationException;
|
||||||
use App\Models\Audition;
|
use App\Models\Audition;
|
||||||
use App\Models\Entry;
|
use Illuminate\Database\Eloquent\Collection;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
|
||||||
|
use function is_numeric;
|
||||||
|
|
||||||
class RankAuditionEntries
|
class RankAuditionEntries
|
||||||
{
|
{
|
||||||
/**
|
protected CalculateEntryScore $calculator;
|
||||||
* Get ranked entries for the provided audition for either seating or advancement.
|
|
||||||
*
|
public function __construct(CalculateEntryScore $calculator)
|
||||||
* If the rank_type is seating, the ranked entries are returned in descending order of seating total.
|
|
||||||
* If the rank_type is advancement, the ranked entries are returned in descending order of advancement total.
|
|
||||||
*
|
|
||||||
* The ranked entries are returned as a Collection of Entry objects.
|
|
||||||
*
|
|
||||||
* @param string $rank_type advancement|seating
|
|
||||||
* @return Collection<Entry>|void
|
|
||||||
*
|
|
||||||
* @throws AuditionAdminException
|
|
||||||
*/
|
|
||||||
public function __invoke(Audition $audition, string $rank_type, bool $pullDeclinedEntries = true): Collection|Entry
|
|
||||||
{
|
{
|
||||||
if ($rank_type !== 'seating' && $rank_type !== 'advancement') {
|
$this->calculator = $calculator;
|
||||||
throw new AuditionAdminException('Invalid rank type (must be seating or advancement)');
|
}
|
||||||
}
|
|
||||||
|
|
||||||
$cache_duration = 15;
|
public function rank(string $mode, Audition $audition): Collection
|
||||||
|
{
|
||||||
|
$cacheKey = 'audition'.$audition->id.$mode;
|
||||||
|
|
||||||
if ($rank_type === 'seating') {
|
return Cache::remember($cacheKey, 300, function () use ($mode, $audition) {
|
||||||
return cache()->remember('rank_seating_'.$audition->id, $cache_duration, function () use ($audition, $pullDeclinedEntries) {
|
return $this->calculateRank($mode, $audition);
|
||||||
return $this->get_seating_ranks($audition, $pullDeclinedEntries);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return cache()->remember('rank_advancement_'.$audition->id, $cache_duration, function () use ($audition) {
|
|
||||||
return $this->get_advancement_ranks($audition);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function get_seating_ranks(Audition $audition, bool $pullDeclinedEntries = true): Collection|Entry
|
/**
|
||||||
|
* For a given audition, return a collection of entries ranked by total score. Each entry will have a
|
||||||
|
* property rank that either is their rank or a flag reflecting no-show, declined, or failed-prelim status
|
||||||
|
*
|
||||||
|
* @throws TabulationException
|
||||||
|
*/
|
||||||
|
public function calculateRank(string $mode, Audition $audition): Collection
|
||||||
{
|
{
|
||||||
if ($audition->bonusScore()->count() > 0) {
|
$this->basicValidation($mode, $audition);
|
||||||
$totalColumn = 'seating_total_with_bonus';
|
$entries = match ($mode) {
|
||||||
} else {
|
'seating' => $audition->entries()->forSeating()->with('scoreSheets')->withCount('bonusScores')->get(),
|
||||||
$totalColumn = 'seating_total';
|
'advancement' => $audition->entries()->forAdvancement()->with('scoreSheets')->get(),
|
||||||
}
|
};
|
||||||
|
|
||||||
$sortedEntries = $audition->entries()
|
foreach ($entries as $entry) {
|
||||||
->where('for_seating', true)
|
$entry->setRelation('audition', $audition);
|
||||||
->whereHas('totalScore')
|
try {
|
||||||
->with('totalScore')
|
$entry->score_totals = $this->calculator->calculate($mode, $entry);
|
||||||
->with('student.school')
|
} catch (TabulationException $ex) {
|
||||||
->with('audition')
|
$entry->score_totals = [-1];
|
||||||
->join('entry_total_scores', 'entries.id', '=', 'entry_total_scores.entry_id')
|
$entry->score_message = $ex->getMessage();
|
||||||
->orderBy('entry_total_scores.'.$totalColumn, 'desc')
|
|
||||||
->orderByRaw('COALESCE(JSON_EXTRACT(entry_total_scores.seating_subscore_totals, "$[0]"), -999999) DESC')
|
|
||||||
->orderByRaw('COALESCE(JSON_EXTRACT(entry_total_scores.seating_subscore_totals, "$[1]"), -999999) DESC')
|
|
||||||
->orderByRaw('COALESCE(JSON_EXTRACT(entry_total_scores.seating_subscore_totals, "$[2]"), -999999) DESC')
|
|
||||||
->orderByRaw('COALESCE(JSON_EXTRACT(entry_total_scores.seating_subscore_totals, "$[3]"), -999999) DESC')
|
|
||||||
->orderByRaw('COALESCE(JSON_EXTRACT(entry_total_scores.seating_subscore_totals, "$[4]"), -999999) DESC')
|
|
||||||
->orderByRaw('COALESCE(JSON_EXTRACT(entry_total_scores.seating_subscore_totals, "$[5]"), -999999) DESC')
|
|
||||||
->orderByRaw('COALESCE(JSON_EXTRACT(entry_total_scores.seating_subscore_totals, "$[6]"), -999999) DESC')
|
|
||||||
->orderByRaw('COALESCE(JSON_EXTRACT(entry_total_scores.seating_subscore_totals, "$[7]"), -999999) DESC')
|
|
||||||
->orderByRaw('COALESCE(JSON_EXTRACT(entry_total_scores.seating_subscore_totals, "$[8]"), -999999) DESC')
|
|
||||||
->orderByRaw('COALESCE(JSON_EXTRACT(entry_total_scores.seating_subscore_totals, "$[9]"), -999999) DESC')
|
|
||||||
->select('entries.*')
|
|
||||||
->get();
|
|
||||||
|
|
||||||
$rankOn = 1;
|
|
||||||
foreach ($sortedEntries as $entry) {
|
|
||||||
if ($entry->hasFlag('declined') && $pullDeclinedEntries) {
|
|
||||||
$entry->seatingRank = 'declined';
|
|
||||||
} else {
|
|
||||||
$entry->seatingRank = $rankOn;
|
|
||||||
$rankOn++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Sort entries based on their total score, then by subscores in tiebreak order
|
||||||
|
$entries = $entries->sort(function ($a, $b) {
|
||||||
|
for ($i = 0; $i < count($a->score_totals); $i++) {
|
||||||
|
if (! array_key_exists($i, $a->score_totals)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (! array_key_exists($i, $b->score_totals)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if ($a->score_totals[$i] > $b->score_totals[$i]) {
|
||||||
|
return -1;
|
||||||
|
} elseif ($a->score_totals[$i] < $b->score_totals[$i]) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return $sortedEntries;
|
return 0;
|
||||||
}
|
});
|
||||||
|
$rank = 1;
|
||||||
|
$rawRank = 1;
|
||||||
|
foreach ($entries as $entry) {
|
||||||
|
$entry->rank = $rank;
|
||||||
|
$entry->raw_rank = $rawRank;
|
||||||
|
// We don't really get a rank for seating if we have certain flags
|
||||||
|
if ($mode === 'seating') {
|
||||||
|
if ($entry->hasFlag('failed_prelim')) {
|
||||||
|
$entry->rank = 'Failed Prelim';
|
||||||
|
} elseif ($entry->hasFlag('declined')) {
|
||||||
|
$entry->rank = 'Declined';
|
||||||
|
} elseif ($entry->hasFlag('no_show')) {
|
||||||
|
$entry->rank = 'No Show';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private function get_advancement_ranks(Audition $audition): Collection|Entry
|
if (is_numeric($entry->rank)) {
|
||||||
{
|
$rank++;
|
||||||
$sortedEntries = $audition->entries()
|
}
|
||||||
->whereHas('totalScore')
|
$rawRank++;
|
||||||
->with('totalScore')
|
|
||||||
->with('student.school')
|
|
||||||
->with('audition')
|
|
||||||
->join('entry_total_scores', 'entries.id', '=', 'entry_total_scores.entry_id')
|
|
||||||
->orderBy('entry_total_scores.advancement_total', 'desc')
|
|
||||||
->orderByRaw('COALESCE(JSON_EXTRACT(entry_total_scores.advancement_subscore_totals, "$[0]"), -999999) DESC')
|
|
||||||
->orderByRaw('COALESCE(JSON_EXTRACT(entry_total_scores.advancement_subscore_totals, "$[1]"), -999999) DESC')
|
|
||||||
->orderByRaw('COALESCE(JSON_EXTRACT(entry_total_scores.advancement_subscore_totals, "$[2]"), -999999) DESC')
|
|
||||||
->orderByRaw('COALESCE(JSON_EXTRACT(entry_total_scores.advancement_subscore_totals, "$[3]"), -999999) DESC')
|
|
||||||
->orderByRaw('COALESCE(JSON_EXTRACT(entry_total_scores.advancement_subscore_totals, "$[4]"), -999999) DESC')
|
|
||||||
->orderByRaw('COALESCE(JSON_EXTRACT(entry_total_scores.advancement_subscore_totals, "$[5]"), -999999) DESC')
|
|
||||||
->orderByRaw('COALESCE(JSON_EXTRACT(entry_total_scores.advancement_subscore_totals, "$[6]"), -999999) DESC')
|
|
||||||
->orderByRaw('COALESCE(JSON_EXTRACT(entry_total_scores.advancement_subscore_totals, "$[7]"), -999999) DESC')
|
|
||||||
->orderByRaw('COALESCE(JSON_EXTRACT(entry_total_scores.advancement_subscore_totals, "$[8]"), -999999) DESC')
|
|
||||||
->orderByRaw('COALESCE(JSON_EXTRACT(entry_total_scores.advancement_subscore_totals, "$[9]"), -999999) DESC')
|
|
||||||
->select('entries.*')
|
|
||||||
->get();
|
|
||||||
$n = 1;
|
|
||||||
foreach ($sortedEntries as $entry) {
|
|
||||||
$entry->advancementRank = $n;
|
|
||||||
$n++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $sortedEntries;
|
return $entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function basicValidation($mode, Audition $audition): void
|
||||||
|
{
|
||||||
|
if ($mode !== 'seating' && $mode !== 'advancement') {
|
||||||
|
throw new TabulationException('Mode must be seating or advancement');
|
||||||
|
}
|
||||||
|
if (! $audition->exists()) {
|
||||||
|
throw new TabulationException('Invalid audition provided');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,92 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Actions\Tabulation;
|
|
||||||
|
|
||||||
use App\Models\BonusScore;
|
|
||||||
use App\Models\Entry;
|
|
||||||
use App\Models\EntryTotalScore;
|
|
||||||
use App\Models\ScoreSheet;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles the calculation of a total score for an entry, including seating and advancement scores,
|
|
||||||
* based on scoring sheets and subscores defined in the audition's scoring guide.
|
|
||||||
*/
|
|
||||||
class TotalEntryScores
|
|
||||||
{
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public function __invoke(Entry $entry, bool $force_recalculation = false): void
|
|
||||||
{
|
|
||||||
if ($force_recalculation) {
|
|
||||||
EntryTotalScore::where('entry_id', $entry->id)->delete();
|
|
||||||
}
|
|
||||||
// bail out if a total score is already calculated
|
|
||||||
if (EntryTotalScore::where('entry_id', $entry->id)->count() > 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$requiredSubscores = $entry->audition->scoringGuide->subscores;
|
|
||||||
$newTotaledScore = EntryTotalScore::make();
|
|
||||||
$newTotaledScore->entry_id = $entry->id;
|
|
||||||
|
|
||||||
// deal with seating scores
|
|
||||||
// TODO: Consider a rewrite to pull the scoreSheets from the entry model so they may be preloaded
|
|
||||||
$scoreSheets = ScoreSheet::where('entry_id', $entry->id)->orderBy('seating_total', 'desc')->get();
|
|
||||||
|
|
||||||
// bail out if there are not enough score sheets
|
|
||||||
$assignedJudges = $entry->audition->judges()->count();
|
|
||||||
if ($scoreSheets->count() == 0 || $scoreSheets->count() < $assignedJudges) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (auditionSetting('olympic_scoring') && $scoreSheets->count() > 2) {
|
|
||||||
// under olympic scoring, drop the first and last element
|
|
||||||
$scoreSheets->shift();
|
|
||||||
$scoreSheets->pop();
|
|
||||||
}
|
|
||||||
$newTotaledScore->seating_total = round($scoreSheets->avg('seating_total'), 6);
|
|
||||||
$seatingSubscores = $requiredSubscores
|
|
||||||
->filter(fn ($subscore) => $subscore->for_seating == true)
|
|
||||||
->sortBy('tiebreak_order');
|
|
||||||
$total_seating_subscores = [];
|
|
||||||
foreach ($seatingSubscores as $subscore) {
|
|
||||||
$runningTotal = 0;
|
|
||||||
foreach ($scoreSheets as $scoreSheet) {
|
|
||||||
$runningTotal += $scoreSheet->subscores[$subscore->id]['score'];
|
|
||||||
}
|
|
||||||
$total_seating_subscores[] = round($runningTotal / $scoreSheets->count(), 4);
|
|
||||||
}
|
|
||||||
$newTotaledScore->seating_subscore_totals = $total_seating_subscores;
|
|
||||||
|
|
||||||
// deal with advancement scores
|
|
||||||
$scoreSheets = ScoreSheet::where('entry_id', $entry->id)->orderBy('advancement_total', 'desc')->get();
|
|
||||||
if (auditionSetting('olympic_scoring') && $scoreSheets->count() > 2) {
|
|
||||||
// under olympic scoring, drop the first and last element
|
|
||||||
$scoreSheets->shift();
|
|
||||||
$scoreSheets->pop();
|
|
||||||
}
|
|
||||||
$newTotaledScore->advancement_total = round($scoreSheets->avg('advancement_total'), 6);
|
|
||||||
$advancement_subscores = $requiredSubscores
|
|
||||||
->filter(fn ($subscore) => $subscore->for_advance == true)
|
|
||||||
->sortBy('tiebreak_order');
|
|
||||||
$total_advancement_subscores = [];
|
|
||||||
foreach ($advancement_subscores as $subscore) {
|
|
||||||
$runningTotal = 0;
|
|
||||||
foreach ($scoreSheets as $scoreSheet) {
|
|
||||||
$runningTotal += $scoreSheet->subscores[$subscore->id]['score'];
|
|
||||||
}
|
|
||||||
$total_advancement_subscores[] = round($runningTotal / $scoreSheets->count(), 4);
|
|
||||||
}
|
|
||||||
$newTotaledScore->advancement_subscore_totals = $total_advancement_subscores;
|
|
||||||
|
|
||||||
// pull in bonus scores
|
|
||||||
$bonusScores = BonusScore::where('entry_id', $entry->id)
|
|
||||||
->selectRaw('SUM(score) as total')
|
|
||||||
->value('total');
|
|
||||||
|
|
||||||
$newTotaledScore->bonus_total = $bonusScores;
|
|
||||||
|
|
||||||
$newTotaledScore->save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,41 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Actions\YearEndProcedures;
|
|
||||||
|
|
||||||
use App\Exceptions\AuditionAdminException;
|
|
||||||
use App\Models\HistoricalSeat;
|
|
||||||
use App\Models\Seat;
|
|
||||||
use Carbon\Carbon;
|
|
||||||
|
|
||||||
class RecordHistoricalSeats
|
|
||||||
{
|
|
||||||
public function __invoke(): void
|
|
||||||
{
|
|
||||||
$this->saveSeats();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @throws AuditionAdminException
|
|
||||||
*/
|
|
||||||
public function saveSeats()
|
|
||||||
{
|
|
||||||
if (! auth()->user() or ! auth()->user()->is_admin) {
|
|
||||||
throw new AuditionAdminException('Only administrators may perform this action');
|
|
||||||
}
|
|
||||||
$seats = Seat::all();
|
|
||||||
if ($seats->count() > 0) {
|
|
||||||
foreach ($seats as $seat) {
|
|
||||||
$student_id = $seat->student->id;
|
|
||||||
$year = Carbon::now()->year;
|
|
||||||
$seat_description = $seat->ensemble->name.' - '.$seat->audition->name.' - '.$seat->seat;
|
|
||||||
HistoricalSeat::create([
|
|
||||||
'student_id' => $student_id,
|
|
||||||
'year' => $year,
|
|
||||||
'seat_description' => $seat_description,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,88 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Actions\YearEndProcedures;
|
|
||||||
|
|
||||||
use App\Exceptions\AuditionAdminException;
|
|
||||||
use App\Models\AuditionFlag;
|
|
||||||
use App\Models\AuditLogEntry;
|
|
||||||
use App\Models\BonusScore;
|
|
||||||
use App\Models\Doubler;
|
|
||||||
use App\Models\DoublerRequest;
|
|
||||||
use App\Models\EntryFlag;
|
|
||||||
use App\Models\EntryTotalScore;
|
|
||||||
use App\Models\JudgeAdvancementVote;
|
|
||||||
use App\Models\NominationEnsembleEntry;
|
|
||||||
use App\Models\ScoreSheet;
|
|
||||||
use App\Models\Seat;
|
|
||||||
use App\Models\Student;
|
|
||||||
use App\Models\UserFlag;
|
|
||||||
use Illuminate\Support\Facades\DB;
|
|
||||||
|
|
||||||
use function auth;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @codeCoverageIgnore
|
|
||||||
*/
|
|
||||||
// TODO: figure out how to test YearEndCleanup
|
|
||||||
class YearEndCleanup
|
|
||||||
{
|
|
||||||
public function __invoke(?array $options = []): void
|
|
||||||
{
|
|
||||||
$this->cleanup($options);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $options array array of reset options - possible values are deleteRooms
|
|
||||||
* removeAuditionsFromRoom unassignJudges
|
|
||||||
*
|
|
||||||
* @throws AuditionAdminException
|
|
||||||
*/
|
|
||||||
public function cleanup(?array $options = []): true
|
|
||||||
{
|
|
||||||
|
|
||||||
if (! auth()->user() or ! auth()->user()->is_admin) {
|
|
||||||
throw new AuditionAdminException('Only administrators may perform this action');
|
|
||||||
}
|
|
||||||
|
|
||||||
$historian = new RecordHistoricalSeats;
|
|
||||||
$historian();
|
|
||||||
|
|
||||||
// Delete all records in the audit_log_entries table
|
|
||||||
AuditLogEntry::truncate();
|
|
||||||
AuditionFlag::truncate();
|
|
||||||
BonusScore::truncate();
|
|
||||||
EntryTotalScore::truncate();
|
|
||||||
DoublerRequest::truncate();
|
|
||||||
Doubler::truncate();
|
|
||||||
EntryFlag::truncate();
|
|
||||||
ScoreSheet::truncate();
|
|
||||||
Seat::truncate();
|
|
||||||
JudgeAdvancementVote::truncate();
|
|
||||||
DB::table('entries')->delete();
|
|
||||||
NominationEnsembleEntry::truncate();
|
|
||||||
|
|
||||||
Student::query()->increment('grade');
|
|
||||||
|
|
||||||
if (is_array($options)) {
|
|
||||||
if (in_array('deleteRooms', $options)) {
|
|
||||||
DB::table('auditions')->update(['room_id' => 0]);
|
|
||||||
DB::table('auditions')->update(['order_in_room' => '0']);
|
|
||||||
DB::table('room_user')->truncate();
|
|
||||||
DB::table('rooms')->where('id', '>', 0)->delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (in_array('removeAuditionsFromRoom', $options)) {
|
|
||||||
DB::table('auditions')->update(['room_id' => 0]);
|
|
||||||
DB::table('auditions')->update(['order_in_room' => '0']);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (in_array('unassignJudges', $options)) {
|
|
||||||
DB::table('room_user')->truncate();
|
|
||||||
UserFlag::where('flag', 'monitor')->delete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,41 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Console\Commands;
|
|
||||||
|
|
||||||
use App\Actions\Tabulation\EnterScore;
|
|
||||||
use App\Models\ScoreSheet;
|
|
||||||
use Illuminate\Console\Command;
|
|
||||||
|
|
||||||
class RecalculateJudgeTotalsCommand extends Command
|
|
||||||
{
|
|
||||||
protected $signature = 'audition:recalculate-judge-totals';
|
|
||||||
|
|
||||||
protected $description = 'Recalculates total scores for all score sheets for unpubished auditions';
|
|
||||||
|
|
||||||
public function handle(): void
|
|
||||||
{
|
|
||||||
$this->info('Starting score recalculation...');
|
|
||||||
$scoreSheets = ScoreSheet::all();
|
|
||||||
foreach ($scoreSheets as $scoreSheet) {
|
|
||||||
if ($scoreSheet->entry->audition->hasFlag('seats_published')) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$this->recalculate($scoreSheet);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->info('Score recalculation completed successfully.');
|
|
||||||
}
|
|
||||||
|
|
||||||
private function recalculate(ScoreSheet|int $scoreSheet): void
|
|
||||||
{
|
|
||||||
if (is_int($scoreSheet)) {
|
|
||||||
$scoreSheet = ScoreSheet::findOrFail($scoreSheet);
|
|
||||||
}
|
|
||||||
$scribe = app()->make(EnterScore::class);
|
|
||||||
$scoreSubmission = [];
|
|
||||||
foreach ($scoreSheet->subscores as $subscore) {
|
|
||||||
$scoreSubmission[$subscore['subscore_id']] = $subscore['score'];
|
|
||||||
}
|
|
||||||
$scribe($scoreSheet->judge, $scoreSheet->entry, $scoreSubmission, $scoreSheet);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,38 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Console\Commands;
|
|
||||||
|
|
||||||
use App\Actions\Tabulation\ForceRecalculateTotalScores;
|
|
||||||
use Illuminate\Console\Command;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @codeCoverageIgnore
|
|
||||||
*/
|
|
||||||
class RecalculateTotalScores extends Command
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* The name and signature of the console command.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $signature = 'audition:recalculate-total-scores';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The console command description.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $description = 'Forces the recalculation of total scores for all entries';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Execute the console command.
|
|
||||||
*/
|
|
||||||
public function handle(ForceRecalculateTotalScores $action): void
|
|
||||||
{
|
|
||||||
$this->info('Starting score recalculation...');
|
|
||||||
|
|
||||||
$action();
|
|
||||||
|
|
||||||
$this->info('Score recalculation completed successfully.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Console\Commands;
|
|
||||||
|
|
||||||
use App\Actions\Tabulation\DoublerSync;
|
|
||||||
use App\Models\Event;
|
|
||||||
use Illuminate\Console\Command;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @codeCoverageIgnore
|
|
||||||
*/
|
|
||||||
class SyncDoublers extends Command
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* The name and signature of the console command.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $signature = 'audition:sync-doublers {event? : Optional event ID}';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The console command description.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $description = 'Update doublers table based on current entries';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Execute the console command.
|
|
||||||
*/
|
|
||||||
public function handle()
|
|
||||||
{
|
|
||||||
$syncer = app(DoublerSync::class);
|
|
||||||
if ($eventId = $this->argument('event')) {
|
|
||||||
$event = Event::findOrFail($eventId);
|
|
||||||
$syncer($event);
|
|
||||||
$this->info("Synced doublers for event {$event->name}");
|
|
||||||
} else {
|
|
||||||
$syncer();
|
|
||||||
$this->info('Synced doublers for all events');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,87 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Console\Commands;
|
|
||||||
|
|
||||||
use App\Models\School;
|
|
||||||
use App\Models\Student;
|
|
||||||
use App\Models\User;
|
|
||||||
use Faker\Factory;
|
|
||||||
use Illuminate\Console\Command;
|
|
||||||
|
|
||||||
class fictionalize extends Command
|
|
||||||
{
|
|
||||||
protected $signature = 'audition:fictionalize
|
|
||||||
{--students : Fictionalize student names}
|
|
||||||
{--schools : Fictionalize school names}
|
|
||||||
{--users : Fictionalize user data}
|
|
||||||
{--all : Fictionalize all data types}';
|
|
||||||
|
|
||||||
protected $description = 'Replace real names with fictional data for specified entity types';
|
|
||||||
|
|
||||||
public function handle()
|
|
||||||
{
|
|
||||||
$faker = Factory::create();
|
|
||||||
|
|
||||||
// If no options are specified or --all is used, process everything
|
|
||||||
$processAll = $this->option('all') ||
|
|
||||||
(! $this->option('students') && ! $this->option('schools') && ! $this->option('users'));
|
|
||||||
|
|
||||||
if ($processAll || $this->option('students')) {
|
|
||||||
$this->info('Fictionalizing students...');
|
|
||||||
$bar = $this->output->createProgressBar(Student::count());
|
|
||||||
|
|
||||||
Student::chunk(100, function ($students) use ($faker, $bar) {
|
|
||||||
foreach ($students as $student) {
|
|
||||||
$student->update([
|
|
||||||
'first_name' => $faker->firstName(),
|
|
||||||
'last_name' => $faker->lastName(),
|
|
||||||
]);
|
|
||||||
$bar->advance();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$bar->finish();
|
|
||||||
$this->newLine();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($processAll || $this->option('schools')) {
|
|
||||||
$this->info('Fictionalizing schools...');
|
|
||||||
$bar = $this->output->createProgressBar(School::count());
|
|
||||||
|
|
||||||
School::chunk(100, function ($schools) use ($faker, $bar) {
|
|
||||||
foreach ($schools as $school) {
|
|
||||||
$school->update([
|
|
||||||
'name' => $faker->city().' High School',
|
|
||||||
]);
|
|
||||||
$bar->advance();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$bar->finish();
|
|
||||||
$this->newLine();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($processAll || $this->option('users')) {
|
|
||||||
$this->info('Fictionalizing users...');
|
|
||||||
$bar = $this->output->createProgressBar(User::where('email', '!=', 'matt@mattyoung.us')->count());
|
|
||||||
|
|
||||||
User::where('email', '!=', 'matt@mattyoung.us')
|
|
||||||
->chunk(100, function ($users) use ($faker, $bar) {
|
|
||||||
foreach ($users as $user) {
|
|
||||||
$user->update([
|
|
||||||
'email' => $faker->unique()->email(),
|
|
||||||
'first_name' => $faker->firstName(),
|
|
||||||
'last_name' => $faker->lastName(),
|
|
||||||
'cell_phone' => $faker->phoneNumber(),
|
|
||||||
]);
|
|
||||||
$bar->advance();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$bar->finish();
|
|
||||||
$this->newLine();
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->info('Fictionalization complete!');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,124 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Console\Commands;
|
|
||||||
|
|
||||||
use App\Models\Audition;
|
|
||||||
use App\Models\Event;
|
|
||||||
use App\Models\Room;
|
|
||||||
use App\Models\ScoringGuide;
|
|
||||||
use App\Services\CsvImportService;
|
|
||||||
use Carbon\Carbon;
|
|
||||||
use Illuminate\Console\Command;
|
|
||||||
|
|
||||||
use function auditionSetting;
|
|
||||||
use function Laravel\Prompts\select;
|
|
||||||
|
|
||||||
class importCheckAuditionsCommand extends Command
|
|
||||||
{
|
|
||||||
protected $signature = 'import:check-auditions';
|
|
||||||
|
|
||||||
protected $description = 'Check the import file for auditions that do not exist in the database';
|
|
||||||
|
|
||||||
protected $csvImporter;
|
|
||||||
|
|
||||||
public function __construct(CsvImportService $csvImporter)
|
|
||||||
{
|
|
||||||
parent::__construct();
|
|
||||||
$this->csvImporter = $csvImporter;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function handle(): void
|
|
||||||
{
|
|
||||||
$lowestPossibleGrade = 1;
|
|
||||||
$highestPossibleGrade = 12;
|
|
||||||
$events = Event::all();
|
|
||||||
$rows = $this->csvImporter->readCsv(storage_path('app/import/import.csv'));
|
|
||||||
$checkedAuditions = collect();
|
|
||||||
foreach ($rows as $row) {
|
|
||||||
if ($checkedAuditions->contains($row['Instrument'])) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$checkedAuditions->push($row['Instrument']);
|
|
||||||
|
|
||||||
if (Audition::where('name', $row['Instrument'])->count() > 0) {
|
|
||||||
$this->info('Audition '.$row['Instrument'].' already exists');
|
|
||||||
} else {
|
|
||||||
$this->newLine();
|
|
||||||
$this->alert('Audition '.$row['Instrument'].' does not exist');
|
|
||||||
if ($events->count() === 1) {
|
|
||||||
$newEventId = $events->first()->id;
|
|
||||||
} else {
|
|
||||||
$newEventId = select(
|
|
||||||
label: 'Which event does this audition belong to?',
|
|
||||||
options: $events->pluck('name', 'id')->toArray(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
$newEventName = $row['Instrument'];
|
|
||||||
$newEventScoreOrder = Audition::max('score_order') + 1;
|
|
||||||
$newEventEntryDeadline = Carbon::yesterday('America/Chicago')->format('Y-m-d');
|
|
||||||
$newEventEntryFee = Audition::max('entry_fee');
|
|
||||||
$newEventMinimumGrade = select(
|
|
||||||
label: 'What is the minimum grade for this audition?',
|
|
||||||
options: range($lowestPossibleGrade, $highestPossibleGrade)
|
|
||||||
);
|
|
||||||
$newEventMaximumGrade = select(
|
|
||||||
label: 'What is the maximum grade for this audition?',
|
|
||||||
options: range($newEventMinimumGrade, $highestPossibleGrade)
|
|
||||||
);
|
|
||||||
$newEventRoomId = select(
|
|
||||||
label: 'Which room does this audition belong to?',
|
|
||||||
options: Room::pluck('name', 'id')->toArray(),
|
|
||||||
);
|
|
||||||
$newEventScoringGuideId = select(
|
|
||||||
label: 'Which scoring guide should this audition use',
|
|
||||||
options: ScoringGuide::pluck('name', 'id')->toArray(),
|
|
||||||
);
|
|
||||||
if (auditionSetting('advanceTo')) {
|
|
||||||
$newEventForSeating = select(
|
|
||||||
label: 'Is this audition for seating?',
|
|
||||||
options: [
|
|
||||||
1 => 'Yes',
|
|
||||||
0 => 'No',
|
|
||||||
]
|
|
||||||
);
|
|
||||||
$newEventForAdvance = select(
|
|
||||||
label: 'Is this audition for '.auditionSetting('advanceTo').'?',
|
|
||||||
options: [
|
|
||||||
1 => 'Yes',
|
|
||||||
0 => 'No',
|
|
||||||
]
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
$newEventForSeating = 1;
|
|
||||||
$newEventForAdvance = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->info('New event ID: '.$newEventId);
|
|
||||||
$this->info('New event name: '.$newEventName);
|
|
||||||
$this->info('New event score order: '.$newEventScoreOrder);
|
|
||||||
$this->info('New event entry deadline: '.$newEventEntryDeadline);
|
|
||||||
$this->info('New event entry fee: '.$newEventEntryFee);
|
|
||||||
$this->info('New event minimum grade: '.$newEventMinimumGrade);
|
|
||||||
$this->info('New event maximum grade: '.$newEventMaximumGrade);
|
|
||||||
$this->info('New event room ID: '.$newEventRoomId);
|
|
||||||
$this->info('New event scoring guide ID: '.$newEventScoringGuideId);
|
|
||||||
$this->info('New event for seating: '.$newEventForSeating);
|
|
||||||
$this->info('New event for advance: '.$newEventForAdvance);
|
|
||||||
|
|
||||||
Audition::create([
|
|
||||||
'event_id' => $newEventId,
|
|
||||||
'name' => $newEventName,
|
|
||||||
'score_order' => $newEventScoreOrder,
|
|
||||||
'entry_deadline' => $newEventEntryDeadline,
|
|
||||||
'entry_fee' => $newEventEntryFee,
|
|
||||||
'minimum_grade' => $newEventMinimumGrade,
|
|
||||||
'maximum_grade' => $newEventMaximumGrade,
|
|
||||||
'room_id' => $newEventRoomId,
|
|
||||||
'scoring_guide_id' => $newEventScoringGuideId,
|
|
||||||
'for_seating' => $newEventForSeating,
|
|
||||||
'for_advancement' => $newEventForAdvance,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,44 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Console\Commands;
|
|
||||||
|
|
||||||
use const PHP_EOL;
|
|
||||||
|
|
||||||
use App\Models\School;
|
|
||||||
use App\Services\CsvImportService;
|
|
||||||
use Illuminate\Console\Command;
|
|
||||||
|
|
||||||
class importCheckSchoolsCommand extends Command
|
|
||||||
{
|
|
||||||
protected $signature = 'import:check-schools';
|
|
||||||
|
|
||||||
protected $description = 'Check the import file for schools that do not exist in the database';
|
|
||||||
|
|
||||||
protected $csvImporter;
|
|
||||||
|
|
||||||
public function __construct(CsvImportService $csvImporter)
|
|
||||||
{
|
|
||||||
parent::__construct();
|
|
||||||
$this->csvImporter = $csvImporter;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function handle(): void
|
|
||||||
{
|
|
||||||
$rows = $this->csvImporter->readCsv(storage_path('app/import/import.csv'));
|
|
||||||
$checkedSchools = collect();
|
|
||||||
foreach ($rows as $row) {
|
|
||||||
if ($checkedSchools->contains($row['School'])) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$checkedSchools->push($row['School']);
|
|
||||||
if (School::where('name', $row['School'])->count() > 0) {
|
|
||||||
$this->info('School '.$row['School'].' already exists');
|
|
||||||
} else {
|
|
||||||
$this->newLine();
|
|
||||||
$this->alert('School '.$row['School'].' does not exist'.PHP_EOL.'Creating school...');
|
|
||||||
School::create(['name' => $row['School']]);
|
|
||||||
$this->info('School '.$row['School'].' created');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,67 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Console\Commands;
|
|
||||||
|
|
||||||
use App\Models\Entry;
|
|
||||||
use App\Models\School;
|
|
||||||
use App\Models\Student;
|
|
||||||
use App\Services\CsvImportService;
|
|
||||||
use Illuminate\Console\Command;
|
|
||||||
|
|
||||||
class importCheckStudentsCommand extends Command
|
|
||||||
{
|
|
||||||
protected $signature = 'import:check-students';
|
|
||||||
|
|
||||||
protected $description = 'Check the import file for students that do not exist in the database';
|
|
||||||
|
|
||||||
protected $csvImporter;
|
|
||||||
|
|
||||||
public function __construct(CsvImportService $csvImporter)
|
|
||||||
{
|
|
||||||
parent::__construct();
|
|
||||||
$this->csvImporter = $csvImporter;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function handle(): void
|
|
||||||
{
|
|
||||||
$purge = $this->confirm('Do you want to purge the database of existing students and entries?', false);
|
|
||||||
if ($purge) {
|
|
||||||
Entry::all()->map(function ($entry) {
|
|
||||||
$entry->delete();
|
|
||||||
});
|
|
||||||
Student::all()->map(function ($student) {
|
|
||||||
$student->delete();
|
|
||||||
});
|
|
||||||
$this->info('Database purged');
|
|
||||||
}
|
|
||||||
$schools = School::pluck('id', 'name');
|
|
||||||
$rows = $this->csvImporter->readCsv(storage_path('app/import/import.csv'));
|
|
||||||
$checkedStudents = collect();
|
|
||||||
foreach ($rows as $row) {
|
|
||||||
$uniqueData = $row['School'].$row['LastName'].$row['LastName'];
|
|
||||||
if ($checkedStudents->contains($uniqueData)) {
|
|
||||||
// continue;
|
|
||||||
}
|
|
||||||
$checkedStudents->push($uniqueData);
|
|
||||||
|
|
||||||
$currentFirstName = $row['FirstName'];
|
|
||||||
$currentLastName = $row['LastName'];
|
|
||||||
$currentSchoolName = $row['School'];
|
|
||||||
$currentSchoolId = $schools[$currentSchoolName];
|
|
||||||
|
|
||||||
if (Student::where('first_name', $currentFirstName)->where('last_name',
|
|
||||||
$currentLastName)->where('school_id', $currentSchoolId)->count() > 0) {
|
|
||||||
$this->info('Student '.$currentFirstName.' '.$currentLastName.' from '.$currentSchoolName.' already exists');
|
|
||||||
} else {
|
|
||||||
$this->alert('Student '.$currentFirstName.' '.$currentLastName.' from '.$currentSchoolName.' does not exist');
|
|
||||||
$newStudent = Student::create([
|
|
||||||
'school_id' => $currentSchoolId,
|
|
||||||
'first_name' => $currentFirstName,
|
|
||||||
'last_name' => $currentLastName,
|
|
||||||
'grade' => $row['Grade'],
|
|
||||||
]);
|
|
||||||
$this->info('Student '.$currentFirstName.' '.$currentLastName.' from '.$currentSchoolName.' created with id of: '.$newStudent->id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,74 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Console\Commands;
|
|
||||||
|
|
||||||
use App\Models\Audition;
|
|
||||||
use App\Models\Entry;
|
|
||||||
use App\Models\School;
|
|
||||||
use App\Models\Student;
|
|
||||||
use App\Services\CsvImportService;
|
|
||||||
use Illuminate\Console\Command;
|
|
||||||
|
|
||||||
class importImportEntriesCommand extends Command
|
|
||||||
{
|
|
||||||
protected $signature = 'import';
|
|
||||||
|
|
||||||
protected $description = 'Import entries from the import.csv file. First check schools, then students, then auditions, then run this import command';
|
|
||||||
|
|
||||||
protected $csvImporter;
|
|
||||||
|
|
||||||
public function __construct(CsvImportService $csvImporter)
|
|
||||||
{
|
|
||||||
parent::__construct();
|
|
||||||
$this->csvImporter = $csvImporter;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function handle(): void
|
|
||||||
{
|
|
||||||
$checkAuditions = $this->confirm('Do you want to check the auditions in the import for validity first?', true);
|
|
||||||
if ($checkAuditions) {
|
|
||||||
$this->call('import:check-auditions');
|
|
||||||
}
|
|
||||||
|
|
||||||
$checkSchools = $this->confirm('Do you want to check the schools in the import for validity first?', true);
|
|
||||||
if ($checkSchools) {
|
|
||||||
$this->call('import:check-schools');
|
|
||||||
}
|
|
||||||
|
|
||||||
$checkStudents = $this->confirm('Do you want to check the students in the import for validity first?', true);
|
|
||||||
if ($checkStudents) {
|
|
||||||
$this->call('import:check-students');
|
|
||||||
}
|
|
||||||
|
|
||||||
$purge = $this->confirm('Do you want to purge the database of existing entries?', false);
|
|
||||||
if ($purge) {
|
|
||||||
Entry::all()->map(function ($entry) {
|
|
||||||
$entry->delete();
|
|
||||||
});
|
|
||||||
$this->info('Database purged');
|
|
||||||
}
|
|
||||||
$schools = School::pluck('id', 'name');
|
|
||||||
$auditions = Audition::pluck('id', 'name');
|
|
||||||
$rows = $this->csvImporter->readCsv(storage_path('app/import/import.csv'));
|
|
||||||
foreach ($rows as $row) {
|
|
||||||
$schoolId = $schools[$row['School']];
|
|
||||||
$student = Student::where('first_name', $row['FirstName'])->where('last_name',
|
|
||||||
$row['LastName'])->where('school_id', $schoolId)->first();
|
|
||||||
if (! $student) {
|
|
||||||
$this->error('Student '.$row['FirstName'].' '.$row['LastName'].' from '.$row['School'].' does not exist');
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$auditionId = $auditions[$row['Instrument']];
|
|
||||||
try {
|
|
||||||
Entry::create([
|
|
||||||
'student_id' => $student->id,
|
|
||||||
'audition_id' => $auditionId,
|
|
||||||
]);
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
$this->warn('Entry already exists for student '.$student->full_name().' in audition '.$row['Instrument']);
|
|
||||||
}
|
|
||||||
$this->info('Entry created for student '.$student->full_name().' in audition '.$row['Instrument']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -8,6 +8,5 @@ enum EntryFlags: string
|
||||||
case DECLINED = 'declined';
|
case DECLINED = 'declined';
|
||||||
case NO_SHOW = 'no_show';
|
case NO_SHOW = 'no_show';
|
||||||
case FAILED_PRELIM = 'failed_prelim';
|
case FAILED_PRELIM = 'failed_prelim';
|
||||||
case PASSED_PRELIM = 'passed_prelim';
|
|
||||||
case LATE_FEE_WAIVED = 'late_fee_waived';
|
case LATE_FEE_WAIVED = 'late_fee_waived';
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,5 +6,5 @@ use Exception;
|
||||||
|
|
||||||
class AuditionServiceException extends Exception
|
class AuditionServiceException extends Exception
|
||||||
{
|
{
|
||||||
//TODO: Fully depricate this class
|
//
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,19 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Exceptions;
|
namespace App\Exceptions;
|
||||||
|
|
||||||
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
|
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
|
||||||
use Throwable;
|
use Throwable;
|
||||||
|
use App\Exceptions\TabulationException;
|
||||||
|
|
||||||
/**
|
|
||||||
* @codeCoverageIgnore
|
|
||||||
*/
|
|
||||||
//TODO: Fully depricate this class
|
|
||||||
class Handler extends ExceptionHandler
|
class Handler extends ExceptionHandler
|
||||||
{
|
{
|
||||||
|
|
||||||
public function render($request, Throwable $e)
|
public function render($request, Throwable $e)
|
||||||
{
|
{
|
||||||
if ($e instanceof TabulationException) {
|
if ($e instanceof TabulationException) {
|
||||||
dd('here');
|
dd('here');
|
||||||
|
|
||||||
return redirect('/tabulation/status')->with('warning', $e->getMessage());
|
return redirect('/tabulation/status')->with('warning', $e->getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
return parent::render($request, $e);
|
return parent::render($request, $e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,5 +6,4 @@ use Exception;
|
||||||
|
|
||||||
class ManageEntryException extends Exception
|
class ManageEntryException extends Exception
|
||||||
{
|
{
|
||||||
//TODO: Fully depricate this class
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,5 +6,5 @@ use Exception;
|
||||||
|
|
||||||
class ScoreEntryException extends Exception
|
class ScoreEntryException extends Exception
|
||||||
{
|
{
|
||||||
//TODO: Fully depricate this class
|
//
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,24 +3,20 @@
|
||||||
namespace App\Exceptions;
|
namespace App\Exceptions;
|
||||||
|
|
||||||
use Exception;
|
use Exception;
|
||||||
|
use Throwable;
|
||||||
use function dd;
|
use function dd;
|
||||||
use function redirect;
|
use function redirect;
|
||||||
|
|
||||||
/**
|
|
||||||
* @codeCoverageIgnore
|
|
||||||
*/
|
|
||||||
class TabulationException extends Exception
|
class TabulationException extends Exception
|
||||||
{
|
{
|
||||||
public function report(): void
|
public function report(): void
|
||||||
{
|
{
|
||||||
//TODO: Fully depricate this class
|
//
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render($request)
|
public function render($request)
|
||||||
{
|
{
|
||||||
dd('in the render');
|
dd('in the render');
|
||||||
|
return redirect('/tabulation/status')->with('error', $this->getMessage());
|
||||||
return redirect('/tabulation/status')->with('error', $this->getMessage());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,15 +3,13 @@
|
||||||
namespace App\Http\Controllers\Admin;
|
namespace App\Http\Controllers\Admin;
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Http\Requests\AuditionStoreOrUpdateRequest;
|
|
||||||
use App\Http\Requests\BulkAuditionEditRequest;
|
|
||||||
use App\Models\Audition;
|
use App\Models\Audition;
|
||||||
use App\Models\Event;
|
use App\Models\Event;
|
||||||
use App\Models\Room;
|
|
||||||
use Illuminate\Http\JsonResponse;
|
use Illuminate\Http\JsonResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
use function compact;
|
use function abort;
|
||||||
use function redirect;
|
use function redirect;
|
||||||
use function request;
|
use function request;
|
||||||
use function response;
|
use function response;
|
||||||
|
|
@ -30,20 +28,38 @@ class AuditionController extends Controller
|
||||||
|
|
||||||
public function create()
|
public function create()
|
||||||
{
|
{
|
||||||
|
if (! Auth::user()->is_admin) {
|
||||||
|
abort(403);
|
||||||
|
}
|
||||||
$events = Event::orderBy('name')->get();
|
$events = Event::orderBy('name')->get();
|
||||||
|
|
||||||
return view('admin.auditions.create', ['events' => $events]);
|
return view('admin.auditions.create', ['events' => $events]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function store(AuditionStoreOrUpdateRequest $request)
|
public function store(Request $request)
|
||||||
{
|
{
|
||||||
$validData = $request->validated();
|
if (! Auth::user()->is_admin) {
|
||||||
|
abort(403);
|
||||||
|
}
|
||||||
|
$validData = request()->validate([
|
||||||
|
'event_id' => ['required', 'exists:events,id'],
|
||||||
|
'name' => ['required'],
|
||||||
|
'entry_deadline' => ['required', 'date'],
|
||||||
|
'entry_fee' => ['required', 'numeric'],
|
||||||
|
'minimum_grade' => ['required', 'integer'],
|
||||||
|
'maximum_grade' => 'required|numeric|gte:minimum_grade',
|
||||||
|
'scoring_guide_id' => 'nullable|exists:scoring_guides,id',
|
||||||
|
], [
|
||||||
|
'maximum_grade.gte' => 'The maximum grade must be greater than the minimum grade.',
|
||||||
|
]);
|
||||||
|
|
||||||
if (empty($validData['scoring_guide_id'])) {
|
$validData['for_seating'] = $request->get('for_seating') ? 1 : 0;
|
||||||
|
$validData['for_advancement'] = $request->get('for_advancement') ? 1 : 0;
|
||||||
|
if (empty($alidData['scoring_guide_id'])) {
|
||||||
$validData['scoring_guide_id'] = 0;
|
$validData['scoring_guide_id'] = 0;
|
||||||
}
|
}
|
||||||
$validData['score_order'] = Audition::max('score_order') + 1;
|
$new_score_order = Audition::max('score_order') + 1;
|
||||||
|
// TODO Check if room 0 exists, create if not
|
||||||
Audition::create([
|
Audition::create([
|
||||||
'event_id' => $validData['event_id'],
|
'event_id' => $validData['event_id'],
|
||||||
'name' => $validData['name'],
|
'name' => $validData['name'],
|
||||||
|
|
@ -55,7 +71,7 @@ class AuditionController extends Controller
|
||||||
'for_advancement' => $validData['for_advancement'],
|
'for_advancement' => $validData['for_advancement'],
|
||||||
'scoring_guide_id' => $validData['scoring_guide_id'],
|
'scoring_guide_id' => $validData['scoring_guide_id'],
|
||||||
'room_id' => 0,
|
'room_id' => 0,
|
||||||
'score_order' => $validData['score_order'],
|
'score_order' => $new_score_order,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return to_route('admin.auditions.index')->with('success', 'Audition created successfully');
|
return to_route('admin.auditions.index')->with('success', 'Audition created successfully');
|
||||||
|
|
@ -63,14 +79,33 @@ class AuditionController extends Controller
|
||||||
|
|
||||||
public function edit(Audition $audition)
|
public function edit(Audition $audition)
|
||||||
{
|
{
|
||||||
|
if (! Auth::user()->is_admin) {
|
||||||
|
abort(403);
|
||||||
|
}
|
||||||
$events = Event::orderBy('name')->get();
|
$events = Event::orderBy('name')->get();
|
||||||
|
|
||||||
return view('admin.auditions.edit', ['audition' => $audition, 'events' => $events]);
|
return view('admin.auditions.edit', ['audition' => $audition, 'events' => $events]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function update(AuditionStoreOrUpdateRequest $request, Audition $audition)
|
public function update(Request $request, Audition $audition)
|
||||||
{
|
{
|
||||||
$validData = $request->validated();
|
if (! Auth::user()->is_admin) {
|
||||||
|
abort(403);
|
||||||
|
}
|
||||||
|
|
||||||
|
$validData = request()->validate([
|
||||||
|
'event_id' => ['required', 'exists:events,id'],
|
||||||
|
'name' => ['required'],
|
||||||
|
'entry_deadline' => ['required', 'date'],
|
||||||
|
'entry_fee' => ['required', 'numeric'],
|
||||||
|
'minimum_grade' => ['required', 'integer'],
|
||||||
|
'maximum_grade' => 'required | numeric | gte:minimum_grade',
|
||||||
|
], [
|
||||||
|
'maximum_grade.gte' => 'The maximum grade must be greater than the minimum grade.',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$validData['for_seating'] = $request->get('for_seating') ? 1 : 0;
|
||||||
|
$validData['for_advancement'] = $request->get('for_advancement') ? 1 : 0;
|
||||||
|
|
||||||
$audition->update([
|
$audition->update([
|
||||||
'event_id' => $validData['event_id'],
|
'event_id' => $validData['event_id'],
|
||||||
|
|
@ -86,59 +121,11 @@ class AuditionController extends Controller
|
||||||
return to_route('admin.auditions.index')->with('success', 'Audition updated successfully');
|
return to_route('admin.auditions.index')->with('success', 'Audition updated successfully');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function bulkEditForm()
|
|
||||||
{
|
|
||||||
$auditions = Audition::with(['event'])->withCount('entries')->orderBy('score_order')->orderBy('created_at',
|
|
||||||
'desc')->get()->groupBy('event_id');
|
|
||||||
$events = Event::orderBy('name')->get();
|
|
||||||
|
|
||||||
return view('admin.auditions.bulk_edit_form', compact('auditions', 'events'));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public function bulkUpdate(BulkAuditionEditRequest $request)
|
|
||||||
{
|
|
||||||
$validated = collect($request->validated());
|
|
||||||
|
|
||||||
$auditions = Audition::whereIn('id', $validated['auditions'])->get();
|
|
||||||
foreach ($auditions as $audition) {
|
|
||||||
if ($validated->has('event_id')) {
|
|
||||||
$audition->event_id = $validated['event_id'];
|
|
||||||
}
|
|
||||||
if ($validated->has('entry_deadline')) {
|
|
||||||
$audition->entry_deadline = $validated['entry_deadline'];
|
|
||||||
}
|
|
||||||
if ($validated->has('entry_fee')) {
|
|
||||||
|
|
||||||
$audition->entry_fee = $validated['entry_fee'];
|
|
||||||
}
|
|
||||||
if ($validated->has('minimum_grade')) {
|
|
||||||
$originalMinimumGrade = $audition->minimum_grade;
|
|
||||||
$audition->minimum_grade = $validated['minimum_grade'];
|
|
||||||
}
|
|
||||||
if ($validated->has('maximum_grade')) {
|
|
||||||
$originalMaximumGrade = $audition->maximum_grade;
|
|
||||||
$audition->maximum_grade = $validated['maximum_grade'];
|
|
||||||
}
|
|
||||||
if ($validated->has('for_seating')) {
|
|
||||||
$audition->for_seating = $validated['for_seating'];
|
|
||||||
}
|
|
||||||
if ($validated->has('for_advancement')) {
|
|
||||||
$audition->for_advancement = $validated['for_advancement'];
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($audition->minimum_grade > $audition->maximum_grade) {
|
|
||||||
$audition->minimum_grade = $originalMinimumGrade;
|
|
||||||
$audition->maximum_grade = $originalMaximumGrade;
|
|
||||||
}
|
|
||||||
$audition->save();
|
|
||||||
}
|
|
||||||
|
|
||||||
return to_route('admin.auditions.index')->with('success', $auditions->count().' Auditions updated successfully');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function reorder(Request $request)
|
public function reorder(Request $request)
|
||||||
{
|
{
|
||||||
|
if (! Auth::user()->is_admin) {
|
||||||
|
abort(403);
|
||||||
|
}
|
||||||
$order = $request->order;
|
$order = $request->order;
|
||||||
foreach ($order as $index => $id) {
|
foreach ($order as $index => $id) {
|
||||||
$audition = Audition::find($id);
|
$audition = Audition::find($id);
|
||||||
|
|
@ -151,15 +138,9 @@ class AuditionController extends Controller
|
||||||
public function roomUpdate(Request $request)
|
public function roomUpdate(Request $request)
|
||||||
{
|
{
|
||||||
$auditions = $request->all();
|
$auditions = $request->all();
|
||||||
/**
|
|
||||||
* $auditions will be an array of arrays with the following structure:
|
|
||||||
* [
|
|
||||||
* ['id' => 1, 'room_id' => 1, 'room_order' => 1],
|
|
||||||
* ]
|
|
||||||
* is is an audition id
|
|
||||||
*/
|
|
||||||
foreach ($auditions as $audition) {
|
foreach ($auditions as $audition) {
|
||||||
$a = Audition::where('id', $audition['id'])
|
Audition::where('id', $audition['id'])
|
||||||
->update([
|
->update([
|
||||||
'room_id' => $audition['room_id'],
|
'room_id' => $audition['room_id'],
|
||||||
'order_in_room' => $audition['room_order'],
|
'order_in_room' => $audition['room_order'],
|
||||||
|
|
|
||||||
|
|
@ -16,17 +16,15 @@ class AuditionSettings extends Controller
|
||||||
return view('admin.audition-settings');
|
return view('admin.audition-settings');
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @codeCoverageIgnore */
|
|
||||||
public function save(Request $request)
|
public function save(Request $request)
|
||||||
{
|
{
|
||||||
// TODO update validation rules to match the settings table
|
|
||||||
$validData = $request->validate([
|
$validData = $request->validate([
|
||||||
'auditionName' => ['required'],
|
'auditionName' => ['required'],
|
||||||
'auditionAbbreviation' => ['required', 'max:10'],
|
'auditionAbbreviation' => ['required', 'max:10'],
|
||||||
'organizerName' => ['required'],
|
'organizerName' => ['required'],
|
||||||
'organizerEmail' => ['required', 'email'],
|
'organizerEmail' => ['required', 'email'],
|
||||||
'registrationCode' => ['required'],
|
'registrationCode' => ['required'],
|
||||||
'fee_structure' => ['required', 'in:oneFeePerEntry,oneFeePerStudent,oneFeePerStudentPerEvent'],
|
'fee_structure' => ['required', 'in:oneFeePerEntry,oneFeePerStudent'],
|
||||||
// Options should align with the boot method of InvoiceDataServiceProvider
|
// Options should align with the boot method of InvoiceDataServiceProvider
|
||||||
'late_fee' => ['nullable', 'numeric', 'min:0'],
|
'late_fee' => ['nullable', 'numeric', 'min:0'],
|
||||||
'school_fee' => ['nullable', 'numeric', 'min:0'],
|
'school_fee' => ['nullable', 'numeric', 'min:0'],
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ class BonusScoreDefinitionController extends Controller
|
||||||
public function store()
|
public function store()
|
||||||
{
|
{
|
||||||
$validData = request()->validate([
|
$validData = request()->validate([
|
||||||
'name' => 'required|unique:bonus_score_definitions,name',
|
'name' => 'required',
|
||||||
'max_score' => 'required|numeric',
|
'max_score' => 'required|numeric',
|
||||||
'weight' => 'required|numeric',
|
'weight' => 'required|numeric',
|
||||||
]);
|
]);
|
||||||
|
|
@ -37,20 +37,6 @@ class BonusScoreDefinitionController extends Controller
|
||||||
return to_route('admin.bonus-scores.index')->with('success', 'Bonus Score Created');
|
return to_route('admin.bonus-scores.index')->with('success', 'Bonus Score Created');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function update(BonusScoreDefinition $bonusScore)
|
|
||||||
{
|
|
||||||
$validData = request()->validate([
|
|
||||||
'name' => 'required|unique:bonus_score_definitions,name,'.$bonusScore->id,
|
|
||||||
'max_score' => 'required|numeric',
|
|
||||||
'weight' => 'required|numeric',
|
|
||||||
]);
|
|
||||||
|
|
||||||
$bonusScore->update($validData);
|
|
||||||
|
|
||||||
return to_route('admin.bonus-scores.index')->with('success', 'Bonus Score Updated');
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public function destroy(BonusScoreDefinition $bonusScore)
|
public function destroy(BonusScoreDefinition $bonusScore)
|
||||||
{
|
{
|
||||||
if ($bonusScore->auditions()->count() > 0) {
|
if ($bonusScore->auditions()->count() > 0) {
|
||||||
|
|
@ -63,7 +49,6 @@ class BonusScoreDefinitionController extends Controller
|
||||||
|
|
||||||
public function assignAuditions(Request $request)
|
public function assignAuditions(Request $request)
|
||||||
{
|
{
|
||||||
// TODO: add pivot model to log changes to assignments
|
|
||||||
$validData = $request->validate([
|
$validData = $request->validate([
|
||||||
'bonus_score_id' => 'required|exists:bonus_score_definitions,id',
|
'bonus_score_id' => 'required|exists:bonus_score_definitions,id',
|
||||||
'audition' => 'required|array',
|
'audition' => 'required|array',
|
||||||
|
|
@ -85,8 +70,12 @@ class BonusScoreDefinitionController extends Controller
|
||||||
|
|
||||||
public function unassignAudition(Audition $audition)
|
public function unassignAudition(Audition $audition)
|
||||||
{
|
{
|
||||||
// TODO: add pivot model to log changes to assignments
|
if (! $audition->exists()) {
|
||||||
|
return redirect()->route('admin.bonus-scores.index')->with('error', 'Audition not found');
|
||||||
|
}
|
||||||
|
if (! $audition->bonusScore()->count() > 0) {
|
||||||
|
return redirect()->route('admin.bonus-scores.index')->with('error', 'Audition does not have a bonus score');
|
||||||
|
}
|
||||||
$audition->bonusScore()->detach();
|
$audition->bonusScore()->detach();
|
||||||
|
|
||||||
return redirect()->route('admin.bonus-scores.index')->with('success', 'Audition unassigned from bonus score');
|
return redirect()->route('admin.bonus-scores.index')->with('success', 'Audition unassigned from bonus score');
|
||||||
|
|
@ -94,7 +83,6 @@ class BonusScoreDefinitionController extends Controller
|
||||||
|
|
||||||
public function judges()
|
public function judges()
|
||||||
{
|
{
|
||||||
//TODO Need to show if judge is assigned, and show bonus assignments or normal judging page
|
|
||||||
$bonusScores = BonusScoreDefinition::all();
|
$bonusScores = BonusScoreDefinition::all();
|
||||||
$users = User::orderBy('last_name')->orderBy('first_name')->get();
|
$users = User::orderBy('last_name')->orderBy('first_name')->get();
|
||||||
|
|
||||||
|
|
@ -103,6 +91,9 @@ class BonusScoreDefinitionController extends Controller
|
||||||
|
|
||||||
public function assignJudge(BonusScoreDefinition $bonusScore)
|
public function assignJudge(BonusScoreDefinition $bonusScore)
|
||||||
{
|
{
|
||||||
|
if (! $bonusScore->exists()) {
|
||||||
|
return redirect()->route('admin.bonus-scores.judges')->with('error', 'Bonus Score not found');
|
||||||
|
}
|
||||||
$validData = request()->validate([
|
$validData = request()->validate([
|
||||||
'judge' => 'required|exists:users,id',
|
'judge' => 'required|exists:users,id',
|
||||||
]);
|
]);
|
||||||
|
|
@ -113,6 +104,9 @@ class BonusScoreDefinitionController extends Controller
|
||||||
|
|
||||||
public function removeJudge(BonusScoreDefinition $bonusScore)
|
public function removeJudge(BonusScoreDefinition $bonusScore)
|
||||||
{
|
{
|
||||||
|
if (! $bonusScore->exists()) {
|
||||||
|
return redirect()->route('admin.bonus-scores.judges')->with('error', 'Bonus Score not found');
|
||||||
|
}
|
||||||
$validData = request()->validate([
|
$validData = request()->validate([
|
||||||
'judge' => 'required|exists:users,id',
|
'judge' => 'required|exists:users,id',
|
||||||
]);
|
]);
|
||||||
|
|
|
||||||
|
|
@ -2,24 +2,30 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers\Admin;
|
namespace App\Http\Controllers\Admin;
|
||||||
|
|
||||||
use App\Actions\Draw\ClearDraw;
|
|
||||||
use App\Actions\Draw\RunDraw;
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Http\Requests\ClearDrawRequest;
|
use App\Http\Requests\ClearDrawRequest;
|
||||||
use App\Http\Requests\RunDrawRequest;
|
use App\Http\Requests\RunDrawRequest;
|
||||||
use App\Models\Audition;
|
use App\Models\Audition;
|
||||||
use App\Models\Event;
|
use App\Models\Event;
|
||||||
|
use App\Services\DrawService;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
use function array_keys;
|
use function array_keys;
|
||||||
use function to_route;
|
use function to_route;
|
||||||
|
|
||||||
class DrawController extends Controller
|
class DrawController extends Controller
|
||||||
{
|
{
|
||||||
|
protected $drawService;
|
||||||
|
|
||||||
|
public function __construct(DrawService $drawService)
|
||||||
|
{
|
||||||
|
$this->drawService = $drawService;
|
||||||
|
}
|
||||||
|
|
||||||
public function index(Request $request)
|
public function index(Request $request)
|
||||||
{
|
{
|
||||||
$events = Event::with('auditions.flags')->get();
|
$events = Event::with('auditions.flags')->get();
|
||||||
|
|
||||||
// $drawnAuditionsExist is true if any audition->hasFlag('drawn') is true
|
// $drawnAuditionsExist is true if any audition->hasFlag('drawn') is true
|
||||||
$drawnAuditionsExist = Audition::whereHas('flags', function ($query) {
|
$drawnAuditionsExist = Audition::whereHas('flags', function ($query) {
|
||||||
$query->where('flag_name', 'drawn');
|
$query->where('flag_name', 'drawn');
|
||||||
|
|
@ -30,23 +36,18 @@ class DrawController extends Controller
|
||||||
|
|
||||||
public function store(RunDrawRequest $request)
|
public function store(RunDrawRequest $request)
|
||||||
{
|
{
|
||||||
// Request will contain audition which is an array of audition IDs all with a value of 1
|
|
||||||
// Code below results in a collection of auditions that were checked on the form
|
|
||||||
$auditions = Audition::with('flags')->findMany(array_keys($request->input('audition', [])));
|
$auditions = Audition::with('flags')->findMany(array_keys($request->input('audition', [])));
|
||||||
|
|
||||||
if ($auditions->contains(fn ($audition) => $audition->hasFlag('drawn'))) {
|
if ($this->drawService->checkCollectionForDrawnAuditions($auditions)) {
|
||||||
return to_route('admin.draw.index')->with('error',
|
return to_route('admin.draw.index')->with('error',
|
||||||
'Cannot run draw. Some auditions have already been drawn.');
|
'Invalid attempt to draw an audition that has already been drawn');
|
||||||
}
|
}
|
||||||
|
|
||||||
app(RunDraw::class)($auditions);
|
$this->drawService->runDrawsOnCollection($auditions);
|
||||||
|
|
||||||
return to_route('admin.draw.index')->with('success', 'Draw completed successfully');
|
return to_route('admin.draw.index')->with('status', 'Draw completed successfully');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* generates the page with checkboxes for each drawn audition with an intent to clear them
|
|
||||||
*/
|
|
||||||
public function edit(Request $request)
|
public function edit(Request $request)
|
||||||
{
|
{
|
||||||
$drawnAuditions = Audition::whereHas('flags', function ($query) {
|
$drawnAuditions = Audition::whereHas('flags', function ($query) {
|
||||||
|
|
@ -56,17 +57,12 @@ class DrawController extends Controller
|
||||||
return view('admin.draw.edit', compact('drawnAuditions'));
|
return view('admin.draw.edit', compact('drawnAuditions'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Clears the draw for auditions
|
|
||||||
*/
|
|
||||||
public function destroy(ClearDrawRequest $request)
|
public function destroy(ClearDrawRequest $request)
|
||||||
{
|
{
|
||||||
// Request will contain audition which is an array of audition IDs all with a value of 1
|
|
||||||
// Code below results in a collection of auditions that were checked on the form
|
|
||||||
$auditions = Audition::with('flags')->findMany(array_keys($request->input('audition', [])));
|
$auditions = Audition::with('flags')->findMany(array_keys($request->input('audition', [])));
|
||||||
app(ClearDraw::class)($auditions);
|
$this->drawService->clearDrawsOnCollection($auditions);
|
||||||
|
|
||||||
return to_route('admin.draw.index')->with('success', 'Draws cleared successfully');
|
return to_route('admin.draw.index')->with('status', 'Draw completed successfully');
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,12 @@
|
||||||
namespace App\Http\Controllers\Admin;
|
namespace App\Http\Controllers\Admin;
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Http\Requests\EnsembleStoreOrUpdateRequest;
|
|
||||||
use App\Models\Ensemble;
|
use App\Models\Ensemble;
|
||||||
use App\Models\Event;
|
use App\Models\Event;
|
||||||
use App\Models\SeatingLimit;
|
use App\Models\SeatingLimit;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Illuminate\Support\Facades\Cache;
|
use Illuminate\Support\Facades\Cache;
|
||||||
use Illuminate\Support\Facades\Log;
|
|
||||||
|
|
||||||
use function redirect;
|
use function redirect;
|
||||||
|
|
||||||
|
|
@ -22,24 +21,30 @@ class EnsembleController extends Controller
|
||||||
return view('admin.ensembles.index', compact('events'));
|
return view('admin.ensembles.index', compact('events'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function store(EnsembleStoreOrUpdateRequest $request)
|
public function store(Request $request)
|
||||||
{
|
{
|
||||||
Log::channel('file')->warning('hello');
|
if (! Auth::user()->is_admin) {
|
||||||
$validated = $request->validated();
|
abort(403);
|
||||||
// get the maximum value of rank from the ensemble table where event_id is equal to the request event_id
|
}
|
||||||
|
request()->validate([
|
||||||
|
'name' => 'required',
|
||||||
|
'code' => ['required', 'max:6'],
|
||||||
|
'event_id' => ['required', 'exists:events,id'],
|
||||||
|
]);
|
||||||
|
// get the maximum value of rank from the ensembles table where event_id is equal to the request event_id
|
||||||
$maxCode = Ensemble::where('event_id', request('event_id'))->max('rank');
|
$maxCode = Ensemble::where('event_id', request('event_id'))->max('rank');
|
||||||
|
|
||||||
Ensemble::create([
|
Ensemble::create([
|
||||||
'name' => $validated['name'],
|
'name' => request('name'),
|
||||||
'code' => $validated['code'],
|
'code' => request('code'),
|
||||||
'event_id' => $validated['event_id'],
|
'event_id' => request('event_id'),
|
||||||
'rank' => $maxCode + 1,
|
'rank' => $maxCode + 1,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return redirect()->route('admin.ensembles.index')->with('success', 'Ensemble created successfully');
|
return redirect()->route('admin.ensembles.index')->with('success', 'Ensemble created successfully');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function destroy(Ensemble $ensemble)
|
public function destroy(Request $request, Ensemble $ensemble)
|
||||||
{
|
{
|
||||||
if ($ensemble->seats->count() > 0) {
|
if ($ensemble->seats->count() > 0) {
|
||||||
return redirect()->route('admin.ensembles.index')->with('error',
|
return redirect()->route('admin.ensembles.index')->with('error',
|
||||||
|
|
@ -50,32 +55,25 @@ class EnsembleController extends Controller
|
||||||
return redirect()->route('admin.ensembles.index')->with('success', 'Ensemble deleted successfully');
|
return redirect()->route('admin.ensembles.index')->with('success', 'Ensemble deleted successfully');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function update(EnsembleStoreOrUpdateRequest $request, Ensemble $ensemble)
|
public function updateEnsemble(Request $request, Ensemble $ensemble)
|
||||||
{
|
{
|
||||||
$valid = $request->validated();
|
request()->validate([
|
||||||
|
'name' => 'required',
|
||||||
|
'code' => 'required|max:6',
|
||||||
|
]);
|
||||||
|
|
||||||
$ensemble->update([
|
$ensemble->update([
|
||||||
'name' => $valid['name'],
|
'name' => request('name'),
|
||||||
'code' => $valid['code'],
|
'code' => request('code'),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return redirect()->route('admin.ensembles.index')->with('success', 'Ensemble updated successfully');
|
return redirect()->route('admin.ensembles.index')->with('success', 'Ensemble updated successfully');
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO Consider moving seating limit related functions to their own controller with index, edit, and update methods
|
|
||||||
public function seatingLimits(Ensemble $ensemble)
|
public function seatingLimits(Ensemble $ensemble)
|
||||||
{
|
{
|
||||||
$limits = [];
|
$limits = [];
|
||||||
/**
|
$ensembles = Ensemble::with(['event'])->orderBy('event_id')->get();
|
||||||
* If we weren't called with an ensemble, we're going to use an array of ensembles to fill a drop-down and
|
|
||||||
* choose one. The user will be sent back here, this time with the chosen audition.
|
|
||||||
*/
|
|
||||||
$ensembles = Ensemble::with(['event'])->orderBy('event_id')->orderBy('rank')->get();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If we were called with an ensemble, we need to load existing seating limits. We will put them in an array
|
|
||||||
* indexed by audition_id for easy use in the form to set seating limits.
|
|
||||||
*/
|
|
||||||
if ($ensemble->exists()) {
|
if ($ensemble->exists()) {
|
||||||
$ensemble->load('seatingLimits');
|
$ensemble->load('seatingLimits');
|
||||||
foreach ($ensemble->seatingLimits as $lim) {
|
foreach ($ensemble->seatingLimits as $lim) {
|
||||||
|
|
@ -114,6 +112,10 @@ class EnsembleController extends Controller
|
||||||
|
|
||||||
public function updateEnsembleRank(Request $request)
|
public function updateEnsembleRank(Request $request)
|
||||||
{
|
{
|
||||||
|
if (! Auth::user()->is_admin) {
|
||||||
|
abort(403);
|
||||||
|
}
|
||||||
|
|
||||||
$order = $request->input('order');
|
$order = $request->input('order');
|
||||||
$eventId = $request->input('event_id');
|
$eventId = $request->input('event_id');
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,15 +4,18 @@ namespace App\Http\Controllers\Admin;
|
||||||
|
|
||||||
use App\Actions\Entries\CreateEntry;
|
use App\Actions\Entries\CreateEntry;
|
||||||
use App\Actions\Entries\UpdateEntry;
|
use App\Actions\Entries\UpdateEntry;
|
||||||
|
use App\Actions\Tabulation\CalculateScoreSheetTotal;
|
||||||
|
use App\Exceptions\ManageEntryException;
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Http\Requests\EntryStoreRequest;
|
|
||||||
use App\Models\Audition;
|
use App\Models\Audition;
|
||||||
use App\Models\AuditLogEntry;
|
use App\Models\AuditLogEntry;
|
||||||
use App\Models\Entry;
|
use App\Models\Entry;
|
||||||
use App\Models\School;
|
use App\Models\School;
|
||||||
use App\Models\Seat;
|
use App\Models\Seat;
|
||||||
use App\Models\Student;
|
use App\Models\Student;
|
||||||
|
use App\Services\ScoreService;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
use function auditionSetting;
|
use function auditionSetting;
|
||||||
use function compact;
|
use function compact;
|
||||||
|
|
@ -22,6 +25,9 @@ class EntryController extends Controller
|
||||||
{
|
{
|
||||||
public function index()
|
public function index()
|
||||||
{
|
{
|
||||||
|
if (! Auth::user()->is_admin) {
|
||||||
|
abort(403);
|
||||||
|
}
|
||||||
$perPage = 25;
|
$perPage = 25;
|
||||||
$filters = session('adminEntryFilters') ?? null;
|
$filters = session('adminEntryFilters') ?? null;
|
||||||
$minGrade = Audition::min('minimum_grade');
|
$minGrade = Audition::min('minimum_grade');
|
||||||
|
|
@ -32,31 +38,31 @@ class EntryController extends Controller
|
||||||
$entries = Entry::with(['student.school', 'audition']);
|
$entries = Entry::with(['student.school', 'audition']);
|
||||||
$entries->orderBy('id', 'DESC');
|
$entries->orderBy('id', 'DESC');
|
||||||
if ($filters) {
|
if ($filters) {
|
||||||
if ($filters['id'] ?? false) {
|
if ($filters['id']) {
|
||||||
$entries->where('id', $filters['id']);
|
$entries->where('id', $filters['id']);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($filters['audition'] ?? false) {
|
if ($filters['audition']) {
|
||||||
$entries->where('audition_id', $filters['audition']);
|
$entries->where('audition_id', $filters['audition']);
|
||||||
}
|
}
|
||||||
if ($filters['school'] ?? false) {
|
if ($filters['school']) {
|
||||||
$entries->whereHas('student', function ($query) use ($filters) {
|
$entries->whereHas('student', function ($query) use ($filters) {
|
||||||
$query->where('school_id', '=', $filters['school']);
|
$query->where('school_id', '=', $filters['school']);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if ($filters['grade'] ?? false) {
|
if ($filters['grade']) {
|
||||||
$entries->whereHas('student', function ($query) use ($filters) {
|
$entries->whereHas('student', function ($query) use ($filters) {
|
||||||
$query->where('grade', $filters['grade']);
|
$query->where('grade', $filters['grade']);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($filters['first_name'] ?? false) {
|
if ($filters['first_name']) {
|
||||||
$entries->whereHas('student', function ($query) use ($filters) {
|
$entries->whereHas('student', function ($query) use ($filters) {
|
||||||
$query->where('first_name', 'like', '%'.$filters['first_name'].'%');
|
$query->where('first_name', 'like', '%'.$filters['first_name'].'%');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($filters['last_name'] ?? false) {
|
if ($filters['last_name']) {
|
||||||
$entries->whereHas('student', function ($query) use ($filters) {
|
$entries->whereHas('student', function ($query) use ($filters) {
|
||||||
$query->where('last_name', 'like', '%'.$filters['last_name'].'%');
|
$query->where('last_name', 'like', '%'.$filters['last_name'].'%');
|
||||||
});
|
});
|
||||||
|
|
@ -65,6 +71,7 @@ class EntryController extends Controller
|
||||||
if (isset($filters['entry_type']) && $filters['entry_type']) {
|
if (isset($filters['entry_type']) && $filters['entry_type']) {
|
||||||
// TODO define actions for each possible type filter from index.blade.php of the admin entry
|
// TODO define actions for each possible type filter from index.blade.php of the admin entry
|
||||||
match ($filters['entry_type']) {
|
match ($filters['entry_type']) {
|
||||||
|
'all' => null,
|
||||||
'seats' => $entries->where('for_seating', true),
|
'seats' => $entries->where('for_seating', true),
|
||||||
'advancement' => $entries->where('for_advancement', true),
|
'advancement' => $entries->where('for_advancement', true),
|
||||||
'seatsOnly' => $entries->where('for_seating', true)->where('for_advancement', false),
|
'seatsOnly' => $entries->where('for_seating', true)->where('for_advancement', false),
|
||||||
|
|
@ -103,19 +110,32 @@ class EntryController extends Controller
|
||||||
return view('admin.entries.create', ['students' => $students, 'auditions' => $auditions]);
|
return view('admin.entries.create', ['students' => $students, 'auditions' => $auditions]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function store(EntryStoreRequest $request, CreateEntry $creator)
|
public function store(Request $request, CreateEntry $creator)
|
||||||
{
|
{
|
||||||
$validData = $request->validatedWithEnterFor();
|
if (! Auth::user()->is_admin) {
|
||||||
|
abort(403);
|
||||||
|
}
|
||||||
|
$validData = request()->validate([
|
||||||
|
'student_id' => ['required', 'exists:students,id'],
|
||||||
|
'audition_id' => ['required', 'exists:auditions,id'],
|
||||||
|
]);
|
||||||
|
|
||||||
/** @noinspection PhpUnhandledExceptionInspection */
|
$validData['for_seating'] = $request->get('for_seating') ? 1 : 0;
|
||||||
$entry = $creator(
|
$validData['for_advancement'] = $request->get('for_advancement') ? 1 : 0;
|
||||||
student: $validData['student_id'],
|
$validData['late_fee_waived'] = $request->get('late_fee_waived') ? 1 : 0;
|
||||||
audition: $validData['audition_id'],
|
$enter_for = [];
|
||||||
for_seating: $validData['for_seating'],
|
if ($validData['for_seating']) {
|
||||||
for_advancement: $validData['for_advancement'],
|
$enter_for[] = 'seating';
|
||||||
late_fee_waived: $validData['late_fee_waived'],
|
}
|
||||||
);
|
if ($validData['for_advancement']) {
|
||||||
|
$enter_for[] = 'advancement';
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$entry = $creator($validData['student_id'], $validData['audition_id'], $enter_for);
|
||||||
|
} catch (ManageEntryException $ex) {
|
||||||
|
return redirect()->route('admin.entries.index')->with('error', $ex->getMessage());
|
||||||
|
}
|
||||||
if ($validData['late_fee_waived']) {
|
if ($validData['late_fee_waived']) {
|
||||||
$entry->addFlag('late_fee_waived');
|
$entry->addFlag('late_fee_waived');
|
||||||
}
|
}
|
||||||
|
|
@ -123,7 +143,7 @@ class EntryController extends Controller
|
||||||
return redirect(route('admin.entries.index'))->with('success', 'The entry has been added.');
|
return redirect(route('admin.entries.index'))->with('success', 'The entry has been added.');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function edit(Entry $entry)
|
public function edit(Entry $entry, CalculateScoreSheetTotal $calculator, ScoreService $scoreService)
|
||||||
{
|
{
|
||||||
if ($entry->audition->hasFlag('seats_published')) {
|
if ($entry->audition->hasFlag('seats_published')) {
|
||||||
return to_route('admin.entries.index')->with('error',
|
return to_route('admin.entries.index')->with('error',
|
||||||
|
|
@ -137,35 +157,31 @@ class EntryController extends Controller
|
||||||
|
|
||||||
$students = Student::with('school')->orderBy('last_name')->orderBy('first_name')->get();
|
$students = Student::with('school')->orderBy('last_name')->orderBy('first_name')->get();
|
||||||
$auditions = Audition::orderBy('score_order')->get();
|
$auditions = Audition::orderBy('score_order')->get();
|
||||||
// TODO: When updating Laravel, can we use the chaperone method I heard about ot load the entry back into the score
|
$scores = $entry->scoreSheets()->with('audition', 'judge')->get();
|
||||||
$scores = $entry->scoreSheets()->with('audition', 'judge', 'entry')->get();
|
foreach ($scores as $score) {
|
||||||
|
$score->entry = $entry;
|
||||||
|
$score->valid = $scoreService->isScoreSheetValid($score);
|
||||||
|
$score->seating_total_score = $calculator('seating', $entry, $score->judge)[0];
|
||||||
|
$score->advancement_total_score = $calculator('advancement', $entry, $score->judge)[0];
|
||||||
|
}
|
||||||
|
|
||||||
$logEntries = AuditLogEntry::whereJsonContains('affected->entries', $entry->id)->orderBy('created_at', 'desc')->get();
|
return view('admin.entries.edit', compact('entry', 'students', 'auditions', 'scores'));
|
||||||
|
|
||||||
return view('admin.entries.edit', compact('entry', 'students', 'auditions', 'scores', 'logEntries'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function update(Request $request, Entry $entry, UpdateEntry $updater)
|
public function update(Request $request, Entry $entry, UpdateEntry $updater)
|
||||||
{
|
{
|
||||||
// If the entry's current audition is published, we can't change it
|
if ($entry->audition->hasFlag('seats_published')) {
|
||||||
if ($entry->audition->hasFlag('seats_published') || $entry->audition->hasFlag('advancement_published')) {
|
|
||||||
return to_route('admin.entries.index')->with('error',
|
return to_route('admin.entries.index')->with('error',
|
||||||
'Entries in published auditions cannot be modified');
|
'Entries in auditions with seats published cannot be modified');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($entry->audition->hasFlag('advancement_published')) {
|
||||||
|
return to_route('admin.entries.index')->with('error',
|
||||||
|
'Entries in auditions with advancement results published cannot be modified');
|
||||||
|
}
|
||||||
$validData = request()->validate([
|
$validData = request()->validate([
|
||||||
'audition_id' => ['required', 'exists:auditions,id'],
|
'audition_id' => ['required', 'exists:auditions,id'],
|
||||||
'late_fee_waived' => ['sometimes'],
|
|
||||||
'for_seating' => ['sometimes'],
|
|
||||||
'for_advancement' => ['sometimes'],
|
|
||||||
]);
|
]);
|
||||||
$proposedAudition = Audition::find($validData['audition_id']);
|
|
||||||
|
|
||||||
// If the entry's new audition is published, we can't change it
|
|
||||||
if ($proposedAudition->hasFlag('seats_published') || $proposedAudition->hasFlag('advancement_published')) {
|
|
||||||
return to_route('admin.entries.index')->with('error',
|
|
||||||
'Entries cannot be moved to published auditions');
|
|
||||||
}
|
|
||||||
|
|
||||||
$validData['for_seating'] = $request->get('for_seating') ? 1 : 0;
|
$validData['for_seating'] = $request->get('for_seating') ? 1 : 0;
|
||||||
$validData['for_advancement'] = $request->get('for_advancement') ? 1 : 0;
|
$validData['for_advancement'] = $request->get('for_advancement') ? 1 : 0;
|
||||||
|
|
@ -175,10 +191,11 @@ class EntryController extends Controller
|
||||||
if (! auditionSetting('advanceTo')) {
|
if (! auditionSetting('advanceTo')) {
|
||||||
$validData['for_seating'] = 1;
|
$validData['for_seating'] = 1;
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
/** @noinspection PhpUnhandledExceptionInspection */
|
$updater($entry, $validData);
|
||||||
$updater($entry, $validData);
|
} catch (ManageEntryException $e) {
|
||||||
|
return redirect()->route('admin.entries.index')->with('error', $e->getMessage());
|
||||||
|
}
|
||||||
if ($validData['late_fee_waived']) {
|
if ($validData['late_fee_waived']) {
|
||||||
$entry->addFlag('late_fee_waived');
|
$entry->addFlag('late_fee_waived');
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -188,13 +205,17 @@ class EntryController extends Controller
|
||||||
return to_route('admin.entries.index')->with('success', 'Entry updated successfully');
|
return to_route('admin.entries.index')->with('success', 'Entry updated successfully');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function destroy(Entry $entry)
|
public function destroy(Request $request, Entry $entry)
|
||||||
{
|
{
|
||||||
if ($entry->audition->hasFlag('seats_published') || $entry->audition->hasFlag('advancement_published')) {
|
if ($entry->audition->hasFlag('seats_published')) {
|
||||||
return to_route('admin.entries.index')->with('error',
|
return to_route('admin.entries.index')->with('error',
|
||||||
'Entries in published auditions cannot be deleted');
|
'Entries in auditions with seats published cannot be deleted');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($entry->audition->hasFlag('advancement_published')) {
|
||||||
|
return to_route('admin.entries.index')->with('error',
|
||||||
|
'Entries in auditions with advancement results published cannot be deleted');
|
||||||
|
}
|
||||||
if (Seat::where('entry_id', $entry->id)->exists()) {
|
if (Seat::where('entry_id', $entry->id)->exists()) {
|
||||||
return redirect()->route('admin.entries.index')->with('error', 'Cannot delete an entry that is seated');
|
return redirect()->route('admin.entries.index')->with('error', 'Cannot delete an entry that is seated');
|
||||||
}
|
}
|
||||||
|
|
@ -203,7 +224,21 @@ class EntryController extends Controller
|
||||||
return redirect()->route('admin.entries.index')->with('error',
|
return redirect()->route('admin.entries.index')->with('error',
|
||||||
'Cannot delete an entry that has been scored');
|
'Cannot delete an entry that has been scored');
|
||||||
}
|
}
|
||||||
|
if (auth()->user()) {
|
||||||
|
$message = 'Deleted entry '.$entry->id;
|
||||||
|
$affected = [
|
||||||
|
'entries' => [$entry->id],
|
||||||
|
'auditions' => [$entry->audition_id],
|
||||||
|
'schools' => [$entry->student->school_id],
|
||||||
|
'students' => [$entry->student_id],
|
||||||
|
];
|
||||||
|
AuditLogEntry::create([
|
||||||
|
'user' => auth()->user()->email,
|
||||||
|
'ip_address' => request()->ip(),
|
||||||
|
'message' => $message,
|
||||||
|
'affected' => $affected,
|
||||||
|
]);
|
||||||
|
}
|
||||||
$entry->delete();
|
$entry->delete();
|
||||||
|
|
||||||
return redirect()->route('admin.entries.index')->with('success', 'Entry Deleted');
|
return redirect()->route('admin.entries.index')->with('success', 'Entry Deleted');
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,9 @@ namespace App\Http\Controllers\Admin;
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Models\Event;
|
use App\Models\Event;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
|
use function abort;
|
||||||
use function compact;
|
use function compact;
|
||||||
|
|
||||||
class EventController extends Controller
|
class EventController extends Controller
|
||||||
|
|
@ -13,16 +15,15 @@ class EventController extends Controller
|
||||||
public function index()
|
public function index()
|
||||||
{
|
{
|
||||||
$events = Event::all();
|
$events = Event::all();
|
||||||
$renameModalXdata = '';
|
|
||||||
foreach ($events as $event) {
|
|
||||||
$renameModalXdata .= 'showRenameModal_'.$event->id.': false, ';
|
|
||||||
}
|
|
||||||
|
|
||||||
return view('admin.event.index', compact('events', 'renameModalXdata'));
|
return view('admin.event.index', compact('events'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function store(Request $request)
|
public function store(Request $request)
|
||||||
{
|
{
|
||||||
|
if (! Auth::user()->is_admin) {
|
||||||
|
abort(403);
|
||||||
|
}
|
||||||
request()->validate([
|
request()->validate([
|
||||||
'name' => ['required', 'unique:events,name'],
|
'name' => ['required', 'unique:events,name'],
|
||||||
]);
|
]);
|
||||||
|
|
@ -34,21 +35,6 @@ class EventController extends Controller
|
||||||
return redirect()->route('admin.events.index')->with('success', 'Event created successfully');
|
return redirect()->route('admin.events.index')->with('success', 'Event created successfully');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function update(Request $request, Event $event)
|
|
||||||
{
|
|
||||||
if ($request->name !== $event->name) {
|
|
||||||
$validated = request()->validate([
|
|
||||||
'name' => ['required', 'unique:events,name'],
|
|
||||||
]);
|
|
||||||
|
|
||||||
$event->update([
|
|
||||||
'name' => $validated['name'],
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return redirect()->route('admin.events.index')->with('success', 'Event renamed successfully');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function destroy(Request $request, Event $event)
|
public function destroy(Request $request, Event $event)
|
||||||
{
|
{
|
||||||
if ($event->auditions()->count() > 0) {
|
if ($event->auditions()->count() > 0) {
|
||||||
|
|
@ -60,4 +46,3 @@ class EventController extends Controller
|
||||||
return redirect()->route('admin.events.index')->with('success', 'Event deleted successfully');
|
return redirect()->route('admin.events.index')->with('success', 'Event deleted successfully');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO add form to modify an event
|
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,6 @@ use App\Http\Controllers\Controller;
|
||||||
use Illuminate\Support\Facades\App;
|
use Illuminate\Support\Facades\App;
|
||||||
use Illuminate\Support\Facades\Response;
|
use Illuminate\Support\Facades\Response;
|
||||||
|
|
||||||
// TODO: Printing testing
|
|
||||||
/** @codeCoverageIgnore */
|
|
||||||
class ExportEntriesController extends Controller
|
class ExportEntriesController extends Controller
|
||||||
{
|
{
|
||||||
public function __invoke()
|
public function __invoke()
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,6 @@ use App\Http\Controllers\Controller;
|
||||||
use Illuminate\Support\Facades\App;
|
use Illuminate\Support\Facades\App;
|
||||||
use Illuminate\Support\Facades\Response;
|
use Illuminate\Support\Facades\Response;
|
||||||
|
|
||||||
// TODO: Printing testing
|
|
||||||
/** @codeCoverageIgnore */
|
|
||||||
class ExportResultsController extends Controller
|
class ExportResultsController extends Controller
|
||||||
{
|
{
|
||||||
public function __invoke()
|
public function __invoke()
|
||||||
|
|
|
||||||
|
|
@ -1,70 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Controllers\Admin;
|
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
|
||||||
use App\Http\Requests\PrelimDefinitionStoreOrUpdateRequest;
|
|
||||||
use App\Models\Audition;
|
|
||||||
use App\Models\PrelimDefinition;
|
|
||||||
use App\Models\Room;
|
|
||||||
use App\Models\ScoringGuide;
|
|
||||||
|
|
||||||
use function view;
|
|
||||||
|
|
||||||
class PrelimDefinitionController extends Controller
|
|
||||||
{
|
|
||||||
public function index()
|
|
||||||
{
|
|
||||||
$prelims = PrelimDefinition::all();
|
|
||||||
|
|
||||||
return view('admin.prelim_definitions.index', compact('prelims'));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function create()
|
|
||||||
{
|
|
||||||
$auditions = Audition::doesntHave('prelimDefinition')->get();
|
|
||||||
$rooms = Room::all();
|
|
||||||
$guides = ScoringGuide::all();
|
|
||||||
$method = 'POST';
|
|
||||||
$action = route('admin.prelim_definitions.store');
|
|
||||||
$prelim = false;
|
|
||||||
|
|
||||||
return view('admin.prelim_definitions.createOrUpdate', compact('auditions', 'rooms', 'guides', 'method', 'action', 'prelim'));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function store(PrelimDefinitionStoreOrUpdateRequest $request)
|
|
||||||
{
|
|
||||||
$validated = $request->validated();
|
|
||||||
PrelimDefinition::create($validated);
|
|
||||||
|
|
||||||
return redirect()->route('admin.prelim_definitions.index')->with('success', 'Prelim definition created');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function edit(PrelimDefinition $prelimDefinition)
|
|
||||||
{
|
|
||||||
$auditions = Audition::doesntHave('prelimDefinition')->get();
|
|
||||||
$rooms = Room::all();
|
|
||||||
$guides = ScoringGuide::all();
|
|
||||||
$method = 'PATCH';
|
|
||||||
$action = route('admin.prelim_definitions.update', $prelimDefinition);
|
|
||||||
$prelim = $prelimDefinition;
|
|
||||||
|
|
||||||
return view('admin.prelim_definitions.createOrUpdate', compact('auditions', 'rooms', 'guides', 'method', 'action', 'prelim'));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public function update(PrelimDefinition $prelimDefinition, PrelimDefinitionStoreOrUpdateRequest $request)
|
|
||||||
{
|
|
||||||
$validated = $request->validated();
|
|
||||||
$prelimDefinition->update($validated);
|
|
||||||
|
|
||||||
return redirect()->route('admin.prelim_definitions.index')->with('success', 'Prelim definition updated');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function destroy(PrelimDefinition $prelimDefinition)
|
|
||||||
{
|
|
||||||
$prelimDefinition->delete();
|
|
||||||
|
|
||||||
return redirect()->route('admin.prelim_definitions.index')->with('success', 'Prelim definition deleted');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -7,8 +7,6 @@ use App\Models\Entry;
|
||||||
use App\Models\Event;
|
use App\Models\Event;
|
||||||
use Illuminate\Support\Carbon;
|
use Illuminate\Support\Carbon;
|
||||||
|
|
||||||
// TODO: Printing testing
|
|
||||||
/** @codeCoverageIgnore */
|
|
||||||
class PrintCards extends Controller
|
class PrintCards extends Controller
|
||||||
{
|
{
|
||||||
public function index() // Display a form to select which cards to print
|
public function index() // Display a form to select which cards to print
|
||||||
|
|
@ -27,15 +25,11 @@ class PrintCards extends Controller
|
||||||
|
|
||||||
public function print(\App\Actions\Print\PrintCards $printer)
|
public function print(\App\Actions\Print\PrintCards $printer)
|
||||||
{
|
{
|
||||||
// dump(request()->all());
|
//dump(request()->all());
|
||||||
// if (request()->audition == null) {
|
if (request()->audition == null) {
|
||||||
// return redirect()->back()->with('error', 'You must specify at least one audition');
|
return redirect()->back()->with('error', 'You must specify at least one audition');
|
||||||
// }
|
|
||||||
if (request()->audition) {
|
|
||||||
$selectedAuditionIds = array_keys(request()->audition);
|
|
||||||
} else {
|
|
||||||
$selectedAuditionIds = [];
|
|
||||||
}
|
}
|
||||||
|
$selectedAuditionIds = array_keys(request()->audition);
|
||||||
$cardQuery = Entry::whereIn('audition_id', $selectedAuditionIds);
|
$cardQuery = Entry::whereIn('audition_id', $selectedAuditionIds);
|
||||||
|
|
||||||
// Process Filters
|
// Process Filters
|
||||||
|
|
@ -66,6 +60,6 @@ class PrintCards extends Controller
|
||||||
}
|
}
|
||||||
$cards = $cards->sortBy($sorts);
|
$cards = $cards->sortBy($sorts);
|
||||||
$printer->print($cards);
|
$printer->print($cards);
|
||||||
// return view('admin.print_cards.print', compact('cards'));
|
//return view('admin.print_cards.print', compact('cards'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,6 @@ use Codedge\Fpdf\Fpdf\Fpdf;
|
||||||
|
|
||||||
use function auditionSetting;
|
use function auditionSetting;
|
||||||
|
|
||||||
// TODO: Printing testing
|
|
||||||
|
|
||||||
/** @codeCoverageIgnore */
|
|
||||||
class PrintRoomAssignmentsController extends Controller
|
class PrintRoomAssignmentsController extends Controller
|
||||||
{
|
{
|
||||||
private $pdf;
|
private $pdf;
|
||||||
|
|
@ -97,7 +94,7 @@ class PrintRoomAssignmentsController extends Controller
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/** @codeCoverageIgnore */
|
|
||||||
class reportPDF extends FPDF
|
class reportPDF extends FPDF
|
||||||
{
|
{
|
||||||
public function getPageBreakTrigger()
|
public function getPageBreakTrigger()
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,6 @@ use App\Models\Room;
|
||||||
use function array_keys;
|
use function array_keys;
|
||||||
use function request;
|
use function request;
|
||||||
|
|
||||||
// TODO: Printing testing
|
|
||||||
/** @codeCoverageIgnore */
|
|
||||||
class PrintSignInSheetsController extends Controller
|
class PrintSignInSheetsController extends Controller
|
||||||
{
|
{
|
||||||
public function index()
|
public function index()
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,6 @@ use App\Actions\Print\PrintStandNameTags;
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use Illuminate\Support\Facades\App;
|
use Illuminate\Support\Facades\App;
|
||||||
|
|
||||||
// TODO: Printing testing
|
|
||||||
/** @codeCoverageIgnore */
|
|
||||||
class PrintStandNameTagsController extends Controller
|
class PrintStandNameTagsController extends Controller
|
||||||
{
|
{
|
||||||
public function __invoke()
|
public function __invoke()
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,6 @@ use App\Http\Controllers\Controller;
|
||||||
use App\Models\Audition;
|
use App\Models\Audition;
|
||||||
use Illuminate\Support\Facades\App;
|
use Illuminate\Support\Facades\App;
|
||||||
|
|
||||||
// TODO: Rewrite Recap to work with new scoring code
|
|
||||||
/** @codeCoverageIgnore */
|
|
||||||
class RecapController extends Controller
|
class RecapController extends Controller
|
||||||
{
|
{
|
||||||
public function selectAudition()
|
public function selectAudition()
|
||||||
|
|
|
||||||
|
|
@ -3,40 +3,22 @@
|
||||||
namespace App\Http\Controllers\Admin;
|
namespace App\Http\Controllers\Admin;
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Models\Audition;
|
|
||||||
use App\Models\BonusScoreDefinition;
|
use App\Models\BonusScoreDefinition;
|
||||||
use App\Models\Room;
|
use App\Models\Room;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Validation\Rule;
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
use function auditionLog;
|
|
||||||
use function redirect;
|
use function redirect;
|
||||||
|
|
||||||
class RoomController extends Controller
|
class RoomController extends Controller
|
||||||
{
|
{
|
||||||
public function index()
|
public function index()
|
||||||
{
|
{
|
||||||
|
if (! Auth::user()->is_admin) {
|
||||||
$rooms = Room::with('auditions.entries', 'entries')->orderBy('name')->get();
|
abort(403);
|
||||||
|
|
||||||
// Check if room id 0 exists, if not, create it and assign all unassigned auditions to it
|
|
||||||
if (! $rooms->contains('id', 0)) {
|
|
||||||
$unassignedRoom = Room::create([
|
|
||||||
'id' => 0,
|
|
||||||
'name' => 'Unassigned',
|
|
||||||
'description' => 'Auditions that have not been assigned to a room',
|
|
||||||
]);
|
|
||||||
$unassignedRoom->id = 0;
|
|
||||||
$unassignedRoom->save();
|
|
||||||
|
|
||||||
$auditionsToUpdate = Audition::whereNull('room_id')->get();
|
|
||||||
foreach ($auditionsToUpdate as $audition) {
|
|
||||||
$audition->room_id = 0;
|
|
||||||
$audition->save();
|
|
||||||
}
|
|
||||||
$rooms = Room::with('auditions.entries', 'entries')->orderBy('name')->get();
|
|
||||||
}
|
}
|
||||||
|
$rooms = Room::with('auditions.entries', 'entries')->orderBy('name')->get();
|
||||||
|
|
||||||
return view('admin.rooms.index', ['rooms' => $rooms]);
|
return view('admin.rooms.index', ['rooms' => $rooms]);
|
||||||
}
|
}
|
||||||
|
|
@ -48,12 +30,14 @@ class RoomController extends Controller
|
||||||
$rooms = Room::with(['judges.school', 'auditions'])->get();
|
$rooms = Room::with(['judges.school', 'auditions'])->get();
|
||||||
$bonusScoresExist = BonusScoreDefinition::count() > 0;
|
$bonusScoresExist = BonusScoreDefinition::count() > 0;
|
||||||
|
|
||||||
return view('admin.rooms.judge_assignments',
|
return view('admin.rooms.judge_assignments', compact('usersWithoutRooms', 'usersWithRooms', 'rooms', 'bonusScoresExist'));
|
||||||
compact('usersWithoutRooms', 'usersWithRooms', 'rooms', 'bonusScoresExist'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function updateJudgeAssignment(Request $request, Room $room)
|
public function updateJudgeAssignment(Request $request, Room $room)
|
||||||
{
|
{
|
||||||
|
if (! Auth::user()->is_admin) {
|
||||||
|
abort(403);
|
||||||
|
}
|
||||||
$validData = $request->validate([
|
$validData = $request->validate([
|
||||||
'judge' => 'exists:users,id',
|
'judge' => 'exists:users,id',
|
||||||
]);
|
]);
|
||||||
|
|
@ -67,23 +51,29 @@ class RoomController extends Controller
|
||||||
// detach judge on delete
|
// detach judge on delete
|
||||||
$room->removeJudge($judge->id);
|
$room->removeJudge($judge->id);
|
||||||
$message = 'Removed '.$judge->full_name().' from '.$room->name;
|
$message = 'Removed '.$judge->full_name().' from '.$room->name;
|
||||||
|
} else {
|
||||||
|
return redirect('/admin/rooms/judging_assignments')->with('error', 'Invalid request method.');
|
||||||
}
|
}
|
||||||
$affected['users'] = [$judge->id];
|
|
||||||
$affected['rooms'] = [$room->id];
|
|
||||||
auditionLog($message, $affected);
|
|
||||||
|
|
||||||
return redirect(route('admin.rooms.judgingAssignment'))->with('success', $message);
|
return redirect('/admin/rooms/judging_assignments')->with('success', $message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function create()
|
public function create()
|
||||||
{
|
{
|
||||||
|
if (! Auth::user()->is_admin) {
|
||||||
|
abort(403);
|
||||||
|
}
|
||||||
|
|
||||||
return view('admin.rooms.create');
|
return view('admin.rooms.create');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function store(Request $request)
|
public function store(Request $request)
|
||||||
{
|
{
|
||||||
|
if (! Auth::user()->is_admin) {
|
||||||
|
abort(403);
|
||||||
|
}
|
||||||
$validData = $request->validate([
|
$validData = $request->validate([
|
||||||
'name' => 'required|unique:rooms,name',
|
'name' => 'required',
|
||||||
'description' => 'nullable',
|
'description' => 'nullable',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -97,8 +87,11 @@ class RoomController extends Controller
|
||||||
|
|
||||||
public function update(Request $request, Room $room)
|
public function update(Request $request, Room $room)
|
||||||
{
|
{
|
||||||
|
if (! Auth::user()->is_admin) {
|
||||||
|
abort(403);
|
||||||
|
}
|
||||||
$validData = $request->validate([
|
$validData = $request->validate([
|
||||||
'name' => ['required', Rule::unique('rooms', 'name')->ignore($room->id)],
|
'name' => 'required',
|
||||||
'description' => 'nullable',
|
'description' => 'nullable',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -111,9 +104,12 @@ class RoomController extends Controller
|
||||||
|
|
||||||
public function destroy(Room $room)
|
public function destroy(Room $room)
|
||||||
{
|
{
|
||||||
|
if (! Auth::user()->is_admin) {
|
||||||
|
abort(403);
|
||||||
|
}
|
||||||
|
|
||||||
if ($room->auditions()->count() > 0) {
|
if ($room->auditions()->count() > 0) {
|
||||||
return redirect()->route('admin.rooms.index')->with('error',
|
return redirect()->route('admin.rooms.index')->with('error', 'Cannot delete room with auditions. First move the auditions to unassigned or another room');
|
||||||
'Cannot delete room with auditions. First move the auditions to unassigned or another room');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$room->delete();
|
$room->delete();
|
||||||
|
|
|
||||||
|
|
@ -2,16 +2,16 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers\Admin;
|
namespace App\Http\Controllers\Admin;
|
||||||
|
|
||||||
use App\Actions\Schools\CreateSchool;
|
|
||||||
use App\Actions\Schools\SetHeadDirector;
|
use App\Actions\Schools\SetHeadDirector;
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Http\Requests\SchoolStoreRequest;
|
|
||||||
use App\Models\AuditLogEntry;
|
use App\Models\AuditLogEntry;
|
||||||
use App\Models\School;
|
use App\Models\School;
|
||||||
use App\Models\SchoolEmailDomain;
|
use App\Models\SchoolEmailDomain;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use App\Services\Invoice\InvoiceDataService;
|
use App\Services\Invoice\InvoiceDataService;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
|
use function abort;
|
||||||
use function redirect;
|
use function redirect;
|
||||||
use function request;
|
use function request;
|
||||||
|
|
||||||
|
|
@ -38,26 +38,46 @@ class SchoolController extends Controller
|
||||||
|
|
||||||
public function show(School $school)
|
public function show(School $school)
|
||||||
{
|
{
|
||||||
$logEntries = AuditLogEntry::whereJsonContains('affected->schools', $school->id)->orderBy('created_at', 'desc')->get();
|
if (! Auth::user()->is_admin) {
|
||||||
|
abort(403);
|
||||||
|
}
|
||||||
|
|
||||||
return view('admin.schools.show', compact('school', 'logEntries'));
|
return view('admin.schools.show', ['school' => $school]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function edit(School $school)
|
public function edit(School $school)
|
||||||
{
|
{
|
||||||
|
if (! Auth::user()->is_admin) {
|
||||||
|
abort(403);
|
||||||
|
}
|
||||||
$school->loadCount('students');
|
$school->loadCount('students');
|
||||||
|
|
||||||
return view('admin.schools.edit', ['school' => $school]);
|
return view('admin.schools.edit', ['school' => $school]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function update(SchoolStoreRequest $request, School $school)
|
public function update(School $school)
|
||||||
{
|
{
|
||||||
|
request()->validate([
|
||||||
|
'name' => ['required'],
|
||||||
|
'address' => ['required'],
|
||||||
|
'city' => ['required'],
|
||||||
|
'state' => ['required'],
|
||||||
|
'zip' => ['required'],
|
||||||
|
]);
|
||||||
|
|
||||||
$school->update([
|
$school->update([
|
||||||
'name' => $request['name'],
|
'name' => request('name'),
|
||||||
'address' => $request['address'],
|
'address' => request('address'),
|
||||||
'city' => $request['city'],
|
'city' => request('city'),
|
||||||
'state' => $request['state'],
|
'state' => request('state'),
|
||||||
'zip' => $request['zip'],
|
'zip' => request('zip'),
|
||||||
|
]);
|
||||||
|
$message = 'Modified school #'.$school->id.' - '.$school->name.' with address <br>'.$school->address.'<br>'.$school->city.', '.$school->state.' '.$school->zip;
|
||||||
|
AuditLogEntry::create([
|
||||||
|
'user' => auth()->user()->email,
|
||||||
|
'ip_address' => request()->ip(),
|
||||||
|
'message' => $message,
|
||||||
|
'affected' => ['schools' => [$school->id]],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return redirect()->route('admin.schools.show', ['school' => $school->id])->with('success',
|
return redirect()->route('admin.schools.show', ['school' => $school->id])->with('success',
|
||||||
|
|
@ -66,30 +86,54 @@ class SchoolController extends Controller
|
||||||
|
|
||||||
public function create()
|
public function create()
|
||||||
{
|
{
|
||||||
|
if (! Auth::user()->is_admin) {
|
||||||
|
abort(403);
|
||||||
|
}
|
||||||
|
|
||||||
return view('admin.schools.create');
|
return view('admin.schools.create');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function store(SchoolStoreRequest $request)
|
public function store()
|
||||||
{
|
{
|
||||||
$creator = app(CreateSchool::class);
|
request()->validate([
|
||||||
|
'name' => ['required'],
|
||||||
|
'address' => ['required'],
|
||||||
|
'city' => ['required'],
|
||||||
|
'state' => ['required'],
|
||||||
|
'zip' => ['required'],
|
||||||
|
]);
|
||||||
|
|
||||||
$school = $creator(
|
$school = School::create([
|
||||||
$request['name'],
|
'name' => request('name'),
|
||||||
$request['address'],
|
'address' => request('address'),
|
||||||
$request['city'],
|
'city' => request('city'),
|
||||||
$request['state'],
|
'state' => request('state'),
|
||||||
$request['zip'],
|
'zip' => request('zip'),
|
||||||
);
|
]);
|
||||||
|
$message = 'Created school #'.$school->id.' - '.$school->name.' with address <br>'.$school->address.'<br>'.$school->city.', '.$school->state.' '.$school->zip;
|
||||||
|
AuditLogEntry::create([
|
||||||
|
'user' => auth()->user()->email,
|
||||||
|
'ip_address' => request()->ip(),
|
||||||
|
'message' => $message,
|
||||||
|
'affected' => ['schools' => [$school->id]],
|
||||||
|
]);
|
||||||
|
|
||||||
return redirect(route('admin.schools.index'))->with('success', 'School '.$school->name.' created');
|
return redirect('/admin/schools')->with('success', 'School '.$school->name.' created');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function destroy(School $school)
|
public function destroy(School $school)
|
||||||
{
|
{
|
||||||
if ($school->students()->count() > 0) {
|
if ($school->students()->count() > 0) {
|
||||||
return to_route('admin.schools.index')->with('error', 'You cannot delete a school that has students.');
|
return to_route('admin.schools.index')->with('error', 'You cannot delete a school with students.');
|
||||||
}
|
}
|
||||||
|
$name = $school->name;
|
||||||
|
$message = 'Delete school #'.$school->id.' - '.$school->name;
|
||||||
|
AuditLogEntry::create([
|
||||||
|
'user' => auth()->user()->email,
|
||||||
|
'ip_address' => request()->ip(),
|
||||||
|
'message' => $message,
|
||||||
|
'affected' => ['schools' => [$school->id]],
|
||||||
|
]);
|
||||||
$school->delete();
|
$school->delete();
|
||||||
|
|
||||||
return to_route('admin.schools.index')->with('success', 'School '.$school->name.' deleted');
|
return to_route('admin.schools.index')->with('success', 'School '.$school->name.' deleted');
|
||||||
|
|
@ -97,6 +141,9 @@ class SchoolController extends Controller
|
||||||
|
|
||||||
public function add_domain(School $school)
|
public function add_domain(School $school)
|
||||||
{
|
{
|
||||||
|
if (! Auth::user()->is_admin) {
|
||||||
|
abort(403);
|
||||||
|
}
|
||||||
request()->validate([
|
request()->validate([
|
||||||
// validate that the combination of school and domain is unique on the school_email_domains table
|
// validate that the combination of school and domain is unique on the school_email_domains table
|
||||||
'domain' => ['required'],
|
'domain' => ['required'],
|
||||||
|
|
@ -105,6 +152,12 @@ class SchoolController extends Controller
|
||||||
'school_id' => $school->id,
|
'school_id' => $school->id,
|
||||||
'domain' => request('domain'),
|
'domain' => request('domain'),
|
||||||
]);
|
]);
|
||||||
|
AuditLogEntry::create([
|
||||||
|
'user' => auth()->user()->email,
|
||||||
|
'ip_address' => request()->ip(),
|
||||||
|
'message' => 'Added '.request('domain').' as an email domain for school #'.$school->id.' - '.$school->name,
|
||||||
|
'affected' => ['schools' => [$school->id]],
|
||||||
|
]);
|
||||||
|
|
||||||
return redirect()->route('admin.schools.show', $school)->with('success', 'Domain Added');
|
return redirect()->route('admin.schools.show', $school)->with('success', 'Domain Added');
|
||||||
|
|
||||||
|
|
@ -116,11 +169,9 @@ class SchoolController extends Controller
|
||||||
$domain->delete();
|
$domain->delete();
|
||||||
|
|
||||||
// return a redirect to the previous URL
|
// return a redirect to the previous URL
|
||||||
return redirect()->back()->with('success', 'Domain removed successfully.');
|
return redirect()->back();
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Add testing for invoicing
|
|
||||||
/** @codeCoverageIgnore */
|
|
||||||
public function viewInvoice(School $school)
|
public function viewInvoice(School $school)
|
||||||
{
|
{
|
||||||
$invoiceData = $this->invoiceService->allData($school->id);
|
$invoiceData = $this->invoiceService->allData($school->id);
|
||||||
|
|
@ -133,9 +184,8 @@ class SchoolController extends Controller
|
||||||
if ($user->school_id !== $school->id) {
|
if ($user->school_id !== $school->id) {
|
||||||
return redirect()->back()->with('error', 'That user is not at that school');
|
return redirect()->back()->with('error', 'That user is not at that school');
|
||||||
}
|
}
|
||||||
/** @noinspection PhpUnhandledExceptionInspection */
|
|
||||||
$headSetter->setHeadDirector($user);
|
$headSetter->setHeadDirector($user);
|
||||||
|
|
||||||
return redirect()->back()->with('success', 'Head director set successfully.');
|
return redirect()->back()->with('success', 'Head director set');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Controllers\Admin;
|
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
|
||||||
use App\Models\School;
|
|
||||||
|
|
||||||
class SchoolEmailDomainController extends Controller
|
|
||||||
{
|
|
||||||
public function index()
|
|
||||||
{
|
|
||||||
$schools = School::with('emailDomains')->get();
|
|
||||||
|
|
||||||
return view('admin.schools.email_domains_index', compact('schools'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -3,12 +3,13 @@
|
||||||
namespace App\Http\Controllers\Admin;
|
namespace App\Http\Controllers\Admin;
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Http\Requests\SubscoreDefinitionRequest;
|
|
||||||
use App\Models\ScoringGuide;
|
use App\Models\ScoringGuide;
|
||||||
use App\Models\SubscoreDefinition;
|
use App\Models\SubscoreDefinition;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
|
use function abort;
|
||||||
use function auditionSetting;
|
use function auditionSetting;
|
||||||
use function request;
|
use function request;
|
||||||
use function response;
|
use function response;
|
||||||
|
|
@ -27,19 +28,26 @@ class ScoringGuideController extends Controller
|
||||||
|
|
||||||
public function store()
|
public function store()
|
||||||
{
|
{
|
||||||
|
if (! Auth::user()->is_admin) {
|
||||||
|
abort(403);
|
||||||
|
}
|
||||||
|
|
||||||
request()->validate([
|
request()->validate([
|
||||||
'name' => ['required', 'unique:scoring_guides'],
|
'name' => ['required', 'unique:scoring_guides'],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
ScoringGuide::create([
|
$guide = ScoringGuide::create([
|
||||||
'name' => request('name'),
|
'name' => request('name'),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return redirect(route('admin.scoring.index'))->with('success', 'Scoring guide created');
|
return redirect(route('admin.scoring.index'))->with('success', 'Scoring guide created');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function edit(ScoringGuide $guide, string $tab = 'detail')
|
public function edit(Request $request, ScoringGuide $guide, string $tab = 'detail')
|
||||||
{
|
{
|
||||||
|
if (! Auth::user()->is_admin) {
|
||||||
|
abort(403);
|
||||||
|
}
|
||||||
if ($tab == 'tiebreakOrder') {
|
if ($tab == 'tiebreakOrder') {
|
||||||
$subscores = SubscoreDefinition::where('scoring_guide_id', $guide->id)->orderBy('tiebreak_order')->get();
|
$subscores = SubscoreDefinition::where('scoring_guide_id', $guide->id)->orderBy('tiebreak_order')->get();
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -51,6 +59,9 @@ class ScoringGuideController extends Controller
|
||||||
|
|
||||||
public function update(ScoringGuide $guide)
|
public function update(ScoringGuide $guide)
|
||||||
{
|
{
|
||||||
|
if (! Auth::user()->is_admin) {
|
||||||
|
abort(403);
|
||||||
|
}
|
||||||
request()->validate([
|
request()->validate([
|
||||||
'name' => ['required', 'unique:scoring_guides'],
|
'name' => ['required', 'unique:scoring_guides'],
|
||||||
]);
|
]);
|
||||||
|
|
@ -64,9 +75,12 @@ class ScoringGuideController extends Controller
|
||||||
|
|
||||||
public function destroy(ScoringGuide $guide)
|
public function destroy(ScoringGuide $guide)
|
||||||
{
|
{
|
||||||
|
if (! Auth::user()->is_admin) {
|
||||||
|
abort(403);
|
||||||
|
}
|
||||||
|
|
||||||
if ($guide->auditions()->count() > 0) {
|
if ($guide->auditions()->count() > 0) {
|
||||||
return redirect('/admin/scoring')->with('error',
|
return redirect('/admin/scoring')->with('error', 'Cannot delete scoring guide with auditions');
|
||||||
'Cannot delete scoring guide being used by one or more auditions');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$guide->delete();
|
$guide->delete();
|
||||||
|
|
@ -74,64 +88,89 @@ class ScoringGuideController extends Controller
|
||||||
return redirect('/admin/scoring')->with('success', 'Scoring guide deleted');
|
return redirect('/admin/scoring')->with('success', 'Scoring guide deleted');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function subscore_store(SubscoreDefinitionRequest $request, ScoringGuide $guide)
|
public function subscore_store(Request $request, ScoringGuide $guide)
|
||||||
{
|
{
|
||||||
|
if (! $guide->exists()) {
|
||||||
|
abort(409);
|
||||||
|
}
|
||||||
|
$validateData = request()->validate([
|
||||||
|
'name' => ['required'],
|
||||||
|
'max_score' => ['required', 'integer'],
|
||||||
|
'weight' => ['required', 'integer'],
|
||||||
|
'for_seating' => ['nullable', 'boolean'],
|
||||||
|
'for_advance' => ['nullable', 'boolean'],
|
||||||
|
]);
|
||||||
|
|
||||||
$validateData = $request->validated();
|
$for_seating = $request->has('for_seating') ? (bool) $request->input('for_seating') : false;
|
||||||
|
$for_advance = $request->has('for_advance') ? (bool) $request->input('for_advance') : false;
|
||||||
|
if (! auditionSetting('advanceTo')) {
|
||||||
|
$for_seating = true;
|
||||||
|
}
|
||||||
|
|
||||||
// Put the new subscore at the end of the list for both display and tiebreak order
|
|
||||||
$display_order = SubscoreDefinition::where('scoring_guide_id', '=', $guide->id)->max('display_order') + 1;
|
$display_order = SubscoreDefinition::where('scoring_guide_id', '=', $guide->id)->max('display_order') + 1;
|
||||||
$tiebreak_order = SubscoreDefinition::where('scoring_guide_id', '=', $guide->id)->max('tiebreak_order') + 1;
|
$tiebreak_order = SubscoreDefinition::where('scoring_guide_id', '=', $guide->id)->max('tiebreak_order') + 1;
|
||||||
if (! auditionSetting('advanceTo')) {
|
|
||||||
$validateData['for_advance'] = 0;
|
$subscore = SubscoreDefinition::create([
|
||||||
$validateData['for_seating'] = 1;
|
|
||||||
}
|
|
||||||
SubscoreDefinition::create([
|
|
||||||
'scoring_guide_id' => $guide->id,
|
'scoring_guide_id' => $guide->id,
|
||||||
'name' => $validateData['name'],
|
'name' => $validateData['name'],
|
||||||
'max_score' => $validateData['max_score'],
|
'max_score' => $validateData['max_score'],
|
||||||
'weight' => $validateData['weight'],
|
'weight' => $validateData['weight'],
|
||||||
'display_order' => $display_order,
|
'display_order' => $display_order,
|
||||||
'tiebreak_order' => $tiebreak_order,
|
'tiebreak_order' => $tiebreak_order,
|
||||||
'for_seating' => $validateData['for_seating'],
|
'for_seating' => $for_seating,
|
||||||
'for_advance' => $validateData['for_advance'],
|
'for_advance' => $for_advance,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return redirect(route('admin.scoring.edit', $guide))->with('success', 'Subscore added');
|
return redirect(route('admin.scoring.edit', $guide))->with('success', 'Subscore added');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function subscore_update(
|
public function subscore_update(ScoringGuide $guide, SubscoreDefinition $subscore)
|
||||||
SubscoreDefinitionRequest $request,
|
{
|
||||||
ScoringGuide $guide,
|
if (! Auth::user()->is_admin) {
|
||||||
SubscoreDefinition $subscore
|
abort(403);
|
||||||
) {
|
|
||||||
if ($subscore->scoring_guide_id !== $guide->id) { // Make sure the subscore were updating belongs to the guide
|
|
||||||
return redirect('/admin/scoring/guides/'.$subscore->scoring_guide_id.'/edit')->with('error',
|
|
||||||
'Cannot update a subscore for a different scoring guide');
|
|
||||||
}
|
}
|
||||||
$validateData = $validateData = $request->validated();
|
if (! $guide->exists() || ! $subscore->exists()) {
|
||||||
|
abort(409);
|
||||||
|
}
|
||||||
|
if ($subscore->scoring_guide_id !== $guide->id) { // Make sure the subscore were updating belongs to the guide
|
||||||
|
abort(409);
|
||||||
|
}
|
||||||
|
$validateData = request()->validate([
|
||||||
|
'name' => ['required'],
|
||||||
|
'max_score' => ['required', 'integer'],
|
||||||
|
'weight' => ['required', 'integer'],
|
||||||
|
'for_seating' => ['nullable', 'boolean'],
|
||||||
|
'for_advance' => ['nullable', 'boolean'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$for_seating = request()->has('for_seating') ? (bool) request()->input('for_seating') : false;
|
||||||
|
$for_advance = request()->has('for_advance') ? (bool) request()->input('for_advance') : false;
|
||||||
|
|
||||||
if (! auditionSetting('advanceTo')) {
|
if (! auditionSetting('advanceTo')) {
|
||||||
$validateData['for_advance'] = 0;
|
$for_seating = true;
|
||||||
$validateData['for_seating'] = 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$subscore->update([
|
$subscore->update([
|
||||||
'name' => $validateData['name'],
|
'name' => $validateData['name'],
|
||||||
'max_score' => $validateData['max_score'],
|
'max_score' => $validateData['max_score'],
|
||||||
'weight' => $validateData['weight'],
|
'weight' => $validateData['weight'],
|
||||||
'for_seating' => $validateData['for_seating'],
|
'for_seating' => $for_seating,
|
||||||
'for_advance' => $validateData['for_advance'],
|
'for_advance' => $for_advance,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return redirect(route('admin.scoring.edit', $guide))->with('success', 'Subscore updated');
|
return redirect('/admin/scoring/guides/'.$guide->id.'/edit')->with('success', 'Subscore updated');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function subscore_destroy(ScoringGuide $guide, SubscoreDefinition $subscore)
|
public function subscore_destroy(ScoringGuide $guide, SubscoreDefinition $subscore)
|
||||||
{
|
{
|
||||||
|
if (! Auth::user()->is_admin) {
|
||||||
|
abort(403);
|
||||||
|
}
|
||||||
|
if (! $guide->exists() || ! $subscore->exists()) {
|
||||||
|
abort(409);
|
||||||
|
}
|
||||||
if ($subscore->scoring_guide_id !== $guide->id) { // Make sure the subscore were updating belongs to the guide
|
if ($subscore->scoring_guide_id !== $guide->id) { // Make sure the subscore were updating belongs to the guide
|
||||||
|
abort(409);
|
||||||
return redirect(route('admin.scoring.edit', $subscore->scoring_guide_id))->with('error',
|
|
||||||
'Cannot delete a subscore for a different scoring guide');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$subscore->delete();
|
$subscore->delete();
|
||||||
|
|
@ -142,6 +181,9 @@ class ScoringGuideController extends Controller
|
||||||
|
|
||||||
public function reorder_display(Request $request)
|
public function reorder_display(Request $request)
|
||||||
{
|
{
|
||||||
|
if (! Auth::user()->is_admin) {
|
||||||
|
abort(403);
|
||||||
|
}
|
||||||
$order = $request->order;
|
$order = $request->order;
|
||||||
foreach ($order as $index => $id) {
|
foreach ($order as $index => $id) {
|
||||||
$subscore = SubscoreDefinition::find($id);
|
$subscore = SubscoreDefinition::find($id);
|
||||||
|
|
@ -154,6 +196,9 @@ class ScoringGuideController extends Controller
|
||||||
|
|
||||||
public function reorder_tiebreak(Request $request)
|
public function reorder_tiebreak(Request $request)
|
||||||
{
|
{
|
||||||
|
if (! Auth::user()->is_admin) {
|
||||||
|
abort(403);
|
||||||
|
}
|
||||||
$order = $request->order;
|
$order = $request->order;
|
||||||
foreach ($order as $index => $id) {
|
foreach ($order as $index => $id) {
|
||||||
$subscore = SubscoreDefinition::find($id);
|
$subscore = SubscoreDefinition::find($id);
|
||||||
|
|
|
||||||
|
|
@ -2,16 +2,17 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers\Admin;
|
namespace App\Http\Controllers\Admin;
|
||||||
|
|
||||||
use App\Actions\Students\CreateStudent;
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Http\Requests\StudentStoreRequest;
|
|
||||||
use App\Models\Audition;
|
use App\Models\Audition;
|
||||||
use App\Models\AuditLogEntry;
|
use App\Models\AuditLogEntry;
|
||||||
|
use App\Models\Entry;
|
||||||
use App\Models\Event;
|
use App\Models\Event;
|
||||||
use App\Models\NominationEnsemble;
|
use App\Models\NominationEnsemble;
|
||||||
use App\Models\School;
|
use App\Models\School;
|
||||||
use App\Models\Student;
|
use App\Models\Student;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
|
use function abort;
|
||||||
use function auth;
|
use function auth;
|
||||||
use function compact;
|
use function compact;
|
||||||
use function max;
|
use function max;
|
||||||
|
|
@ -24,6 +25,9 @@ class StudentController extends Controller
|
||||||
{
|
{
|
||||||
public function index()
|
public function index()
|
||||||
{
|
{
|
||||||
|
if (! Auth::user()->is_admin) {
|
||||||
|
abort(403);
|
||||||
|
}
|
||||||
$filters = session('adminStudentFilters') ?? null;
|
$filters = session('adminStudentFilters') ?? null;
|
||||||
$schools = School::orderBy('name')->get();
|
$schools = School::orderBy('name')->get();
|
||||||
$students = Student::with(['school'])->withCount('entries')->orderBy('last_name')->orderBy('first_name');
|
$students = Student::with(['school'])->withCount('entries')->orderBy('last_name')->orderBy('first_name');
|
||||||
|
|
@ -50,52 +54,155 @@ class StudentController extends Controller
|
||||||
|
|
||||||
public function create()
|
public function create()
|
||||||
{
|
{
|
||||||
$minGrade = $this->minimumGrade();
|
if (! Auth::user()->is_admin) {
|
||||||
$maxGrade = $this->maximumGrade();
|
abort(403);
|
||||||
|
}
|
||||||
|
$minGrade = min(Audition::min('minimum_grade'), NominationEnsemble::min('minimum_grade'));
|
||||||
|
$maxGrade = max(Audition::max('maximum_grade'), NominationEnsemble::max('maximum_grade'));
|
||||||
$schools = School::orderBy('name')->get();
|
$schools = School::orderBy('name')->get();
|
||||||
|
|
||||||
return view('admin.students.create', ['schools' => $schools, 'minGrade' => $minGrade, 'maxGrade' => $maxGrade]);
|
return view('admin.students.create', ['schools' => $schools, 'minGrade' => $minGrade, 'maxGrade' => $maxGrade]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function store(StudentStoreRequest $request, CreateStudent $creator)
|
public function store()
|
||||||
{
|
{
|
||||||
/** @noinspection PhpUnhandledExceptionInspection */
|
if (! Auth::user()->is_admin) {
|
||||||
$creator([
|
abort(403);
|
||||||
'first_name' => $request['first_name'],
|
}
|
||||||
'last_name' => $request['last_name'],
|
request()->validate([
|
||||||
'grade' => $request['grade'],
|
'first_name' => ['required'],
|
||||||
'school_id' => $request['school_id'],
|
'last_name' => ['required'],
|
||||||
'optional_data' => $request->optional_data,
|
'grade' => ['required', 'integer'],
|
||||||
|
'school_id' => ['required', 'exists:schools,id'],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return redirect(route('admin.students.index'))->with('success', 'Student created successfully');
|
if (Student::where('first_name', request('first_name'))
|
||||||
|
->where('last_name', request('last_name'))
|
||||||
|
->where('school_id', request('school_id'))
|
||||||
|
->exists()) {
|
||||||
|
return redirect('/admin/students/create')->with('error', 'This student already exists.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$student = Student::create([
|
||||||
|
'first_name' => request('first_name'),
|
||||||
|
'last_name' => request('last_name'),
|
||||||
|
'grade' => request('grade'),
|
||||||
|
'school_id' => request('school_id'),
|
||||||
|
]);
|
||||||
|
$message = 'Created student #'.$student->id.' - '.$student->full_name().'<br>Grade: '.$student->grade.'<br>School: '.$student->school->name;
|
||||||
|
AuditLogEntry::create([
|
||||||
|
'user' => auth()->user()->email,
|
||||||
|
'ip_address' => request()->ip(),
|
||||||
|
'message' => $message,
|
||||||
|
'affected' => [
|
||||||
|
'students' => [$student->id],
|
||||||
|
'schools' => [$student->school_id],
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
return redirect('/admin/students')->with('success', 'Created student successfully');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function edit(Student $student)
|
public function edit(Student $student)
|
||||||
{
|
{
|
||||||
$minGrade = $this->minimumGrade();
|
if (! Auth::user()->is_admin) {
|
||||||
$maxGrade = $this->maximumGrade();
|
abort(403);
|
||||||
|
}
|
||||||
|
$minGrade = min(Audition::min('minimum_grade'), NominationEnsemble::min('minimum_grade'));
|
||||||
|
$maxGrade = max(Audition::max('maximum_grade'), NominationEnsemble::max('maximum_grade'));
|
||||||
$schools = School::orderBy('name')->get();
|
$schools = School::orderBy('name')->get();
|
||||||
$student->loadCount('entries');
|
$student->loadCount('entries');
|
||||||
$event_entries = $student->entries()->with('audition.flags')->get()->groupBy('audition.event_id');
|
$entries = $student->entries;
|
||||||
$events = Event::all();
|
$events = Event::all();
|
||||||
|
$event_entries = [];
|
||||||
$logEntries = AuditLogEntry::whereJsonContains('affected->students', $student->id)->orderBy('created_at',
|
foreach ($events as $event) {
|
||||||
'desc')->get();
|
$event_entries[$event->id] = $entries->filter(function ($entry) use ($event) {
|
||||||
|
return $event->id === $entry->audition->event_id;
|
||||||
|
});
|
||||||
|
// Check if doubler status can change
|
||||||
|
foreach ($event_entries[$event->id] as $entry) {
|
||||||
|
$entry->doubler_decision_frozen = $this->isDoublerStatusFrozen($entry, $event_entries[$event->id]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return view('admin.students.edit',
|
return view('admin.students.edit',
|
||||||
compact('student', 'schools', 'minGrade', 'maxGrade', 'events', 'event_entries', 'logEntries'));
|
compact('student', 'schools', 'minGrade', 'maxGrade', 'events', 'event_entries'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function update(StudentStoreRequest $request, Student $student)
|
private function isDoublerStatusFrozen(Entry $entry, $entries)
|
||||||
{
|
{
|
||||||
|
// Can't change decision if results are published
|
||||||
|
if ($entry->audition->hasFlag('seats_published')) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Can't change decision if this is the only entry
|
||||||
|
if ($entries->count() === 1) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Can't change decision if this is the only entry with results not published
|
||||||
|
$unpublished = $entries->reject(function ($entry) {
|
||||||
|
return $entry->audition->hasFlag('seats_published');
|
||||||
|
});
|
||||||
|
if ($unpublished->count() < 2) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Can't change decision if we've accepted another audition
|
||||||
|
foreach ($entries as $checkEntry) {
|
||||||
|
if ($checkEntry->audition->hasFlag('seats_published') && ! $checkEntry->hasFlag('declined')) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update(Student $student)
|
||||||
|
{
|
||||||
|
if (! Auth::user()->is_admin) {
|
||||||
|
abort(403);
|
||||||
|
}
|
||||||
|
request()->validate([
|
||||||
|
'first_name' => ['required'],
|
||||||
|
'last_name' => ['required'],
|
||||||
|
'grade' => ['required', 'integer'],
|
||||||
|
'school_id' => ['required', 'exists:schools,id'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
foreach ($student->entries as $entry) {
|
||||||
|
if ($entry->audition->minimum_grade > request('grade') || $entry->audition->maximum_grade < request('grade')) {
|
||||||
|
return redirect('/admin/students/'.$student->id.'/edit')->with('error',
|
||||||
|
'This student is entered in an audition that is not available to their new grade.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Student::where('first_name', request('first_name'))
|
||||||
|
->where('last_name', request('last_name'))
|
||||||
|
->where('school_id', request('school_id'))
|
||||||
|
->where('id', '!=', $student->id)
|
||||||
|
->exists()) {
|
||||||
|
return redirect('/admin/students/'.$student->id.'/edit')->with('error',
|
||||||
|
'A student with that name already exists at that school');
|
||||||
|
}
|
||||||
|
|
||||||
$student->update([
|
$student->update([
|
||||||
'first_name' => $request['first_name'],
|
'first_name' => request('first_name'),
|
||||||
'last_name' => $request['last_name'],
|
'last_name' => request('last_name'),
|
||||||
'grade' => $request['grade'],
|
'grade' => request('grade'),
|
||||||
'school_id' => $request['school_id'],
|
'school_id' => request('school_id'),
|
||||||
'optional_data' => $request->optional_data,
|
]);
|
||||||
|
|
||||||
|
$message = 'Updated student #'.$student->id.'<br>Name: '.$student->full_name().'<br>Grade: '.$student->grade.'<br>School: '.$student->school->name;
|
||||||
|
AuditLogEntry::create([
|
||||||
|
'user' => auth()->user()->email,
|
||||||
|
'ip_address' => request()->ip(),
|
||||||
|
'message' => $message,
|
||||||
|
'affected' => [
|
||||||
|
'students' => [$student->id],
|
||||||
|
'schools' => [$student->school_id],
|
||||||
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return redirect('/admin/students')->with('success', 'Student updated');
|
return redirect('/admin/students')->with('success', 'Student updated');
|
||||||
|
|
@ -105,7 +212,7 @@ class StudentController extends Controller
|
||||||
public function destroy(Student $student)
|
public function destroy(Student $student)
|
||||||
{
|
{
|
||||||
if ($student->entries()->count() > 0) {
|
if ($student->entries()->count() > 0) {
|
||||||
return to_route('admin.students.index')->with('error', 'Student has entries and cannot be deleted');
|
return to_route('admin.students.index')->with('error', 'You cannot delete a student with entries.');
|
||||||
}
|
}
|
||||||
$name = $student->full_name();
|
$name = $student->full_name();
|
||||||
$message = 'Deleted student #'.$student->id.'<br>Name: '.$student->full_name().'<br>Grade: '.$student->grade.'<br>School: '.$student->school->name;
|
$message = 'Deleted student #'.$student->id.'<br>Name: '.$student->full_name().'<br>Grade: '.$student->grade.'<br>School: '.$student->school->name;
|
||||||
|
|
@ -123,33 +230,8 @@ class StudentController extends Controller
|
||||||
return to_route('admin.students.index')->with('success', 'Student '.$name.' deleted successfully.');
|
return to_route('admin.students.index')->with('success', 'Student '.$name.' deleted successfully.');
|
||||||
}
|
}
|
||||||
|
|
||||||
private function minimumGrade(): int
|
public function set_filter()
|
||||||
{
|
{
|
||||||
$nomMin = NominationEnsemble::min('minimum_grade');
|
//
|
||||||
$normMin = Audition::min('minimum_grade');
|
|
||||||
|
|
||||||
if (is_null($nomMin)) {
|
|
||||||
$minGrade = $normMin;
|
|
||||||
} else {
|
|
||||||
$minGrade = min($nomMin, $normMin);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $minGrade;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private function maximumGrade(): int
|
|
||||||
{
|
|
||||||
$nomMax = NominationEnsemble::max('maximum_grade');
|
|
||||||
$normMax = Audition::max('maximum_grade');
|
|
||||||
|
|
||||||
if (is_null($nomMax)) {
|
|
||||||
$maxGrade = $normMax;
|
|
||||||
} else {
|
|
||||||
$maxGrade = max($nomMax, $normMax);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $maxGrade;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
/** @noinspection PhpUnhandledExceptionInspection */
|
|
||||||
|
|
||||||
namespace App\Http\Controllers\Admin;
|
namespace App\Http\Controllers\Admin;
|
||||||
|
|
||||||
use App\Actions\Fortify\CreateNewUser;
|
|
||||||
use App\Actions\Fortify\UpdateUserPrivileges;
|
|
||||||
use App\Actions\Fortify\UpdateUserProfileInformation;
|
|
||||||
use App\Actions\Schools\AssignUserToSchool;
|
|
||||||
use App\Actions\Schools\SetHeadDirector;
|
use App\Actions\Schools\SetHeadDirector;
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Mail\NewUserPassword;
|
use App\Mail\NewUserPassword;
|
||||||
|
|
@ -15,6 +9,7 @@ use App\Models\AuditLogEntry;
|
||||||
use App\Models\School;
|
use App\Models\School;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Illuminate\Support\Facades\Hash;
|
use Illuminate\Support\Facades\Hash;
|
||||||
use Illuminate\Support\Facades\Mail;
|
use Illuminate\Support\Facades\Mail;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
|
|
@ -25,6 +20,9 @@ class UserController extends Controller
|
||||||
{
|
{
|
||||||
public function index()
|
public function index()
|
||||||
{
|
{
|
||||||
|
if (! Auth::user()->is_admin) {
|
||||||
|
abort(403);
|
||||||
|
}
|
||||||
$users = User::with('school')->with('flags')->orderBy('last_name')->orderBy('first_name')->get();
|
$users = User::with('school')->with('flags')->orderBy('last_name')->orderBy('first_name')->get();
|
||||||
|
|
||||||
return view('admin.users.index', ['users' => $users]);
|
return view('admin.users.index', ['users' => $users]);
|
||||||
|
|
@ -32,65 +30,95 @@ class UserController extends Controller
|
||||||
|
|
||||||
public function edit(User $user)
|
public function edit(User $user)
|
||||||
{
|
{
|
||||||
|
if (! Auth::user()->is_admin) {
|
||||||
|
abort(403);
|
||||||
|
}
|
||||||
$schools = School::orderBy('name')->get();
|
$schools = School::orderBy('name')->get();
|
||||||
$logEntries = AuditLogEntry::whereJsonContains('affected->users', $user->id)->orderBy('created_at',
|
|
||||||
'desc')->get();
|
|
||||||
$userActions = AuditLogEntry::where('user', $user->email)->orderBy('created_at', 'desc')->get();
|
|
||||||
|
|
||||||
return view('admin.users.edit', compact('user', 'schools', 'logEntries', 'userActions'));
|
return view('admin.users.edit', ['user' => $user, 'schools' => $schools]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function create()
|
public function create()
|
||||||
{
|
{
|
||||||
|
if (! Auth::user()->is_admin) {
|
||||||
|
abort(403);
|
||||||
|
}
|
||||||
$schools = School::orderBy('name')->get();
|
$schools = School::orderBy('name')->get();
|
||||||
|
|
||||||
return view('admin.users.create', ['schools' => $schools]);
|
return view('admin.users.create', ['schools' => $schools]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function update(
|
public function update(Request $request, User $user, SetHeadDirector $headSetter)
|
||||||
Request $request,
|
{
|
||||||
User $user,
|
if (! Auth::user()->is_admin) {
|
||||||
SetHeadDirector $headSetter,
|
abort(403);
|
||||||
UpdateUserProfileInformation $profileUpdater,
|
|
||||||
AssignUserToSchool $schoolAssigner,
|
|
||||||
UpdateUserPrivileges $privilegesUpdater
|
|
||||||
) {
|
|
||||||
// Update basic profile data
|
|
||||||
$profileData = [
|
|
||||||
'first_name' => $request->get('first_name'),
|
|
||||||
'last_name' => $request->get('last_name'),
|
|
||||||
'email' => $request->get('email'),
|
|
||||||
'cell_phone' => $request->get('cell_phone'),
|
|
||||||
'judging_preference' => $request->get('judging_preference'),
|
|
||||||
];
|
|
||||||
$profileUpdater->update($user, $profileData);
|
|
||||||
|
|
||||||
// Deal with school assignment
|
|
||||||
dump($request->get('school_id'));
|
|
||||||
if ($user->school_id != $request->get('school_id')) {
|
|
||||||
$schoolAssigner($user, $request->get('school_id'));
|
|
||||||
}
|
}
|
||||||
|
$oldEmail = $user->email;
|
||||||
|
$wasAdmin = $user->is_admin;
|
||||||
|
$wasTab = $user->is_tab;
|
||||||
|
$validData = $request->validate([
|
||||||
|
'first_name' => ['required'],
|
||||||
|
'last_name' => ['required'],
|
||||||
|
'email' => ['required', 'email'],
|
||||||
|
'cell_phone' => ['required'],
|
||||||
|
'judging_preference' => ['required'],
|
||||||
|
'school_id' => ['nullable', 'exists:schools,id'],
|
||||||
|
]);
|
||||||
|
$validData['is_admin'] = $request->get('is_admin') == 'on' ? 1 : 0;
|
||||||
|
$validData['is_tab'] = $request->get('is_tab') == 'on' ? 1 : 0;
|
||||||
|
$validData['is_head'] = $request->get('is_head') == 'on' ? 1 : 0;
|
||||||
|
$user->update([
|
||||||
|
'first_name' => $validData['first_name'],
|
||||||
|
'last_name' => $validData['last_name'],
|
||||||
|
'email' => $validData['email'],
|
||||||
|
'cell_phone' => $validData['cell_phone'],
|
||||||
|
'judging_preference' => $validData['judging_preference'],
|
||||||
|
'school_id' => $validData['school_id'],
|
||||||
|
'is_admin' => $validData['is_admin'],
|
||||||
|
'is_tab' => $validData['is_tab'],
|
||||||
|
]);
|
||||||
|
$user->refresh();
|
||||||
|
$logged_school = $user->school_id ? $user->school->name : 'No School';
|
||||||
|
$message = 'Updated user #'.$user->id.' - '.$oldEmail
|
||||||
|
.'<br>Name: '.$user->full_name()
|
||||||
|
.'<br>Email: '.$user->email
|
||||||
|
.'<br>Cell Phone: '.$user->cell_phone
|
||||||
|
.'<br>Judging Pref: '.$user->judging_preference
|
||||||
|
.'<br>School: '.$logged_school;
|
||||||
|
|
||||||
// Deal with the head director flag
|
AuditLogEntry::create([
|
||||||
if ($request->has('head_director')) {
|
'user' => auth()->user()->email,
|
||||||
$headSetter($user);
|
'ip_address' => request()->ip(),
|
||||||
} else {
|
'message' => $message,
|
||||||
$user->removeFlag('head_director');
|
'affected' => ['users' => [$user->id]],
|
||||||
|
]);
|
||||||
|
if ($user->is_admin != $wasAdmin) {
|
||||||
|
$messageStart = $user->is_admin ? 'Granted admin privileges to ' : 'Revoked admin privileges from ';
|
||||||
|
AuditLogEntry::create([
|
||||||
|
'user' => auth()->user()->email,
|
||||||
|
'ip_address' => request()->ip(),
|
||||||
|
'message' => $messageStart.$user->full_name().' - '.$user->email,
|
||||||
|
'affected' => ['users' => [$user->id]],
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
if ($user->is_tab != $wasTab) {
|
||||||
// Deal with privileges
|
$messageStart = $user->is_tab ? 'Granted tabulation privileges to ' : 'Revoked tabulation privileges from ';
|
||||||
if ($request->has('is_admin')) {
|
AuditLogEntry::create([
|
||||||
$privilegesUpdater($user, 'grant', 'admin');
|
'user' => auth()->user()->email,
|
||||||
} else {
|
'ip_address' => request()->ip(),
|
||||||
$privilegesUpdater($user, 'revoke', 'admin');
|
'message' => $messageStart.$user->full_name().' - '.$user->email,
|
||||||
|
'affected' => ['users' => [$user->id]],
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
if ($user->hasFlag('head_director') != $validData['is_head'] && ! is_null($user->school_id)) {
|
||||||
if ($request->has('is_tab')) {
|
if ($validData['is_head']) {
|
||||||
$privilegesUpdater($user, 'grant', 'tab');
|
$headSetter->setHeadDirector($user);
|
||||||
} else {
|
} else {
|
||||||
$privilegesUpdater($user, 'revoke', 'tab');
|
$user->removeFlag('head_director');
|
||||||
|
$logMessage = 'Removed '.$user->full_name().' as head director at '.$user->school->name;
|
||||||
|
$logAffected = ['users' => [$user->id], 'schools' => [$user->school_id]];
|
||||||
|
auditionLog($logMessage, $logAffected);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect('/admin/users');
|
return redirect('/admin/users');
|
||||||
|
|
@ -98,23 +126,60 @@ class UserController extends Controller
|
||||||
|
|
||||||
public function store(Request $request)
|
public function store(Request $request)
|
||||||
{
|
{
|
||||||
$userCreator = app(CreateNewUser::class);
|
$request->validate([
|
||||||
$randomPassword = Str::random(12);
|
'first_name' => ['required'],
|
||||||
$data = request()->all();
|
'last_name' => ['required'],
|
||||||
$data['password'] = $randomPassword;
|
'email' => ['required', 'email', 'unique:users'],
|
||||||
$data['password_confirmation'] = $randomPassword;
|
|
||||||
$newDirector = $userCreator->create($data);
|
|
||||||
$newDirector->update([
|
|
||||||
'school_id' => $request->get('school_id') ?? null,
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
Mail::to($newDirector->email)->send(new NewUserPassword($newDirector, $randomPassword));
|
// Generate a random password
|
||||||
|
$randomPassword = Str::random(12);
|
||||||
|
|
||||||
return redirect(route('admin.users.index'))->with('success', 'Director added');
|
$user = User::make([
|
||||||
|
'first_name' => request('first_name'),
|
||||||
|
'last_name' => request('last_name'),
|
||||||
|
'email' => request('email'),
|
||||||
|
'cell_phone' => request('cell_phone'),
|
||||||
|
'judging_preference' => request('judging_preference'),
|
||||||
|
'password' => Hash::make($randomPassword),
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (! is_null(request('school_id'))) {
|
||||||
|
$request->validate([
|
||||||
|
'school_id' => ['exists:schools,id'],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
$user->school_id = request('school_id');
|
||||||
|
$user->save();
|
||||||
|
$message = 'Created user '.$user->email.' - '.$user->full_name().'<br>Cell Phone: '.$user->cell_phone.'<br>Judging Pref: '.$user->judging_preference;
|
||||||
|
AuditLogEntry::create([
|
||||||
|
'user' => auth()->user()->email,
|
||||||
|
'ip_address' => request()->ip(),
|
||||||
|
'message' => $message,
|
||||||
|
'affected' => ['users' => [$user->id]],
|
||||||
|
]);
|
||||||
|
if ($user->school_id) {
|
||||||
|
$message = 'Set user '.$user->full_name().' ('.$user->email.') as a director at '.$user->school->name.'(#'.$user->school->id.')';
|
||||||
|
AuditLogEntry::create([
|
||||||
|
'user' => auth()->user()->email,
|
||||||
|
'ip_address' => request()->ip(),
|
||||||
|
'message' => $message,
|
||||||
|
'affected' => [
|
||||||
|
'users' => [$user->id],
|
||||||
|
'schools' => [$user->id],
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
Mail::to($user->email)->send(new NewUserPassword($user, $randomPassword));
|
||||||
|
|
||||||
|
return redirect('/admin/users');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function destroy(User $user)
|
public function destroy(User $user)
|
||||||
{
|
{
|
||||||
|
if (! Auth::user()->is_admin) {
|
||||||
|
abort(403);
|
||||||
|
}
|
||||||
$message = 'Deleted user '.$user->email;
|
$message = 'Deleted user '.$user->email;
|
||||||
AuditLogEntry::create([
|
AuditLogEntry::create([
|
||||||
'user' => auth()->user()->email,
|
'user' => auth()->user()->email,
|
||||||
|
|
@ -126,22 +191,4 @@ class UserController extends Controller
|
||||||
|
|
||||||
return redirect()->route('admin.users.index')->with('success', 'User deleted successfully');
|
return redirect()->route('admin.users.index')->with('success', 'User deleted successfully');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setPassword(User $user, Request $request)
|
|
||||||
{
|
|
||||||
$validated = $request->validate([
|
|
||||||
'admin_password' => ['required', 'string', 'current_password:web'],
|
|
||||||
'new_password' => ['required', 'string', 'confirmed', 'min:8'],
|
|
||||||
]);
|
|
||||||
$user->forceFill([
|
|
||||||
'password' => Hash::make($validated['new_password']),
|
|
||||||
])->save();
|
|
||||||
|
|
||||||
auditionLog('Manually set password for '.$user->email, [
|
|
||||||
'users' => [$user->id],
|
|
||||||
]);
|
|
||||||
|
|
||||||
return redirect()->route('admin.users.index')->with('success',
|
|
||||||
'Password changed successfully for '.$user->email);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Controllers\Admin;
|
|
||||||
|
|
||||||
use App\Actions\YearEndProcedures\YearEndCleanup;
|
|
||||||
use App\Http\Controllers\Controller;
|
|
||||||
|
|
||||||
use function auditionLog;
|
|
||||||
|
|
||||||
class YearEndResetController extends Controller
|
|
||||||
{
|
|
||||||
public function index()
|
|
||||||
{
|
|
||||||
return view('admin.year_end_reset');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function execute()
|
|
||||||
{
|
|
||||||
$cleanUpProcedure = app(YearEndCleanup::class);
|
|
||||||
$options = request()->options;
|
|
||||||
$cleanUpProcedure($options);
|
|
||||||
auditionLog('Executed year end reset.', []);
|
|
||||||
|
|
||||||
return redirect()->route('dashboard')->with('success', 'Year end reset completed');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -2,6 +2,10 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Actions\Entries\GetEntrySeatingResult;
|
||||||
|
use App\Actions\Tabulation\CalculateEntryScore;
|
||||||
|
use App\Actions\Tabulation\RankAuditionEntries;
|
||||||
|
use App\Models\AuditionFlag;
|
||||||
use App\Models\School;
|
use App\Models\School;
|
||||||
use App\Services\Invoice\InvoiceDataService;
|
use App\Services\Invoice\InvoiceDataService;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
@ -24,15 +28,40 @@ class DashboardController extends Controller
|
||||||
}
|
}
|
||||||
|
|
||||||
public function dashboard(
|
public function dashboard(
|
||||||
|
CalculateEntryScore $scoreCalc,
|
||||||
|
GetEntrySeatingResult $resultGenerator,
|
||||||
|
RankAuditionEntries $ranker
|
||||||
) {
|
) {
|
||||||
return view('dashboard.dashboard');
|
|
||||||
|
// Info for director results report
|
||||||
|
$entries = Auth::user()->entries;
|
||||||
|
$entries = $entries->filter(function ($entry) {
|
||||||
|
return $entry->audition->hasFlag('seats_published');
|
||||||
|
});
|
||||||
|
$entries = $entries->sortBy(function ($entry) {
|
||||||
|
return $entry->student->full_name(true);
|
||||||
|
});
|
||||||
|
$scores = [];
|
||||||
|
$results = [];
|
||||||
|
$ranks = [];
|
||||||
|
foreach ($entries as $entry) {
|
||||||
|
$results[$entry->id] = $resultGenerator->getResult($entry);
|
||||||
|
if (! $entry->hasFlag('no_show') && ! $entry->hasFlag('failed_prelim')) {
|
||||||
|
$scores[$entry->id] = $scoreCalc->calculate('seating', $entry);
|
||||||
|
$auditionResults = $ranker->rank('seating', $entry->audition);
|
||||||
|
$ranks[$entry->id] = $auditionResults->firstWhere('id', $entry->id)->raw_rank;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$showRecapLink = AuditionFlag::where('flag_name', 'seats_published')->count() > 0;
|
||||||
|
|
||||||
|
return view('dashboard.dashboard', compact('entries', 'scores', 'results', 'ranks', 'showRecapLink'));
|
||||||
// return view('dashboard.dashboard');
|
// return view('dashboard.dashboard');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function my_school()
|
public function my_school()
|
||||||
{
|
{
|
||||||
if (Auth::user()->school) {
|
if (Auth::user()->school) {
|
||||||
return redirect(route('schools.show', auth()->user()->school));
|
return redirect('/schools/'.Auth::user()->school->id);
|
||||||
}
|
}
|
||||||
$possibilities = Auth::user()->possibleSchools();
|
$possibilities = Auth::user()->possibleSchools();
|
||||||
if (count($possibilities) < 1) {
|
if (count($possibilities) < 1) {
|
||||||
|
|
|
||||||
|
|
@ -2,67 +2,88 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App\Http\Requests\DoublerRequestsStoreRequest;
|
use App\Models\AuditLogEntry;
|
||||||
use App\Models\DoublerRequest;
|
use App\Models\DoublerRequest;
|
||||||
use App\Models\Event;
|
use App\Models\Event;
|
||||||
use Illuminate\Contracts\Foundation\Application;
|
use App\Models\Student;
|
||||||
use Illuminate\Contracts\View\Factory;
|
use App\Services\DoublerService;
|
||||||
use Illuminate\Contracts\View\View;
|
use Barryvdh\Debugbar\Facades\Debugbar;
|
||||||
|
use Psr\Container\ContainerExceptionInterface;
|
||||||
|
use Psr\Container\NotFoundExceptionInterface;
|
||||||
|
|
||||||
use function auth;
|
use function auth;
|
||||||
use function compact;
|
use function compact;
|
||||||
|
use function request;
|
||||||
use function to_route;
|
use function to_route;
|
||||||
|
|
||||||
class DoublerRequestController extends Controller
|
class DoublerRequestController extends Controller
|
||||||
{
|
{
|
||||||
/**
|
public function index(DoublerService $doublerService)
|
||||||
* Display a listing of the resource.
|
|
||||||
*
|
|
||||||
* Data sent to view:
|
|
||||||
* - events - all existing events
|
|
||||||
* - existingRequests - previously made requests for each event, keyed by student id
|
|
||||||
* existingRequest[eventId][student id]-> Request
|
|
||||||
* - doublers - existing doublers, grouped by event. Keyed by event_id and student_id
|
|
||||||
*
|
|
||||||
* @return Application|Factory|View|\Illuminate\Foundation\Application|\Illuminate\View\View
|
|
||||||
*/
|
|
||||||
public function index()
|
|
||||||
{
|
{
|
||||||
$events = Event::all();
|
$events = Event::all();
|
||||||
$existingRequests = auth()->user()->school->doublerRequests
|
$students = auth()->user()->school->students;
|
||||||
->groupBy('event_id')
|
$studentIds = $students->pluck('id');
|
||||||
->map(function ($requestsForEvent) {
|
$existingRequests = DoublerRequest::whereIn('student_id', $studentIds)->get();
|
||||||
return $requestsForEvent->keyBy('student_id');
|
$doublers = [];
|
||||||
});
|
foreach ($events as $event) {
|
||||||
$doublers = auth()->user()->school->doublers()
|
$event_doublers = $doublerService->doublersForEvent($event);
|
||||||
->with('student')
|
$doublers[$event->id] = $event_doublers;
|
||||||
->with('event')
|
|
||||||
->get()
|
|
||||||
->groupBy('event_id');
|
|
||||||
|
|
||||||
return view('doubler_request.index', compact('events', 'doublers', 'existingRequests'));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function makeRequest(DoublerRequestsStoreRequest $request)
|
|
||||||
{
|
|
||||||
foreach ($request->getDoublerRequests() as $thisRequest) {
|
|
||||||
if (! $thisRequest['request']) {
|
|
||||||
DoublerRequest::where('event_id', $thisRequest['event_id'])
|
|
||||||
->where('student_id', $thisRequest['student_id'])->delete();
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
DoublerRequest::upsert([
|
|
||||||
'event_id' => $thisRequest['event_id'],
|
|
||||||
'student_id' => $thisRequest['student_id'],
|
|
||||||
'request' => $thisRequest['request'],
|
|
||||||
],
|
|
||||||
uniqueBy: ['event_id', 'student_id'],
|
|
||||||
update: ['request']
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return to_route('doubler_request.index')->with('success', 'Recorded doubler requests');
|
return view('doubler_request.index', compact('events', 'doublers', 'students', 'existingRequests'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws ContainerExceptionInterface
|
||||||
|
* @throws NotFoundExceptionInterface
|
||||||
|
*/
|
||||||
|
public function makeRequest()
|
||||||
|
{
|
||||||
|
foreach (request()->get('doubler_requests') as $event_id => $requests) {
|
||||||
|
if (! Event::find($event_id)->exists()) {
|
||||||
|
return to_route('doubler_request.index')->with('error', 'Invalid event id specified');
|
||||||
|
}
|
||||||
|
$thisEvent = Event::find($event_id);
|
||||||
|
foreach ($requests as $student_id => $request) {
|
||||||
|
if (! Student::find($student_id)->exists()) {
|
||||||
|
return to_route('doubler_request.index')->with('error', 'Invalid student id specified');
|
||||||
|
}
|
||||||
|
$thisStudent = Student::find($student_id);
|
||||||
|
if (! $request) {
|
||||||
|
$oldRequest = DoublerRequest::where('student_id', $student_id)
|
||||||
|
->where('event_id', $event_id)
|
||||||
|
->first();
|
||||||
|
if ($oldRequest) {
|
||||||
|
Debugbar::info('hit');
|
||||||
|
AuditLogEntry::create([
|
||||||
|
'user' => auth()->user()->email,
|
||||||
|
'ip_address' => request()->ip(),
|
||||||
|
'message' => 'Removed doubler request for '.$thisStudent->full_name().' in '.$thisEvent->name,
|
||||||
|
'affected' => ['students' => [$student_id]],
|
||||||
|
]);
|
||||||
|
$oldRequest->delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
DoublerRequest::upsert([
|
||||||
|
'event_id' => $event_id,
|
||||||
|
'student_id' => $student_id,
|
||||||
|
'request' => $request,
|
||||||
|
],
|
||||||
|
uniqueBy: ['event_id', 'student_id'],
|
||||||
|
update: ['request']
|
||||||
|
);
|
||||||
|
AuditLogEntry::create([
|
||||||
|
'user' => auth()->user()->email,
|
||||||
|
'ip_address' => request()->ip(),
|
||||||
|
'message' => 'Made doubler request for '.$thisStudent->full_name().' in '.$thisEvent->name.'<br>Request: '.$request,
|
||||||
|
'affected' => ['students' => [$student_id]],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
echo 'hi';
|
||||||
|
|
||||||
|
return to_route('doubler_request.index')->with('success', 'Recorded doubler requests');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,11 @@
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App\Actions\Entries\CreateEntry;
|
use App\Actions\Entries\CreateEntry;
|
||||||
use App\Http\Requests\EntryStoreRequest;
|
use App\Exceptions\ManageEntryException;
|
||||||
use App\Models\Audition;
|
use App\Models\Audition;
|
||||||
|
use App\Models\AuditLogEntry;
|
||||||
use App\Models\Entry;
|
use App\Models\Entry;
|
||||||
|
use Carbon\Carbon;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
|
|
@ -15,19 +17,11 @@ class EntryController extends Controller
|
||||||
{
|
{
|
||||||
public function index()
|
public function index()
|
||||||
{
|
{
|
||||||
if (! auth()->user()->school_id) {
|
|
||||||
abort(403);
|
|
||||||
}
|
|
||||||
|
|
||||||
$entries = Auth::user()->entries()
|
$entries = Auth::user()->entries()->with(['student', 'audition'])->get();
|
||||||
->select('entries.*')
|
$entries = $entries->sortBy(function ($entry) {
|
||||||
->join('students as s', 's.id', '=', 'entries.student_id')
|
return $entry->student->last_name.$entry->student->first_name.$entry->audition->score_order;
|
||||||
->join('auditions as a', 'a.id', '=', 'entries.audition_id')
|
});
|
||||||
->with(['student', 'audition'])
|
|
||||||
->orderBy('s.last_name')
|
|
||||||
->orderBy('s.first_name')
|
|
||||||
->orderBy('a.score_order')
|
|
||||||
->get();
|
|
||||||
$auditions = Audition::open()->get();
|
$auditions = Audition::open()->get();
|
||||||
$students = Auth::user()->students;
|
$students = Auth::user()->students;
|
||||||
$students->load('school');
|
$students->load('school');
|
||||||
|
|
@ -35,15 +29,37 @@ class EntryController extends Controller
|
||||||
return view('entries.index', ['entries' => $entries, 'students' => $students, 'auditions' => $auditions]);
|
return view('entries.index', ['entries' => $entries, 'students' => $students, 'auditions' => $auditions]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function store(EntryStoreRequest $request, CreateEntry $creator)
|
public function store(Request $request, CreateEntry $creator)
|
||||||
{
|
{
|
||||||
$validData = $request->validatedWithEnterFor();
|
if ($request->user()->cannot('create', Entry::class)) {
|
||||||
$creator(
|
abort(403);
|
||||||
$validData['student_id'],
|
}
|
||||||
$validData['audition_id'],
|
$validData = $request->validate([
|
||||||
for_seating: $validData['for_seating'],
|
'student_id' => ['required', 'exists:students,id'],
|
||||||
for_advancement: $validData['for_advancement'],
|
'audition_id' => ['required', 'exists:auditions,id'],
|
||||||
);
|
]);
|
||||||
|
$audition = Audition::find($validData['audition_id']);
|
||||||
|
$currentDate = Carbon::now('America/Chicago');
|
||||||
|
$currentDate = $currentDate->format('Y-m-d');
|
||||||
|
if ($audition->entry_deadline < $currentDate) {
|
||||||
|
return redirect()->route('entries.index')->with('error', 'The entry deadline for that audition has passed');
|
||||||
|
}
|
||||||
|
|
||||||
|
$validData['for_seating'] = $request->get('for_seating') ? 1 : 0;
|
||||||
|
$validData['for_advancement'] = $request->get('for_advancement') ? 1 : 0;
|
||||||
|
$enter_for = [];
|
||||||
|
if ($validData['for_seating']) {
|
||||||
|
$enter_for[] = 'seating';
|
||||||
|
}
|
||||||
|
if ($validData['for_advancement']) {
|
||||||
|
$enter_for[] = 'advancement';
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$creator($validData['student_id'], $validData['audition_id'], $enter_for);
|
||||||
|
} catch (ManageEntryException $ex) {
|
||||||
|
return redirect()->route('entries.index')->with('error', $ex->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
return redirect()->route('entries.index')->with('success', 'The entry has been added.');
|
return redirect()->route('entries.index')->with('success', 'The entry has been added.');
|
||||||
}
|
}
|
||||||
|
|
@ -53,7 +69,21 @@ class EntryController extends Controller
|
||||||
if ($request->user()->cannot('delete', $entry)) {
|
if ($request->user()->cannot('delete', $entry)) {
|
||||||
abort(403);
|
abort(403);
|
||||||
}
|
}
|
||||||
|
if (auth()->user()) {
|
||||||
|
$message = 'Deleted entry '.$entry->id;
|
||||||
|
$affected = [
|
||||||
|
'entries' => [$entry->id],
|
||||||
|
'auditions' => [$entry->audition_id],
|
||||||
|
'schools' => [$entry->student->school_id],
|
||||||
|
'students' => [$entry->student_id],
|
||||||
|
];
|
||||||
|
AuditLogEntry::create([
|
||||||
|
'user' => auth()->user()->email,
|
||||||
|
'ip_address' => request()->ip(),
|
||||||
|
'message' => $message,
|
||||||
|
'affected' => $affected,
|
||||||
|
]);
|
||||||
|
}
|
||||||
$entry->delete();
|
$entry->delete();
|
||||||
|
|
||||||
return redirect()->route('entries.index')->with('success',
|
return redirect()->route('entries.index')->with('success',
|
||||||
|
|
|
||||||
|
|
@ -20,14 +20,14 @@ class FilterController extends Controller
|
||||||
|
|
||||||
session(['adminEntryFilters' => $filters]);
|
session(['adminEntryFilters' => $filters]);
|
||||||
|
|
||||||
return redirect(route('admin.entries.index'))->with('success', 'Filters Applied');
|
return redirect('/admin/entries')->with('success', 'Filters Applied');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function clearAdminEntryFilter(Request $request)
|
public function clearAdminEntryFilter(Request $request)
|
||||||
{
|
{
|
||||||
session()->forget('adminEntryFilters');
|
session()->forget('adminEntryFilters');
|
||||||
|
|
||||||
return redirect(route('admin.entries.index'))->with('success', 'Filters Cleared');
|
return redirect('/admin/entries')->with('success', 'Filters Cleared');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function adminStudentFilter(Request $request)
|
public function adminStudentFilter(Request $request)
|
||||||
|
|
@ -40,7 +40,7 @@ class FilterController extends Controller
|
||||||
|
|
||||||
session(['adminStudentFilters' => $filters]);
|
session(['adminStudentFilters' => $filters]);
|
||||||
|
|
||||||
return redirect(route('admin.students.index'))->with('success', 'Filters Applied');
|
return redirect()->back()->with('success', 'Filters Applied');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function clearAdminStudentFilter()
|
public function clearAdminStudentFilter()
|
||||||
|
|
|
||||||
|
|
@ -12,23 +12,16 @@ use function redirect;
|
||||||
|
|
||||||
class BonusScoreEntryController extends Controller
|
class BonusScoreEntryController extends Controller
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* Displays a form for a judge to enter a bonus score for an entry.
|
|
||||||
*/
|
|
||||||
public function __invoke(Entry $entry)
|
public function __invoke(Entry $entry)
|
||||||
{
|
{
|
||||||
// We can't submit another bonus score for this entry if we have already submitted one.
|
|
||||||
if (BonusScore::where('entry_id', $entry->id)->where('user_id', Auth::user()->id)->exists()) {
|
if (BonusScore::where('entry_id', $entry->id)->where('user_id', Auth::user()->id)->exists()) {
|
||||||
return redirect()->route('judging.bonusScore.EntryList', $entry->audition)->with('error',
|
return redirect()->route('judging.bonusScore.EntryList', $entry->audition)->with('error', 'You have already judged that entry');
|
||||||
'You have already judged that entry');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @var BonusScoreDefinition $bonusScore */
|
/** @var BonusScoreDefinition $bonusScore */
|
||||||
$bonusScore = $entry->audition->bonusScore()->first();
|
$bonusScore = $entry->audition->bonusScore()->first();
|
||||||
if (! $bonusScore->judges->contains(auth()->id())) {
|
if (! $bonusScore->judges->contains(auth()->id())) {
|
||||||
return redirect()->route('judging.index')->with('error', 'You are not assigned to judge that entry');
|
return redirect()->route('judging.index')->with('error', 'You are not assigned to judge this entry');
|
||||||
}
|
}
|
||||||
|
|
||||||
$maxScore = $bonusScore->max_score;
|
$maxScore = $bonusScore->max_score;
|
||||||
$bonusName = $bonusScore->name;
|
$bonusName = $bonusScore->name;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,15 +10,12 @@ use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
class BonusScoreEntryListController extends Controller
|
class BonusScoreEntryListController extends Controller
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* Lists entries for a bonus score so the judge may select one to score.
|
|
||||||
*/
|
|
||||||
public function __invoke(Audition $audition)
|
public function __invoke(Audition $audition)
|
||||||
{
|
{
|
||||||
/** @var BonusScoreDefinition $bonusScore */
|
/** @var BonusScoreDefinition $bonusScore */
|
||||||
$bonusScore = $audition->bonusScore()->first();
|
$bonusScore = $audition->bonusScore()->first();
|
||||||
if (! $bonusScore->judges->contains(auth()->id())) {
|
if (! $bonusScore->judges->contains(auth()->id())) {
|
||||||
return redirect()->route('dashboard')->with('error', 'You are not assigned to judge that bonus score');
|
return redirect()->route('dashboard')->with('error', 'You are not assigned to judge this bonus score');
|
||||||
}
|
}
|
||||||
$entries = $audition->entries()->orderBy('draw_number')->get();
|
$entries = $audition->entries()->orderBy('draw_number')->get();
|
||||||
$entries = $entries->reject(fn ($entry) => $entry->hasFlag('no_show'));
|
$entries = $entries->reject(fn ($entry) => $entry->hasFlag('no_show'));
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
namespace App\Http\Controllers\Judging;
|
namespace App\Http\Controllers\Judging;
|
||||||
|
|
||||||
use App\Actions\Tabulation\EnterBonusScore;
|
use App\Actions\Tabulation\EnterBonusScore;
|
||||||
use App\Exceptions\AuditionAdminException;
|
use App\Exceptions\ScoreEntryException;
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Models\Entry;
|
use App\Models\Entry;
|
||||||
use Illuminate\Support\Facades\App;
|
use Illuminate\Support\Facades\App;
|
||||||
|
|
@ -14,17 +14,15 @@ class BonusScoreRecordController extends Controller
|
||||||
public function __invoke(Entry $entry)
|
public function __invoke(Entry $entry)
|
||||||
{
|
{
|
||||||
$enterBonusScore = App::make(EnterBonusScore::class);
|
$enterBonusScore = App::make(EnterBonusScore::class);
|
||||||
$max = $entry->audition->bonusScore()->first()->max_score;
|
|
||||||
$validData = request()->validate([
|
$validData = request()->validate([
|
||||||
'score' => 'required|integer|min:0|max:'.$max,
|
'score' => 'required|integer',
|
||||||
]);
|
]);
|
||||||
try {
|
try {
|
||||||
$enterBonusScore(Auth::user(), $entry, $validData['score']);
|
$enterBonusScore(Auth::user(), $entry, $validData['score']);
|
||||||
} catch (AuditionAdminException $ex) {
|
} catch (ScoreEntryException $ex) {
|
||||||
return redirect(route('dashboard'))->with('error', 'Score Entry Error - '.$ex->getMessage());
|
return redirect()->back()->with('error', 'Score Entry Error - '.$ex->getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect()->route('judging.bonusScore.EntryList', $entry->audition)->with('success',
|
return redirect()->route('judging.bonusScore.EntryList', $entry->audition)->with('Score Recorded Successfully');
|
||||||
'Score Recorded Successfully');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,19 +3,22 @@
|
||||||
namespace App\Http\Controllers\Judging;
|
namespace App\Http\Controllers\Judging;
|
||||||
|
|
||||||
use App\Actions\Tabulation\EnterScore;
|
use App\Actions\Tabulation\EnterScore;
|
||||||
use App\Exceptions\AuditionAdminException;
|
use App\Exceptions\AuditionServiceException;
|
||||||
|
use App\Exceptions\ScoreEntryException;
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Models\Audition;
|
use App\Models\Audition;
|
||||||
use App\Models\Entry;
|
use App\Models\Entry;
|
||||||
use App\Models\JudgeAdvancementVote;
|
use App\Models\JudgeAdvancementVote;
|
||||||
use App\Models\ScoreSheet;
|
use App\Models\ScoreSheet;
|
||||||
use App\Services\AuditionService;
|
use App\Services\AuditionService;
|
||||||
|
use Exception;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Illuminate\Support\Facades\Gate;
|
use Illuminate\Support\Facades\Gate;
|
||||||
|
|
||||||
use function compact;
|
use function compact;
|
||||||
use function redirect;
|
use function redirect;
|
||||||
|
use function url;
|
||||||
|
|
||||||
class JudgingController extends Controller
|
class JudgingController extends Controller
|
||||||
{
|
{
|
||||||
|
|
@ -28,25 +31,19 @@ class JudgingController extends Controller
|
||||||
|
|
||||||
public function index()
|
public function index()
|
||||||
{
|
{
|
||||||
$rooms = Auth::user()->judgingAssignments()->with('auditions')->with('prelimAuditions')->get();
|
$rooms = Auth::user()->judgingAssignments()->with('auditions')->get();
|
||||||
$bonusScoresToJudge = Auth::user()->bonusJudgingAssignments()->with('auditions')->get();
|
$bonusScoresToJudge = Auth::user()->bonusJudgingAssignments()->with('auditions')->get();
|
||||||
|
|
||||||
// $rooms->load('auditions');
|
//$rooms->load('auditions');
|
||||||
return view('judging.index', compact('rooms', 'bonusScoresToJudge'));
|
return view('judging.index', compact('rooms', 'bonusScoresToJudge'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function auditionEntryList(Request $request, Audition $audition)
|
public function auditionEntryList(Request $request, Audition $audition)
|
||||||
{
|
{
|
||||||
// TODO: Add error message if scoring guide is not set
|
|
||||||
if ($request->user()->cannot('judge', $audition)) {
|
if ($request->user()->cannot('judge', $audition)) {
|
||||||
return redirect()->route('judging.index')->with('error', 'You are not assigned to judge that audition');
|
return redirect()->route('judging.index')->with('error', 'You are not assigned to judge this audition');
|
||||||
}
|
}
|
||||||
$entries = Entry::where('audition_id', '=', $audition->id)->orderBy('draw_number')->with('audition')->get();
|
$entries = Entry::where('audition_id', '=', $audition->id)->orderBy('draw_number')->with('audition')->get();
|
||||||
|
|
||||||
// If there is a prelim audition, only show entries that have passed the prelim
|
|
||||||
if ($audition->prelimDefinition) {
|
|
||||||
$entries = $entries->reject(fn ($entry) => ! $entry->hasFlag('passed_prelim'));
|
|
||||||
}
|
|
||||||
$subscores = $audition->scoringGuide->subscores()->orderBy('display_order')->get();
|
$subscores = $audition->scoringGuide->subscores()->orderBy('display_order')->get();
|
||||||
|
|
||||||
$votes = JudgeAdvancementVote::where('user_id', Auth::id())->get();
|
$votes = JudgeAdvancementVote::where('user_id', Auth::id())->get();
|
||||||
|
|
@ -71,13 +68,6 @@ class JudgingController extends Controller
|
||||||
return redirect()->route('judging.auditionEntryList', $entry->audition)->with('error',
|
return redirect()->route('judging.auditionEntryList', $entry->audition)->with('error',
|
||||||
'The requested entry is marked as a no-show. Scores cannot be entered.');
|
'The requested entry is marked as a no-show. Scores cannot be entered.');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Turn away users if the entry is flagged as a failed-prelim
|
|
||||||
if ($entry->hasFlag('failed_prelim')) {
|
|
||||||
return redirect()->route('judging.auditionEntryList', $entry->audition)->with('error',
|
|
||||||
'The requested entry is marked as having failed a prelim. Scores cannot be entered.');
|
|
||||||
}
|
|
||||||
|
|
||||||
$oldSheet = ScoreSheet::where('user_id', Auth::id())->where('entry_id', $entry->id)->value('subscores') ?? null;
|
$oldSheet = ScoreSheet::where('user_id', Auth::id())->where('entry_id', $entry->id)->value('subscores') ?? null;
|
||||||
$oldVote = JudgeAdvancementVote::where('user_id', Auth::id())->where('entry_id', $entry->id)->first();
|
$oldVote = JudgeAdvancementVote::where('user_id', Auth::id())->where('entry_id', $entry->id)->first();
|
||||||
$oldVote = $oldVote ? $oldVote->vote : 'noVote';
|
$oldVote = $oldVote ? $oldVote->vote : 'noVote';
|
||||||
|
|
@ -88,11 +78,15 @@ class JudgingController extends Controller
|
||||||
public function saveScoreSheet(Request $request, Entry $entry, EnterScore $enterScore)
|
public function saveScoreSheet(Request $request, Entry $entry, EnterScore $enterScore)
|
||||||
{
|
{
|
||||||
if ($request->user()->cannot('judge', $entry->audition)) {
|
if ($request->user()->cannot('judge', $entry->audition)) {
|
||||||
return redirect()->route('judging.index')->with('error', 'You are not assigned to judge this entry');
|
abort(403, 'You are not assigned to judge this entry');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate form data
|
// Validate form data
|
||||||
$subscores = $entry->audition->subscoreDefinitions;
|
try {
|
||||||
|
$subscores = $this->auditionService->getSubscores($entry->audition, 'all');
|
||||||
|
} catch (AuditionServiceException $e) {
|
||||||
|
return redirect()->back()->with('error', 'Unable to get subscores - '.$e->getMessage());
|
||||||
|
}
|
||||||
$validationChecks = [];
|
$validationChecks = [];
|
||||||
foreach ($subscores as $subscore) {
|
foreach ($subscores as $subscore) {
|
||||||
$validationChecks['score'.'.'.$subscore->id] = 'required|integer|max:'.$subscore->max_score;
|
$validationChecks['score'.'.'.$subscore->id] = 'required|integer|max:'.$subscore->max_score;
|
||||||
|
|
@ -100,17 +94,16 @@ class JudgingController extends Controller
|
||||||
$validatedData = $request->validate($validationChecks);
|
$validatedData = $request->validate($validationChecks);
|
||||||
|
|
||||||
// Enter the score
|
// Enter the score
|
||||||
/** @noinspection PhpUnhandledExceptionInspection */
|
|
||||||
try {
|
try {
|
||||||
$enterScore(Auth::user(), $entry, $validatedData['score']);
|
$enterScore(Auth::user(), $entry, $validatedData['score']);
|
||||||
} catch (AuditionAdminException $e) {
|
} catch (ScoreEntryException $e) {
|
||||||
return redirect()->back()->with('error', $e->getMessage());
|
return redirect()->back()->with('error', 'Error saving score - '.$e->getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deal with an advancement vote if needed
|
// Deal with an advancement vote if needed
|
||||||
$this->advancementVote($request, $entry);
|
$this->advancementVote($request, $entry);
|
||||||
|
|
||||||
return redirect(route('judging.auditionEntryList', $entry->audition))->with('success',
|
return redirect('/judging/audition/'.$entry->audition_id)->with('success',
|
||||||
'Entered scores for '.$entry->audition->name.' '.$entry->draw_number);
|
'Entered scores for '.$entry->audition->name.' '.$entry->draw_number);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -118,10 +111,8 @@ class JudgingController extends Controller
|
||||||
public function updateScoreSheet(Request $request, Entry $entry, EnterScore $enterScore)
|
public function updateScoreSheet(Request $request, Entry $entry, EnterScore $enterScore)
|
||||||
{
|
{
|
||||||
if ($request->user()->cannot('judge', $entry->audition)) {
|
if ($request->user()->cannot('judge', $entry->audition)) {
|
||||||
return redirect()->route('judging.index')->with('error', 'You are not assigned to judge this entry');
|
abort(403, 'You are not assigned to judge this entry');
|
||||||
}
|
}
|
||||||
|
|
||||||
// We can't update a scoresheet that doesn't exist
|
|
||||||
$scoreSheet = ScoreSheet::where('user_id', Auth::id())->where('entry_id', $entry->id)->first();
|
$scoreSheet = ScoreSheet::where('user_id', Auth::id())->where('entry_id', $entry->id)->first();
|
||||||
if (! $scoreSheet) {
|
if (! $scoreSheet) {
|
||||||
return redirect()->back()->with('error', 'Attempt to edit non existent score sheet');
|
return redirect()->back()->with('error', 'Attempt to edit non existent score sheet');
|
||||||
|
|
@ -129,8 +120,11 @@ class JudgingController extends Controller
|
||||||
Gate::authorize('update', $scoreSheet);
|
Gate::authorize('update', $scoreSheet);
|
||||||
|
|
||||||
// Validate form data
|
// Validate form data
|
||||||
|
try {
|
||||||
$subscores = $entry->audition->subscoreDefinitions;
|
$subscores = $this->auditionService->getSubscores($entry->audition, 'all');
|
||||||
|
} catch (AuditionServiceException $e) {
|
||||||
|
return redirect()->back()->with('error', 'Error getting subscores - '.$e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
$validationChecks = [];
|
$validationChecks = [];
|
||||||
foreach ($subscores as $subscore) {
|
foreach ($subscores as $subscore) {
|
||||||
|
|
@ -139,29 +133,38 @@ class JudgingController extends Controller
|
||||||
$validatedData = $request->validate($validationChecks);
|
$validatedData = $request->validate($validationChecks);
|
||||||
|
|
||||||
// Enter the score
|
// Enter the score
|
||||||
|
try {
|
||||||
$enterScore(Auth::user(), $entry, $validatedData['score'], $scoreSheet);
|
$enterScore(Auth::user(), $entry, $validatedData['score'], $scoreSheet);
|
||||||
|
} catch (ScoreEntryException $e) {
|
||||||
|
return redirect()->back()->with('error', 'Error updating score - '.$e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
$this->advancementVote($request, $entry);
|
$this->advancementVote($request, $entry);
|
||||||
|
|
||||||
return redirect(route('judging.auditionEntryList', $entry->audition))->with('success',
|
return redirect('/judging/audition/'.$entry->audition_id)->with('success',
|
||||||
'Updated scores for '.$entry->audition->name.' '.$entry->draw_number);
|
'Updated scores for '.$entry->audition->name.' '.$entry->draw_number);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function advancementVote(Request $request, Entry $entry)
|
protected function advancementVote(Request $request, Entry $entry)
|
||||||
{
|
{
|
||||||
|
if ($request->user()->cannot('judge', $entry->audition)) {
|
||||||
|
abort(403, 'You are not assigned to judge this entry');
|
||||||
|
}
|
||||||
|
|
||||||
if ($entry->for_advancement and auditionSetting('advanceTo')) {
|
if ($entry->for_advancement and auditionSetting('advanceTo')) {
|
||||||
$request->validate([
|
$request->validate([
|
||||||
'advancement-vote' => ['required', 'in:yes,no,dq'],
|
'advancement-vote' => ['required', 'in:yes,no,dq'],
|
||||||
]);
|
]);
|
||||||
|
try {
|
||||||
JudgeAdvancementVote::where('user_id', Auth::id())->where('entry_id', $entry->id)->delete();
|
JudgeAdvancementVote::where('user_id', Auth::id())->where('entry_id', $entry->id)->delete();
|
||||||
JudgeAdvancementVote::create([
|
JudgeAdvancementVote::create([
|
||||||
'user_id' => Auth::user()->id,
|
'user_id' => Auth::user()->id,
|
||||||
'entry_id' => $entry->id,
|
'entry_id' => $entry->id,
|
||||||
'vote' => $request->input('advancement-vote'),
|
'vote' => $request->input('advancement-vote'),
|
||||||
]);
|
]);
|
||||||
|
} catch (Exception) {
|
||||||
|
return redirect(url()->previous())->with('error', 'Error saving advancement vote');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
|
||||||
|
|
@ -1,111 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Controllers\Judging;
|
|
||||||
|
|
||||||
use App\Actions\Tabulation\EnterPrelimScore;
|
|
||||||
use App\Exceptions\AuditionAdminException;
|
|
||||||
use App\Http\Controllers\Controller;
|
|
||||||
use App\Models\Entry;
|
|
||||||
use App\Models\PrelimDefinition;
|
|
||||||
use App\Models\PrelimScoreSheet;
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
use Illuminate\Support\Facades\Auth;
|
|
||||||
|
|
||||||
class PrelimJudgingController extends Controller
|
|
||||||
{
|
|
||||||
public function prelimEntryList(PrelimDefinition $prelimDefinition)
|
|
||||||
{
|
|
||||||
if (auth()->user()->cannot('judge', $prelimDefinition)) {
|
|
||||||
return redirect()->route('dashboard')->with('error', 'You are not assigned to judge that prelim audition.');
|
|
||||||
}
|
|
||||||
$entries = $prelimDefinition->audition->entries;
|
|
||||||
$subscores = $prelimDefinition->scoringGuide->subscores()->orderBy('display_order')->get();
|
|
||||||
$published = $prelimDefinition->audition->hasFlag('seats_published');
|
|
||||||
|
|
||||||
$prelimScoresheets = PrelimScoreSheet::where('user_id', Auth::id())->get()->keyBy('entry_id');
|
|
||||||
|
|
||||||
return view('judging.prelim_entry_list',
|
|
||||||
compact('prelimDefinition', 'entries', 'subscores', 'published', 'prelimScoresheets'));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function prelimScoreEntryForm(Entry $entry)
|
|
||||||
{
|
|
||||||
if (auth()->user()->cannot('judge', $entry->audition->prelimDefinition)) {
|
|
||||||
return redirect()->route('dashboard')->with('error', 'You are not assigned to judge that prelim audition.');
|
|
||||||
}
|
|
||||||
if ($entry->audition->hasFlag('seats_published')) {
|
|
||||||
return redirect()->route('dashboard')->with('error',
|
|
||||||
'Scores for entries in published auditions cannot be modified.');
|
|
||||||
}
|
|
||||||
if ($entry->hasFlag('no_show')) {
|
|
||||||
return redirect()->route('judging.prelimEntryList', $entry->audition->prelimDefinition)->with('error',
|
|
||||||
'The requested entry is marked as a no-show. Scores cannot be entered.');
|
|
||||||
}
|
|
||||||
|
|
||||||
$oldSheet = PrelimScoreSheet::where('user_id', Auth::id())->where('entry_id',
|
|
||||||
$entry->id)->value('subscores') ?? null;
|
|
||||||
|
|
||||||
if ($oldSheet) {
|
|
||||||
$formRoute = 'update.savePrelimScoreSheet';
|
|
||||||
$formMethod = 'PATCH';
|
|
||||||
} else {
|
|
||||||
$formRoute = 'judging.savePrelimScoreSheet';
|
|
||||||
$formMethod = 'POST';
|
|
||||||
}
|
|
||||||
|
|
||||||
return view('judging.prelim_entry_form', compact('entry', 'oldSheet', 'formRoute', 'formMethod'));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @throws AuditionAdminException
|
|
||||||
*/
|
|
||||||
public function savePrelimScoreSheet(Entry $entry, Request $request, EnterPrelimScore $scribe)
|
|
||||||
{
|
|
||||||
if (auth()->user()->cannot('judge', $entry->audition->prelimDefinition)) {
|
|
||||||
return redirect()->route('dashboard')->with('error', 'You are not assigned to judge that prelim audition.');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate form data
|
|
||||||
$subscores = $entry->audition->prelimDefinition->scoringGuide->subscores;
|
|
||||||
$validationChecks = [];
|
|
||||||
foreach ($subscores as $subscore) {
|
|
||||||
$validationChecks['score'.'.'.$subscore->id] = 'required|integer|max:'.$subscore->max_score;
|
|
||||||
}
|
|
||||||
$validatedData = $request->validate($validationChecks);
|
|
||||||
|
|
||||||
// Enter the score
|
|
||||||
$scribe(auth()->user(), $entry, $validatedData['score']);
|
|
||||||
|
|
||||||
return redirect()->route('judging.prelimEntryList', $entry->audition->prelimDefinition)->with('success',
|
|
||||||
'Entered prelim scores for '.$entry->audition->name.' '.$entry->draw_number);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function updatePrelimScoreSheet(Entry $entry, Request $request, EnterPrelimScore $scribe)
|
|
||||||
{
|
|
||||||
if (auth()->user()->cannot('judge', $entry->audition->prelimDefinition)) {
|
|
||||||
return redirect()->route('dashboard')->with('error', 'You are not assigned to judge that prelim audition.');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate form data
|
|
||||||
$subscores = $entry->audition->prelimDefinition->scoringGuide->subscores;
|
|
||||||
$validationChecks = [];
|
|
||||||
foreach ($subscores as $subscore) {
|
|
||||||
$validationChecks['score'.'.'.$subscore->id] = 'required|integer|max:'.$subscore->max_score;
|
|
||||||
}
|
|
||||||
$validatedData = $request->validate($validationChecks);
|
|
||||||
|
|
||||||
// Get the existing score
|
|
||||||
$scoreSheet = PrelimScoreSheet::where('user_id', auth()->user()->id)->where('entry_id', $entry->id)->first();
|
|
||||||
|
|
||||||
if (! $scoreSheet) {
|
|
||||||
return redirect()->back()->with('error', 'No score sheet exists.');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the score
|
|
||||||
$scribe(auth()->user(), $entry, $validatedData['score'], $scoreSheet);
|
|
||||||
|
|
||||||
return redirect()->route('judging.prelimEntryList', $entry->audition->prelimDefinition)->with('success',
|
|
||||||
'Updated prelim scores for '.$entry->audition->name.' '.$entry->draw_number);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -2,56 +2,97 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App\Models\Audition;
|
|
||||||
use App\Models\Entry;
|
use App\Models\Entry;
|
||||||
|
|
||||||
use function compact;
|
|
||||||
|
|
||||||
class MonitorController extends Controller
|
class MonitorController extends Controller
|
||||||
{
|
{
|
||||||
public function index()
|
public function index()
|
||||||
{
|
{
|
||||||
if (! auth()->user()->hasFlag('monitor')) {
|
if (! auth()->user()->hasFlag('monitor')) {
|
||||||
abort(403);
|
return redirect()->route('dashboard')->with('error', 'You are not assigned as a monitor');
|
||||||
}
|
}
|
||||||
|
$method = 'GET';
|
||||||
|
$formRoute = 'monitor.enterFlag';
|
||||||
|
$title = 'Flag Entry';
|
||||||
|
|
||||||
$auditions = Audition::orderBy('score_order')->with('flags')->get();
|
return view('tabulation.choose_entry', compact('method', 'formRoute', 'title'));
|
||||||
$audition = null;
|
|
||||||
|
|
||||||
return view('monitor.index', compact('audition', 'auditions'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function auditionStatus(Audition $audition)
|
public function flagForm()
|
||||||
{
|
{
|
||||||
if (! auth()->user()->hasFlag('monitor')) {
|
if (! auth()->user()->hasFlag('monitor')) {
|
||||||
abort(403);
|
return redirect()->route('dashboard')->with('error', 'You are not assigned as a monitor');
|
||||||
|
}
|
||||||
|
$validData = request()->validate([
|
||||||
|
'entry_id' => ['required', 'integer', 'exists:entries,id'],
|
||||||
|
]);
|
||||||
|
$entry = Entry::find($validData['entry_id']);
|
||||||
|
|
||||||
|
// If the entries audition is published, bounce out
|
||||||
|
if ($entry->audition->hasFlag('seats_published') || $entry->audition->hasFlag('advance_published')) {
|
||||||
|
return redirect()->route('monitor.index')->with('error', 'Cannot set flags while results are published');
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($audition->hasFlag('seats_published') || $audition->hasFlag('advancement_published')) {
|
// If entry has scores, bounce on out
|
||||||
return redirect()->route('monitor.index')->with('error', 'Results for that audition are published');
|
if ($entry->scoreSheets()->count() > 0) {
|
||||||
|
return redirect()->route('monitor.index')->with('error', 'That entry has existing scores');
|
||||||
}
|
}
|
||||||
|
|
||||||
$auditions = Audition::orderBy('score_order')->with('flags')->get();
|
return view('monitor_entry_flag_form', compact('entry'));
|
||||||
$entries = $audition->entries()->with('flags')->with('student.school')->withCount([
|
|
||||||
'prelimScoreSheets', 'scoreSheets',
|
|
||||||
])->orderBy('draw_number')->get();
|
|
||||||
|
|
||||||
return view('monitor.index', compact('audition', 'auditions', 'entries'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function toggleNoShow(Entry $entry)
|
public function storeFlag(Entry $entry)
|
||||||
{
|
{
|
||||||
if ($entry->audition->hasFlag('seats_published') || $entry->audition->hasFlag('advancement_published')) {
|
if (! auth()->user()->hasFlag('monitor')) {
|
||||||
return redirect()->route('monitor.index')->with('error', 'Results for that audition are published');
|
return redirect()->route('dashboard')->with('error', 'You are not assigned as a monitor');
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($entry->hasFlag('no_show')) {
|
// If the entries audition is published, bounce out
|
||||||
|
if ($entry->audition->hasFlag('seats_published') || $entry->audition->hasFlag('advance_published')) {
|
||||||
|
return redirect()->route('monitor.index')->with('error', 'Cannot set flags while results are published');
|
||||||
|
}
|
||||||
|
|
||||||
|
// If entry has scores, bounce on out
|
||||||
|
if ($entry->scoreSheets()->count() > 0) {
|
||||||
|
return redirect()->route('monitor.index')->with('error', 'That entry has existing scores');
|
||||||
|
}
|
||||||
|
|
||||||
|
$action = request()->input('action');
|
||||||
|
$result = match ($action) {
|
||||||
|
'failed-prelim' => $this->setFlag($entry, 'failed_prelim'),
|
||||||
|
'no-show' => $this->setFlag($entry, 'no_show'),
|
||||||
|
'clear' => $this->setFlag($entry, 'clear'),
|
||||||
|
default => redirect()->route('monitor.index')->with('error', 'Invalid action requested'),
|
||||||
|
};
|
||||||
|
if (! $result) {
|
||||||
|
return redirect()->route('monitor.index')->with('error', 'Failed to set flag');
|
||||||
|
}
|
||||||
|
|
||||||
|
return redirect()->route('monitor.index')->with('success', 'Flag set for entry #'.$entry->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function setFlag(Entry $entry, string $flag)
|
||||||
|
{
|
||||||
|
if ($flag === 'no_show') {
|
||||||
|
$entry->removeFlag('failed_prelim');
|
||||||
|
$entry->addFlag('no_show');
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if ($flag === 'failed_prelim') {
|
||||||
|
$entry->addFlag('failed_prelim');
|
||||||
|
$entry->addFlag('no_show');
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if ($flag === 'clear') {
|
||||||
|
$entry->removeFlag('failed_prelim');
|
||||||
$entry->removeFlag('no_show');
|
$entry->removeFlag('no_show');
|
||||||
|
|
||||||
return redirect()->back()->with('success', 'No Show Flag Cleared');
|
return true;
|
||||||
}
|
}
|
||||||
$entry->addFlag('no_show');
|
|
||||||
|
|
||||||
return redirect()->back()->with('success', 'No Show Entered');
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,6 @@ use App\Http\Controllers\Controller;
|
||||||
use App\Models\NominationEnsemble;
|
use App\Models\NominationEnsemble;
|
||||||
use App\Models\NominationEnsembleEntry;
|
use App\Models\NominationEnsembleEntry;
|
||||||
use App\Models\School;
|
use App\Models\School;
|
||||||
use App\Models\Student;
|
|
||||||
use Illuminate\Validation\Rule;
|
|
||||||
|
|
||||||
use function redirect;
|
|
||||||
|
|
||||||
class MeobdaNominationAdminController extends Controller implements NominationAdminController
|
class MeobdaNominationAdminController extends Controller implements NominationAdminController
|
||||||
{
|
{
|
||||||
|
|
@ -72,178 +68,46 @@ class MeobdaNominationAdminController extends Controller implements NominationAd
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($filterData['split'] ?? false) {
|
if ($filterData['split'] ?? false) {
|
||||||
if ($filterData['split'] == 'NO-SPLIT-ASSIGNED') {
|
$splitFilter = explode('---', $filterData['split']);
|
||||||
$nominations = $nominations->whereNull('data->split');
|
$nominations = $nominations->where('nomination_ensemble_id', $splitFilter[0]);
|
||||||
} else {
|
if ($splitFilter[1] != 'all') {
|
||||||
$splitFilter = explode('---', $filterData['split']);
|
$nominations = $nominations->where('data->split', $splitFilter[1]);
|
||||||
$nominations = $nominations->where('nomination_ensemble_id', $splitFilter[0]);
|
|
||||||
if ($splitFilter[1] != 'all') {
|
|
||||||
$nominations = $nominations->where('data->split', $splitFilter[1]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort
|
|
||||||
$nominations = $nominations->orderBy('id', 'desc');
|
|
||||||
|
|
||||||
$nominations = $nominations->paginate(50);
|
$nominations = $nominations->paginate(50);
|
||||||
|
|
||||||
return view('nomination_ensembles.meobda.admin.nominations',
|
return view('nomination_ensembles.meobda.admin.nominations',
|
||||||
compact('nominations', 'schools', 'filterData', 'ensembles', 'sections', 'splits'));
|
compact('nominations', 'schools', 'filterData', 'ensembles', 'sections', 'splits'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function show(NominationEnsembleEntry $nominationEnsembleEntry)
|
public function show(NominationEnsembleEntry $entry)
|
||||||
{
|
{
|
||||||
// TODO: Implement show() method.
|
// TODO: Implement show() method.
|
||||||
}
|
}
|
||||||
|
|
||||||
public function create()
|
public function create()
|
||||||
{
|
{
|
||||||
$target_ensemble = null;
|
// TODO: Implement create() method.
|
||||||
$instrumentation = null;
|
|
||||||
$students = null;
|
|
||||||
if (request()->get('ensemble')) {
|
|
||||||
$validData = request()->validate([
|
|
||||||
'ensemble' => 'nullable|exists:nomination_ensembles,id',
|
|
||||||
]);
|
|
||||||
$target_ensemble = NominationEnsemble::find($validData['ensemble']);
|
|
||||||
|
|
||||||
// Get viable students for entering
|
|
||||||
$students = Student::where('grade', '<=', $target_ensemble->maximum_grade)
|
|
||||||
->where('grade', '>=', $target_ensemble->minimum_grade)
|
|
||||||
->with('school')
|
|
||||||
->join('schools', 'schools.id', '=', 'students.school_id')
|
|
||||||
->orderBy('schools.name', 'asc')
|
|
||||||
->orderBy('students.last_name', 'asc')
|
|
||||||
->orderBy('students.first_name', 'asc')
|
|
||||||
->get(['students.*']);
|
|
||||||
|
|
||||||
// Remove students already nominated
|
|
||||||
$nominated_student_ids = NominationEnsembleEntry::where('nomination_ensemble_id',
|
|
||||||
$target_ensemble->id)->pluck('student_id')->all();
|
|
||||||
|
|
||||||
$students = $students->reject(function ($student) use ($nominated_student_ids) {
|
|
||||||
return in_array($student->id, $nominated_student_ids);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Get current instrumentation of target ensemble
|
|
||||||
$instrumentation = $this->get_ensemble_instrumentation($target_ensemble);
|
|
||||||
}
|
|
||||||
$ensembles = NominationEnsemble::all();
|
|
||||||
|
|
||||||
return view('nomination_ensembles.meobda.admin.nomination-create',
|
|
||||||
compact('ensembles', 'target_ensemble', 'students', 'instrumentation'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function store()
|
public function store()
|
||||||
{
|
{
|
||||||
// Initial Validation
|
// TODO: Implement store() method.
|
||||||
$validData = request()->validate([
|
|
||||||
'ensemble' => 'required|exists:nomination_ensembles,id',
|
|
||||||
'student' => 'required|exists:students,id',
|
|
||||||
'instrument' => 'required|string',
|
|
||||||
'split' => 'nullable|string',
|
|
||||||
'seat' => 'nullable|integer',
|
|
||||||
]);
|
|
||||||
$proposed_ensemble = NominationEnsemble::find($validData['ensemble']);
|
|
||||||
|
|
||||||
// Check if $validData['instrument'] is a valid instrument for the proposed ensemble
|
|
||||||
$validInstruments = array_column($proposed_ensemble->data['instruments'], 'name');
|
|
||||||
if (! in_array($validData['instrument'], $validInstruments)) {
|
|
||||||
return redirect()->back()->with('error', 'Invalid Instrument Specified');
|
|
||||||
}
|
|
||||||
|
|
||||||
$data['instrument'] = $validData['instrument'];
|
|
||||||
if ($validData['seat'] > 0) {
|
|
||||||
$data['seat'] = $validData['seat'];
|
|
||||||
}
|
|
||||||
if ($validData['split'] != '---') {
|
|
||||||
$data['split'] = $validData['split'];
|
|
||||||
// Check if $validData['split'] is a valid split for the proposed ensemble
|
|
||||||
$validSplits = $proposed_ensemble->data['split_names'];
|
|
||||||
if (! in_array($validData['split'], $validSplits)) {
|
|
||||||
return redirect()->back()->with('error', 'Invalid Split Specified');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$newNomination = NominationEnsembleEntry::make([
|
|
||||||
'student_id' => $validData['student'],
|
|
||||||
'nomination_ensemble_id' => $validData['ensemble'],
|
|
||||||
'data' => $data,
|
|
||||||
]);
|
|
||||||
$newNomination->save();
|
|
||||||
|
|
||||||
return redirect()->route('nomination.admin.index')->with('success', 'New Nomination created');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function edit(NominationEnsembleEntry $nominationEnsembleEntry)
|
public function edit(NominationEnsembleEntry $entry)
|
||||||
{
|
{
|
||||||
$students = Student::with('school')->get()
|
// TODO: Implement edit() method.
|
||||||
->sortBy('school.name');
|
|
||||||
|
|
||||||
if (! isset($nominationEnsembleEntry->data['seat'])) {
|
|
||||||
$data = $nominationEnsembleEntry->data;
|
|
||||||
$data['seat'] = null;
|
|
||||||
$nominationEnsembleEntry->data = $data;
|
|
||||||
}
|
|
||||||
|
|
||||||
$instrumentation = $this->get_ensemble_instrumentation($nominationEnsembleEntry->ensemble);
|
|
||||||
|
|
||||||
return view('nomination_ensembles.meobda.admin.nomination-edit',
|
|
||||||
compact('nominationEnsembleEntry', 'students', 'instrumentation'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function update(NominationEnsembleEntry $nominationEnsembleEntry)
|
public function update(NominationEnsembleEntry $entry)
|
||||||
{
|
{
|
||||||
$ensemble = $nominationEnsembleEntry->ensemble;
|
// TODO: Implement update() method.
|
||||||
$validSplits = $ensemble->data['split_names'];
|
|
||||||
$validInstruments = [];
|
|
||||||
foreach ($ensemble->data['instruments'] as $instrument) {
|
|
||||||
$validInstruments[] = $instrument['name'];
|
|
||||||
}
|
|
||||||
$validData = request()->validate([
|
|
||||||
'instrument' => ['nullable', Rule::in($validInstruments)],
|
|
||||||
'split' => ['nullable', Rule::in($validSplits)],
|
|
||||||
'seat' => ['nullable', 'integer'],
|
|
||||||
]);
|
|
||||||
$data = $nominationEnsembleEntry->data;
|
|
||||||
$data['instrument'] = $validData['instrument'];
|
|
||||||
$data['split'] = $validData['split'];
|
|
||||||
$data['seat'] = $validData['seat'];
|
|
||||||
$nominationEnsembleEntry->update([
|
|
||||||
'data' => $data,
|
|
||||||
]);
|
|
||||||
|
|
||||||
return redirect()->route('nomination.admin.index')->with('success', 'Nomination updated');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function destroy(NominationEnsembleEntry $nominationEnsembleEntry)
|
public function destroy(NominationEnsembleEntry $entry)
|
||||||
{
|
{
|
||||||
// TODO: Implement destroy() method.
|
// TODO: Implement destroy() method.
|
||||||
}
|
}
|
||||||
|
|
||||||
private function get_ensemble_instrumentation(NominationEnsemble $ensemble)
|
|
||||||
{
|
|
||||||
$entries = NominationEnsembleEntry::where('nomination_ensemble_id', $ensemble->id)->get();
|
|
||||||
$splits = $ensemble->data['split_names'];
|
|
||||||
$instruments = [];
|
|
||||||
foreach ($ensemble->data['instruments'] as $instrument) {
|
|
||||||
$instruments[] = $instrument['name'];
|
|
||||||
}
|
|
||||||
$counts = [];
|
|
||||||
foreach ($splits as $split) {
|
|
||||||
$counts[$split] = [];
|
|
||||||
foreach ($instruments as $instrument) {
|
|
||||||
$counts[$split][$instrument] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($entries as $entry) {
|
|
||||||
if (! isset($entry->data['split'])) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$counts[$entry->data['split']][$entry->data['instrument']] += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $counts;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,81 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Controllers\NominationEnsembles;
|
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
|
||||||
use App\Models\NominationEnsembleEntry;
|
|
||||||
use Codedge\Fpdf\Fpdf\Fpdf;
|
|
||||||
|
|
||||||
class MeobdaNominationAdminUtilitiesController extends Controller implements NominationAdminUtilitiesController
|
|
||||||
{
|
|
||||||
public function __invoke(string $action)
|
|
||||||
{
|
|
||||||
match ($action) {
|
|
||||||
'name_tags' => $this->printNameTags(),
|
|
||||||
default => $this->invalidAction(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private function printNameTags()
|
|
||||||
{
|
|
||||||
$pdf = new Fpdf('P', 'in', 'letter');
|
|
||||||
$pdf->SetFont('Arial', 'B', 36);
|
|
||||||
|
|
||||||
$nominations = NominationEnsembleEntry::with('ensemble')
|
|
||||||
->with('student.school')
|
|
||||||
->orderBy('nomination_ensemble_id')
|
|
||||||
->orderBy('data->split')
|
|
||||||
->orderBy('data->instrument')
|
|
||||||
->orderByRaw('CAST(data->"$.seat" AS UNSIGNED)')
|
|
||||||
->get();
|
|
||||||
|
|
||||||
// echo '<table><tr><th>Ensemble</th><th>Split</th><th>Instrument</th><th>Seat</th><th>Name</th><th>School</th></tr>';
|
|
||||||
// foreach ($nominations as $nomination) {
|
|
||||||
// if (! isset($nomination->data['split'])) {
|
|
||||||
// $temp = $nomination->data;
|
|
||||||
// $temp['split'] = '---';
|
|
||||||
// $nomination->data = $temp;
|
|
||||||
// }
|
|
||||||
// if (! isset($nomination->data['seat'])) {
|
|
||||||
// $temp = $nomination->data;
|
|
||||||
// $temp['seat'] = '---';
|
|
||||||
// $nomination->data = $temp;
|
|
||||||
// }
|
|
||||||
// echo '<tr>';
|
|
||||||
// echo '<td>'.$nomination->ensemble->name.'</td>';
|
|
||||||
// echo '<td>'.$nomination->data['split'] ?? 'none'.'</td>';
|
|
||||||
// echo '<td>'.$nomination->data['instrument'].'</td>';
|
|
||||||
// echo '<td>'.$nomination->data['seat'].'</td>';
|
|
||||||
// echo '<td>'.$nomination->student->full_name().'</td>';
|
|
||||||
// echo '<td>'.$nomination->student->school->name.'</td>';
|
|
||||||
// echo '</tr>';
|
|
||||||
// }
|
|
||||||
// echo '</table>';
|
|
||||||
|
|
||||||
foreach ($nominations as $nomination) {
|
|
||||||
if (! isset($nomination->data['split'])) {
|
|
||||||
$temp = $nomination->data;
|
|
||||||
$temp['split'] = '---';
|
|
||||||
$nomination->data = $temp;
|
|
||||||
}
|
|
||||||
if (! isset($nomination->data['seat'])) {
|
|
||||||
$temp = $nomination->data;
|
|
||||||
$temp['seat'] = '---';
|
|
||||||
$nomination->data = $temp;
|
|
||||||
}
|
|
||||||
$pdf->AddPage();
|
|
||||||
$pdf->SetY('6');
|
|
||||||
$pdf->Cell(0, .8, $nomination->student->full_name(), 0, 1, 'C');
|
|
||||||
$pdf->Cell(0, .8, $nomination->student->school->name, 0, 1, 'C');
|
|
||||||
$pdf->Cell(0, .8, $nomination->data['split'], 0, 1, 'C');
|
|
||||||
$pdf->Cell(0, .8, $nomination->data['instrument'].' - '.$nomination->data['seat'], 0, 1, 'C');
|
|
||||||
|
|
||||||
}
|
|
||||||
$pdf->Output('D', 'StandNameTags.pdf');
|
|
||||||
}
|
|
||||||
|
|
||||||
private function invalidAction()
|
|
||||||
{
|
|
||||||
return redirect()->back()->with('error', 'Invalid Action');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,56 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Controllers\NominationEnsembles;
|
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
|
||||||
use App\Models\NominationEnsembleEntry;
|
|
||||||
use Illuminate\Support\Facades\Response;
|
|
||||||
|
|
||||||
class MeobdaNominationExportController extends Controller implements NominationExportController
|
|
||||||
{
|
|
||||||
public function __invoke()
|
|
||||||
{
|
|
||||||
$data = $this->getData();
|
|
||||||
// Create a callback to write the CSV data
|
|
||||||
$callback = function () use ($data) {
|
|
||||||
$file = fopen('php://output', 'w');
|
|
||||||
|
|
||||||
foreach ($data as $line) {
|
|
||||||
// Convert the string into an array
|
|
||||||
$fields = explode(',', $line);
|
|
||||||
// Write the array to the CSV file
|
|
||||||
fputcsv($file, $fields);
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose($file);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Return a response with the CSV content
|
|
||||||
return Response::stream($callback, 200, [
|
|
||||||
'Content-Type' => 'text/csv',
|
|
||||||
'Content-Disposition' => 'attachment; filename="audition_entries_export.csv"',
|
|
||||||
]);
|
|
||||||
dd($this->getData());
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getData()
|
|
||||||
{
|
|
||||||
// Room, Audition, Draw Number, Name, School
|
|
||||||
$exportRows = [
|
|
||||||
'Ensemble,Split,Instrument,Seat,First Name, Last Name,School',
|
|
||||||
];
|
|
||||||
$nominations = NominationEnsembleEntry::with('ensemble')->with('student.school')->get();
|
|
||||||
foreach ($nominations as $nomination) {
|
|
||||||
$ensemble = $nomination->ensemble->name;
|
|
||||||
$split = $nomination->data['split'];
|
|
||||||
$instrument = $nomination->data['instrument'];
|
|
||||||
$seat = $nomination->data['seat'];
|
|
||||||
$firstName = $nomination->student->first_name;
|
|
||||||
$lastName = $nomination->student->last_name;
|
|
||||||
$schoolName = $nomination->student->school->name;
|
|
||||||
$exportRows[] = "$ensemble, $split, $instrument, $seat, $firstName, $lastName, $schoolName";
|
|
||||||
}
|
|
||||||
|
|
||||||
return $exportRows;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -8,15 +8,15 @@ interface NominationAdminController
|
||||||
{
|
{
|
||||||
public function index();
|
public function index();
|
||||||
|
|
||||||
public function show(NominationEnsembleEntry $nominationEnsembleEntry);
|
public function show(NominationEnsembleEntry $entry);
|
||||||
|
|
||||||
public function create();
|
public function create();
|
||||||
|
|
||||||
public function store();
|
public function store();
|
||||||
|
|
||||||
public function edit(NominationEnsembleEntry $nominationEnsembleEntry);
|
public function edit(NominationEnsembleEntry $entry);
|
||||||
|
|
||||||
public function update(NominationEnsembleEntry $nominationEnsembleEntry);
|
public function update(NominationEnsembleEntry $entry);
|
||||||
|
|
||||||
public function destroy(NominationEnsembleEntry $nominationEnsembleEntry);
|
public function destroy(NominationEnsembleEntry $entry);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Controllers\NominationEnsembles;
|
|
||||||
|
|
||||||
interface NominationAdminUtilitiesController
|
|
||||||
{
|
|
||||||
public function __invoke(string $action);
|
|
||||||
}
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Controllers\NominationEnsembles;
|
|
||||||
|
|
||||||
interface NominationExportController
|
|
||||||
{
|
|
||||||
public function __invoke();
|
|
||||||
}
|
|
||||||
|
|
@ -14,7 +14,7 @@ class ScobdaNominationAdminController extends Controller implements NominationAd
|
||||||
return view('nomination_ensembles.scobda.admin.index', compact('nominations'));
|
return view('nomination_ensembles.scobda.admin.index', compact('nominations'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function show(NominationEnsembleEntry $nominationEnsembleEntry)
|
public function show(NominationEnsembleEntry $entry)
|
||||||
{
|
{
|
||||||
// TODO: Implement show() method.
|
// TODO: Implement show() method.
|
||||||
}
|
}
|
||||||
|
|
@ -29,17 +29,17 @@ class ScobdaNominationAdminController extends Controller implements NominationAd
|
||||||
// TODO: Implement store() method.
|
// TODO: Implement store() method.
|
||||||
}
|
}
|
||||||
|
|
||||||
public function edit(NominationEnsembleEntry $nominationEnsembleEntry)
|
public function edit(NominationEnsembleEntry $entry)
|
||||||
{
|
{
|
||||||
// TODO: Implement edit() method.
|
// TODO: Implement edit() method.
|
||||||
}
|
}
|
||||||
|
|
||||||
public function update(NominationEnsembleEntry $nominationEnsembleEntry)
|
public function update(NominationEnsembleEntry $entry)
|
||||||
{
|
{
|
||||||
// TODO: Implement update() method.
|
// TODO: Implement update() method.
|
||||||
}
|
}
|
||||||
|
|
||||||
public function destroy(NominationEnsembleEntry $nominationEnsembleEntry)
|
public function destroy(NominationEnsembleEntry $entry)
|
||||||
{
|
{
|
||||||
// TODO: Implement destroy() method.
|
// TODO: Implement destroy() method.
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,66 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Controllers\NominationEnsembles;
|
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
|
||||||
use App\Models\School;
|
|
||||||
use Codedge\Fpdf\Fpdf\Fpdf;
|
|
||||||
|
|
||||||
class ScobdaNominationAdminUtilitiesController extends Controller implements NominationAdminUtilitiesController
|
|
||||||
{
|
|
||||||
public function __invoke(string $action)
|
|
||||||
{
|
|
||||||
match ($action) {
|
|
||||||
'school_shirt_distribution_report' => $this->printShirtDistributionLists(),
|
|
||||||
default => $this->invalidAction(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private function printShirtDistributionLists()
|
|
||||||
{
|
|
||||||
$output = '';
|
|
||||||
$schools = School::with('nominations.student')->orderBy('name')->get();
|
|
||||||
|
|
||||||
$pdf = new Fpdf('P', 'in', 'letter');
|
|
||||||
|
|
||||||
foreach ($schools as $school) {
|
|
||||||
if ($school->nominations->count() < 1) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$pdf->AddPage();
|
|
||||||
$pdf->SetFont('Arial', 'B', 16);
|
|
||||||
$pdf->Cell(0, .3, $school->name, 1, 1, 'L');
|
|
||||||
$director_text = 'Directors: ';
|
|
||||||
$first_director = true;
|
|
||||||
foreach ($school->users as $user) {
|
|
||||||
if (! $first_director) {
|
|
||||||
$director_text .= ', ';
|
|
||||||
}
|
|
||||||
$director_text .= $user->full_name();
|
|
||||||
$first_director = false;
|
|
||||||
}
|
|
||||||
$pdf->SetFont('Arial', 'B', 12);
|
|
||||||
$pdf->MultiCell(0, .3, $director_text, 0, 'L', 0);
|
|
||||||
$pdf->SetFont('Arial', '', 10);
|
|
||||||
$nominations = $school->nominations;
|
|
||||||
$nominations = $nominations->sortBy(function ($entry) {
|
|
||||||
return $entry->student->full_name(true);
|
|
||||||
});
|
|
||||||
foreach ($nominations as $nomination) {
|
|
||||||
$accepted = $nomination->data['accepted'] ?? false;
|
|
||||||
if (! $accepted) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$text = $nomination->student->full_name().' - ';
|
|
||||||
if ($nomination->student->optional_data && array_key_exists('shirt_size',
|
|
||||||
$nomination->student->optional_data)) {
|
|
||||||
$text .= $nomination->student->optional_data['shirt_size'];
|
|
||||||
} else {
|
|
||||||
$text .= 'No size provided';
|
|
||||||
}
|
|
||||||
$pdf->Cell(0, .25, $text, 0, 1, 'L');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$pdf->Output('D', 'ShirtRosters.pdf');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Controllers\NominationEnsembles;
|
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
|
||||||
|
|
||||||
class ScobdaNominationExportController extends Controller implements NominationExportController
|
|
||||||
{
|
|
||||||
public function __invoke()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue