Compare commits
No commits in common. "master" and "bonusScoreRewrite" have entirely different histories.
master
...
bonusScore
|
|
@ -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,10 +1,10 @@
|
||||||
<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>
|
||||||
|
|
@ -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,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -4,6 +4,7 @@ namespace App\Actions\Tabulation;
|
||||||
|
|
||||||
use App\Models\Audition;
|
use App\Models\Audition;
|
||||||
use App\Models\Entry;
|
use App\Models\Entry;
|
||||||
|
use Debugbar;
|
||||||
|
|
||||||
class CalculateAuditionScores
|
class CalculateAuditionScores
|
||||||
{
|
{
|
||||||
|
|
@ -22,6 +23,7 @@ class CalculateAuditionScores
|
||||||
->with('audition.scoringGuide.subscores')
|
->with('audition.scoringGuide.subscores')
|
||||||
->get();
|
->get();
|
||||||
foreach ($pending_entries as $entry) {
|
foreach ($pending_entries as $entry) {
|
||||||
|
Debugbar::debug('Calculating scores for entry: '.$entry->id);
|
||||||
$totaler->__invoke($entry);
|
$totaler->__invoke($entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,21 +4,24 @@
|
||||||
|
|
||||||
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\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);
|
||||||
|
|
||||||
|
|
@ -35,18 +38,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');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,6 @@ use App\Models\EntryTotalScore;
|
||||||
use App\Models\ScoreSheet;
|
use App\Models\ScoreSheet;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
use function auditionLog;
|
|
||||||
|
|
||||||
class EnterNoShow
|
class EnterNoShow
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
|
@ -53,13 +51,6 @@ class EnterNoShow
|
||||||
$msg = 'No Show has been entered for '.$entry->audition->name.' #'.$entry->draw_number.' (ID: '.$entry->id.').';
|
$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;
|
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,7 +6,6 @@
|
||||||
|
|
||||||
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\AuditLogEntry;
|
||||||
use App\Models\Entry;
|
use App\Models\Entry;
|
||||||
|
|
@ -34,17 +33,17 @@ class EnterScore
|
||||||
$scores = collect($scores);
|
$scores = collect($scores);
|
||||||
|
|
||||||
// Basic Validity Checks
|
// Basic Validity Checks
|
||||||
if (! User::where('id', $user->id)->exists()) {
|
if (! $user->exists()) {
|
||||||
throw new AuditionAdminException('User does not exist');
|
throw new ScoreEntryException('User does not exist');
|
||||||
}
|
}
|
||||||
if (! Entry::where('id', $entry->id)->exists()) {
|
if (! $entry->exists()) {
|
||||||
throw new AuditionAdminException('Entry does not exist');
|
throw new ScoreEntryException('Entry does not exist');
|
||||||
}
|
}
|
||||||
if ($entry->audition->hasFlag('seats_published')) {
|
if ($entry->audition->hasFlag('seats_published')) {
|
||||||
throw new AuditionAdminException('Cannot score an entry in an audition where seats are published');
|
throw new ScoreEntryException('Cannot score an entry in an audition with published seats');
|
||||||
}
|
}
|
||||||
if ($entry->audition->hasFlag('advancement_published')) {
|
if ($entry->audition->hasFlag('advancement_published')) {
|
||||||
throw new AuditionAdminException('Cannot score an entry in an audition where advancement is published');
|
throw new ScoreEntryException('Cannot score an entry in an audition with published advancement');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that the specified user is assigned to judge this entry
|
// Check that the specified user is assigned to judge this entry
|
||||||
|
|
@ -52,20 +51,20 @@ class EnterScore
|
||||||
->where('room_id', $entry->audition->room_id)
|
->where('room_id', $entry->audition->room_id)
|
||||||
->where('user_id', $user->id)->exists();
|
->where('user_id', $user->id)->exists();
|
||||||
if (! $check) {
|
if (! $check) {
|
||||||
throw new AuditionAdminException('This judge is not assigned to judge this entry');
|
throw new ScoreEntryException('This judge is not assigned to judge this entry');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if a score already exists
|
// Check if a score already exists
|
||||||
if (! $scoreSheet) {
|
if (! $scoreSheet) {
|
||||||
if (ScoreSheet::where('user_id', $user->id)->where('entry_id', $entry->id)->exists()) {
|
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');
|
throw new ScoreEntryException('That judge has already entered scores for that entry');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if ($scoreSheet->user_id !== $user->id) {
|
if ($scoreSheet->user_id !== $user->id) {
|
||||||
throw new AuditionAdminException('Existing score sheet is from a different judge');
|
throw new ScoreEntryException('Existing score sheet is from a different judge');
|
||||||
}
|
}
|
||||||
if ($scoreSheet->entry_id !== $entry->id) {
|
if ($scoreSheet->entry_id !== $entry->id) {
|
||||||
throw new AuditionAdminException('Existing score sheet is for a different entry');
|
throw new ScoreEntryException('Existing score sheet is for a different entry');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -77,17 +76,16 @@ class EnterScore
|
||||||
$advancementTotal = 0;
|
$advancementTotal = 0;
|
||||||
$advancementMaxPossible = 0;
|
$advancementMaxPossible = 0;
|
||||||
if ($scores->count() !== $subscoresRequired->count()) {
|
if ($scores->count() !== $subscoresRequired->count()) {
|
||||||
throw new AuditionAdminException('Invalid number of scores');
|
throw new ScoreEntryException('Invalid number of scores');
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($subscoresRequired as $subscore) {
|
foreach ($subscoresRequired as $subscore) {
|
||||||
// check that there is an element in the $scores collection with the key = $subscore->id
|
// check that there is an element in the $scores collection with the key = $subscore->id
|
||||||
if (! $scores->keys()->contains($subscore->id)) {
|
if (! $scores->keys()->contains($subscore->id)) {
|
||||||
throw new AuditionAdminException('Invalid Score Submission');
|
throw new ScoreEntryException('Invalid Score Submission');
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($scores[$subscore->id] > $subscore->max_score) {
|
if ($scores[$subscore->id] > $subscore->max_score) {
|
||||||
throw new AuditionAdminException('Supplied subscore exceeds maximum allowed');
|
throw new ScoreEntryException('Supplied subscore exceeds maximum allowed');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add subscore to the storage array
|
// Add subscore to the storage array
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,6 @@ namespace App\Actions\Tabulation;
|
||||||
|
|
||||||
use App\Models\Entry;
|
use App\Models\Entry;
|
||||||
|
|
||||||
/**
|
|
||||||
* @codeCoverageIgnore
|
|
||||||
*/
|
|
||||||
class ForceRecalculateTotalScores
|
class ForceRecalculateTotalScores
|
||||||
{
|
{
|
||||||
public function __invoke(): void
|
public function __invoke(): void
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -24,27 +24,23 @@ class RankAuditionEntries
|
||||||
*
|
*
|
||||||
* @throws AuditionAdminException
|
* @throws AuditionAdminException
|
||||||
*/
|
*/
|
||||||
public function __invoke(Audition $audition, string $rank_type, bool $pullDeclinedEntries = true): Collection|Entry
|
public function __invoke(Audition $audition, string $rank_type)
|
||||||
{
|
{
|
||||||
if ($rank_type !== 'seating' && $rank_type !== 'advancement') {
|
if ($rank_type !== 'seating' && $rank_type !== 'advancement') {
|
||||||
throw new AuditionAdminException('Invalid rank type (must be seating or advancement)');
|
throw new AuditionAdminException('Invalid rank type: '.$rank_type.' (must be seating or advancement)');
|
||||||
}
|
}
|
||||||
|
|
||||||
$cache_duration = 15;
|
$cache_duration = 15;
|
||||||
|
|
||||||
if ($rank_type === 'seating') {
|
if ($rank_type === 'seating') {
|
||||||
return cache()->remember('rank_seating_'.$audition->id, $cache_duration, function () use ($audition, $pullDeclinedEntries) {
|
return cache()->remember('rank_seating_'.$audition->id, $cache_duration, function () use ($audition) {
|
||||||
return $this->get_seating_ranks($audition, $pullDeclinedEntries);
|
return $this->get_seating_ranks($audition);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
private function get_seating_ranks(Audition $audition): Collection
|
||||||
{
|
{
|
||||||
if ($audition->bonusScore()->count() > 0) {
|
if ($audition->bonusScore()->count() > 0) {
|
||||||
$totalColumn = 'seating_total_with_bonus';
|
$totalColumn = 'seating_total_with_bonus';
|
||||||
|
|
@ -53,7 +49,6 @@ class RankAuditionEntries
|
||||||
}
|
}
|
||||||
|
|
||||||
$sortedEntries = $audition->entries()
|
$sortedEntries = $audition->entries()
|
||||||
->where('for_seating', true)
|
|
||||||
->whereHas('totalScore')
|
->whereHas('totalScore')
|
||||||
->with('totalScore')
|
->with('totalScore')
|
||||||
->with('student.school')
|
->with('student.school')
|
||||||
|
|
@ -75,7 +70,7 @@ class RankAuditionEntries
|
||||||
|
|
||||||
$rankOn = 1;
|
$rankOn = 1;
|
||||||
foreach ($sortedEntries as $entry) {
|
foreach ($sortedEntries as $entry) {
|
||||||
if ($entry->hasFlag('declined') && $pullDeclinedEntries) {
|
if ($entry->hasFlag('declined')) {
|
||||||
$entry->seatingRank = 'declined';
|
$entry->seatingRank = 'declined';
|
||||||
} else {
|
} else {
|
||||||
$entry->seatingRank = $rankOn;
|
$entry->seatingRank = $rankOn;
|
||||||
|
|
@ -86,9 +81,9 @@ class RankAuditionEntries
|
||||||
return $sortedEntries;
|
return $sortedEntries;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function get_advancement_ranks(Audition $audition): Collection|Entry
|
private function get_advancement_ranks(Audition $audition): Collection
|
||||||
{
|
{
|
||||||
$sortedEntries = $audition->entries()
|
return $audition->entries()
|
||||||
->whereHas('totalScore')
|
->whereHas('totalScore')
|
||||||
->with('totalScore')
|
->with('totalScore')
|
||||||
->with('student.school')
|
->with('student.school')
|
||||||
|
|
@ -107,12 +102,5 @@ class RankAuditionEntries
|
||||||
->orderByRaw('COALESCE(JSON_EXTRACT(entry_total_scores.advancement_subscore_totals, "$[9]"), -999999) DESC')
|
->orderByRaw('COALESCE(JSON_EXTRACT(entry_total_scores.advancement_subscore_totals, "$[9]"), -999999) DESC')
|
||||||
->select('entries.*')
|
->select('entries.*')
|
||||||
->get();
|
->get();
|
||||||
$n = 1;
|
|
||||||
foreach ($sortedEntries as $entry) {
|
|
||||||
$entry->advancementRank = $n;
|
|
||||||
$n++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $sortedEntries;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,19 +33,16 @@ class TotalEntryScores
|
||||||
// deal with seating scores
|
// deal with seating scores
|
||||||
// TODO: Consider a rewrite to pull the scoreSheets from the entry model so they may be preloaded
|
// 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();
|
$scoreSheets = ScoreSheet::where('entry_id', $entry->id)->orderBy('seating_total', 'desc')->get();
|
||||||
|
// bail out if there are no score sheets
|
||||||
// bail out if there are not enough score sheets
|
if ($scoreSheets->count() == 0) {
|
||||||
$assignedJudges = $entry->audition->judges()->count();
|
|
||||||
if ($scoreSheets->count() == 0 || $scoreSheets->count() < $assignedJudges) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (auditionSetting('olympic_scoring' && $scoreSheets->count() > 2)) {
|
||||||
if (auditionSetting('olympic_scoring') && $scoreSheets->count() > 2) {
|
|
||||||
// under olympic scoring, drop the first and last element
|
// under olympic scoring, drop the first and last element
|
||||||
$scoreSheets->shift();
|
$scoreSheets->shift();
|
||||||
$scoreSheets->pop();
|
$scoreSheets->pop();
|
||||||
}
|
}
|
||||||
$newTotaledScore->seating_total = round($scoreSheets->avg('seating_total'), 6);
|
$newTotaledScore->seating_total = $scoreSheets->avg('seating_total');
|
||||||
$seatingSubscores = $requiredSubscores
|
$seatingSubscores = $requiredSubscores
|
||||||
->filter(fn ($subscore) => $subscore->for_seating == true)
|
->filter(fn ($subscore) => $subscore->for_seating == true)
|
||||||
->sortBy('tiebreak_order');
|
->sortBy('tiebreak_order');
|
||||||
|
|
@ -55,18 +52,18 @@ class TotalEntryScores
|
||||||
foreach ($scoreSheets as $scoreSheet) {
|
foreach ($scoreSheets as $scoreSheet) {
|
||||||
$runningTotal += $scoreSheet->subscores[$subscore->id]['score'];
|
$runningTotal += $scoreSheet->subscores[$subscore->id]['score'];
|
||||||
}
|
}
|
||||||
$total_seating_subscores[] = round($runningTotal / $scoreSheets->count(), 4);
|
$total_seating_subscores[] = $runningTotal / $scoreSheets->count();
|
||||||
}
|
}
|
||||||
$newTotaledScore->seating_subscore_totals = $total_seating_subscores;
|
$newTotaledScore->seating_subscore_totals = $total_seating_subscores;
|
||||||
|
|
||||||
// deal with advancement scores
|
// deal with advancement scores
|
||||||
$scoreSheets = ScoreSheet::where('entry_id', $entry->id)->orderBy('advancement_total', 'desc')->get();
|
$scoreSheets = ScoreSheet::where('entry_id', $entry->id)->orderBy('advancement_total', 'desc')->get();
|
||||||
if (auditionSetting('olympic_scoring') && $scoreSheets->count() > 2) {
|
if (auditionSetting('olympic_scoring' && $scoreSheets->count() > 2)) {
|
||||||
// under olympic scoring, drop the first and last element
|
// under olympic scoring, drop the first and last element
|
||||||
$scoreSheets->shift();
|
$scoreSheets->shift();
|
||||||
$scoreSheets->pop();
|
$scoreSheets->pop();
|
||||||
}
|
}
|
||||||
$newTotaledScore->advancement_total = round($scoreSheets->avg('advancement_total'), 6);
|
$newTotaledScore->advancement_total = $scoreSheets->avg('advancement_total');
|
||||||
$advancement_subscores = $requiredSubscores
|
$advancement_subscores = $requiredSubscores
|
||||||
->filter(fn ($subscore) => $subscore->for_advance == true)
|
->filter(fn ($subscore) => $subscore->for_advance == true)
|
||||||
->sortBy('tiebreak_order');
|
->sortBy('tiebreak_order');
|
||||||
|
|
@ -76,7 +73,7 @@ class TotalEntryScores
|
||||||
foreach ($scoreSheets as $scoreSheet) {
|
foreach ($scoreSheets as $scoreSheet) {
|
||||||
$runningTotal += $scoreSheet->subscores[$subscore->id]['score'];
|
$runningTotal += $scoreSheet->subscores[$subscore->id]['score'];
|
||||||
}
|
}
|
||||||
$total_advancement_subscores[] = round($runningTotal / $scoreSheets->count(), 4);
|
$total_advancement_subscores[] = $runningTotal / $scoreSheets->count();
|
||||||
}
|
}
|
||||||
$newTotaledScore->advancement_subscore_totals = $total_advancement_subscores;
|
$newTotaledScore->advancement_subscore_totals = $total_advancement_subscores;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,10 @@ use Carbon\Carbon;
|
||||||
|
|
||||||
class RecordHistoricalSeats
|
class RecordHistoricalSeats
|
||||||
{
|
{
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public function __invoke(): void
|
public function __invoke(): void
|
||||||
{
|
{
|
||||||
$this->saveSeats();
|
$this->saveSeats();
|
||||||
|
|
|
||||||
|
|
@ -6,26 +6,24 @@ use App\Exceptions\AuditionAdminException;
|
||||||
use App\Models\AuditionFlag;
|
use App\Models\AuditionFlag;
|
||||||
use App\Models\AuditLogEntry;
|
use App\Models\AuditLogEntry;
|
||||||
use App\Models\BonusScore;
|
use App\Models\BonusScore;
|
||||||
use App\Models\Doubler;
|
use App\Models\CalculatedScore;
|
||||||
use App\Models\DoublerRequest;
|
use App\Models\DoublerRequest;
|
||||||
use App\Models\EntryFlag;
|
use App\Models\EntryFlag;
|
||||||
use App\Models\EntryTotalScore;
|
|
||||||
use App\Models\JudgeAdvancementVote;
|
use App\Models\JudgeAdvancementVote;
|
||||||
use App\Models\NominationEnsembleEntry;
|
use App\Models\NominationEnsembleEntry;
|
||||||
use App\Models\ScoreSheet;
|
use App\Models\ScoreSheet;
|
||||||
use App\Models\Seat;
|
use App\Models\Seat;
|
||||||
use App\Models\Student;
|
use App\Models\Student;
|
||||||
use App\Models\UserFlag;
|
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
use function auth;
|
use function auth;
|
||||||
|
|
||||||
/**
|
|
||||||
* @codeCoverageIgnore
|
|
||||||
*/
|
|
||||||
// TODO: figure out how to test YearEndCleanup
|
|
||||||
class YearEndCleanup
|
class YearEndCleanup
|
||||||
{
|
{
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public function __invoke(?array $options = []): void
|
public function __invoke(?array $options = []): void
|
||||||
{
|
{
|
||||||
$this->cleanup($options);
|
$this->cleanup($options);
|
||||||
|
|
@ -51,9 +49,8 @@ class YearEndCleanup
|
||||||
AuditLogEntry::truncate();
|
AuditLogEntry::truncate();
|
||||||
AuditionFlag::truncate();
|
AuditionFlag::truncate();
|
||||||
BonusScore::truncate();
|
BonusScore::truncate();
|
||||||
EntryTotalScore::truncate();
|
CalculatedScore::truncate();
|
||||||
DoublerRequest::truncate();
|
DoublerRequest::truncate();
|
||||||
Doubler::truncate();
|
|
||||||
EntryFlag::truncate();
|
EntryFlag::truncate();
|
||||||
ScoreSheet::truncate();
|
ScoreSheet::truncate();
|
||||||
Seat::truncate();
|
Seat::truncate();
|
||||||
|
|
@ -65,20 +62,19 @@ class YearEndCleanup
|
||||||
|
|
||||||
if (is_array($options)) {
|
if (is_array($options)) {
|
||||||
if (in_array('deleteRooms', $options)) {
|
if (in_array('deleteRooms', $options)) {
|
||||||
DB::table('auditions')->update(['room_id' => 0]);
|
DB::table('auditions')->update(['room_id' => null]);
|
||||||
DB::table('auditions')->update(['order_in_room' => '0']);
|
DB::table('auditions')->update(['order_in_room' => '0']);
|
||||||
DB::table('room_user')->truncate();
|
DB::table('room_user')->truncate();
|
||||||
DB::table('rooms')->where('id', '>', 0)->delete();
|
DB::table('rooms')->delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (in_array('removeAuditionsFromRoom', $options)) {
|
if (in_array('removeAuditionsFromRoom', $options)) {
|
||||||
DB::table('auditions')->update(['room_id' => 0]);
|
DB::table('auditions')->update(['room_id' => null]);
|
||||||
DB::table('auditions')->update(['order_in_room' => '0']);
|
DB::table('auditions')->update(['order_in_room' => '0']);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (in_array('unassignJudges', $options)) {
|
if (in_array('unassignJudges', $options)) {
|
||||||
DB::table('room_user')->truncate();
|
DB::table('room_user')->truncate();
|
||||||
UserFlag::where('flag', 'monitor')->delete();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -5,17 +5,14 @@ namespace App\Console\Commands;
|
||||||
use App\Actions\Tabulation\ForceRecalculateTotalScores;
|
use App\Actions\Tabulation\ForceRecalculateTotalScores;
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
|
|
||||||
/**
|
class RecalculateScores extends Command
|
||||||
* @codeCoverageIgnore
|
|
||||||
*/
|
|
||||||
class RecalculateTotalScores extends Command
|
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* The name and signature of the console command.
|
* The name and signature of the console command.
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $signature = 'audition:recalculate-total-scores';
|
protected $signature = 'audition:recalculate-scores';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The console command description.
|
* The console command description.
|
||||||
|
|
@ -2,13 +2,10 @@
|
||||||
|
|
||||||
namespace App\Console\Commands;
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
use App\Actions\Tabulation\DoublerSync;
|
use App\Models\Doubler;
|
||||||
use App\Models\Event;
|
use App\Models\Event;
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
|
|
||||||
/**
|
|
||||||
* @codeCoverageIgnore
|
|
||||||
*/
|
|
||||||
class SyncDoublers extends Command
|
class SyncDoublers extends Command
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
|
@ -30,13 +27,12 @@ class SyncDoublers extends Command
|
||||||
*/
|
*/
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
$syncer = app(DoublerSync::class);
|
|
||||||
if ($eventId = $this->argument('event')) {
|
if ($eventId = $this->argument('event')) {
|
||||||
$event = Event::findOrFail($eventId);
|
$event = Event::findOrFail($eventId);
|
||||||
$syncer($event);
|
Doubler::syncForEvent($event);
|
||||||
$this->info("Synced doublers for event {$event->name}");
|
$this->info("Synced doublers for event {$event->name}");
|
||||||
} else {
|
} else {
|
||||||
$syncer();
|
Doubler::syncDoublers();
|
||||||
$this->info('Synced doublers for all events');
|
$this->info('Synced doublers for all events');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,78 +10,43 @@ use Illuminate\Console\Command;
|
||||||
|
|
||||||
class fictionalize extends Command
|
class fictionalize extends Command
|
||||||
{
|
{
|
||||||
protected $signature = 'audition:fictionalize
|
/**
|
||||||
{--students : Fictionalize student names}
|
* The name and signature of the console command.
|
||||||
{--schools : Fictionalize school names}
|
*
|
||||||
{--users : Fictionalize user data}
|
* @var string
|
||||||
{--all : Fictionalize all data types}';
|
*/
|
||||||
|
protected $signature = 'audition:fictionalize';
|
||||||
|
|
||||||
protected $description = 'Replace real names with fictional data for specified entity types';
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Command description';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*/
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
$faker = Factory::create();
|
$faker = Factory::create();
|
||||||
|
foreach (Student::all() as $student) {
|
||||||
// If no options are specified or --all is used, process everything
|
$student->first_name = $faker->firstName();
|
||||||
$processAll = $this->option('all') ||
|
$student->last_name = $faker->lastName();
|
||||||
(! $this->option('students') && ! $this->option('schools') && ! $this->option('users'));
|
$student->save();
|
||||||
|
|
||||||
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')) {
|
foreach (School::all() as $school) {
|
||||||
$this->info('Fictionalizing schools...');
|
$school->name = $faker->city().' High School';
|
||||||
$bar = $this->output->createProgressBar(School::count());
|
$school->save();
|
||||||
|
|
||||||
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')) {
|
foreach (User::where('email', '!=', 'matt@mattyoung.us')->get() as $user) {
|
||||||
$this->info('Fictionalizing users...');
|
$user->email = $faker->email();
|
||||||
$bar = $this->output->createProgressBar(User::where('email', '!=', 'matt@mattyoung.us')->count());
|
$user->first_name = $faker->firstName();
|
||||||
|
$user->last_name = $faker->lastName();
|
||||||
User::where('email', '!=', 'matt@mattyoung.us')
|
$user->cell_phone = $faker->phoneNumber();
|
||||||
->chunk(100, function ($users) use ($faker, $bar) {
|
$user->save();
|
||||||
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()
|
||||||
|
|
|
||||||
|
|
@ -8,19 +8,18 @@ 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) {
|
||||||
|
abort(403);
|
||||||
|
}
|
||||||
$rooms = Room::with('auditions.entries', 'entries')->orderBy('name')->get();
|
$rooms = Room::with('auditions.entries', 'entries')->orderBy('name')->get();
|
||||||
|
|
||||||
// Check if room id 0 exists, if not, create it and assign all unassigned auditions to it
|
|
||||||
if (! $rooms->contains('id', 0)) {
|
if (! $rooms->contains('id', 0)) {
|
||||||
$unassignedRoom = Room::create([
|
$unassignedRoom = Room::create([
|
||||||
'id' => 0,
|
'id' => 0,
|
||||||
|
|
@ -30,7 +29,7 @@ class RoomController extends Controller
|
||||||
$unassignedRoom->id = 0;
|
$unassignedRoom->id = 0;
|
||||||
$unassignedRoom->save();
|
$unassignedRoom->save();
|
||||||
|
|
||||||
$auditionsToUpdate = Audition::whereNull('room_id')->get();
|
$auditionsToUpdate = Audition::where('room_id', null)->get();
|
||||||
foreach ($auditionsToUpdate as $audition) {
|
foreach ($auditionsToUpdate as $audition) {
|
||||||
$audition->room_id = 0;
|
$audition->room_id = 0;
|
||||||
$audition->save();
|
$audition->save();
|
||||||
|
|
@ -54,6 +53,9 @@ class RoomController extends Controller
|
||||||
|
|
||||||
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 +69,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 +105,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,6 +122,10 @@ 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');
|
||||||
|
|
|
||||||
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ class YearEndResetController extends Controller
|
||||||
|
|
||||||
public function execute()
|
public function execute()
|
||||||
{
|
{
|
||||||
$cleanUpProcedure = app(YearEndCleanup::class);
|
$cleanUpProcedure = new YearEndCleanup;
|
||||||
$options = request()->options;
|
$options = request()->options;
|
||||||
$cleanUpProcedure($options);
|
$cleanUpProcedure($options);
|
||||||
auditionLog('Executed year end reset.', []);
|
auditionLog('Executed year end reset.', []);
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ class DashboardController extends Controller
|
||||||
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,10 +9,6 @@ use Illuminate\Http\Request;
|
||||||
|
|
||||||
use function auditionSetting;
|
use function auditionSetting;
|
||||||
|
|
||||||
/**
|
|
||||||
* @codeCoverageIgnore
|
|
||||||
* TODO: Figure out testing for printing
|
|
||||||
*/
|
|
||||||
class PdfInvoiceController extends Controller
|
class PdfInvoiceController extends Controller
|
||||||
{
|
{
|
||||||
protected $pdf;
|
protected $pdf;
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,6 @@ use App\Actions\Tabulation\RankAuditionEntries;
|
||||||
use App\Models\Audition;
|
use App\Models\Audition;
|
||||||
use Illuminate\Support\Facades\App;
|
use Illuminate\Support\Facades\App;
|
||||||
|
|
||||||
/** @codeCoverageIgnore */
|
|
||||||
// TODO: Rewrite Recap
|
|
||||||
class RecapController extends Controller
|
class RecapController extends Controller
|
||||||
{
|
{
|
||||||
public function selectAudition()
|
public function selectAudition()
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ use App\Models\Ensemble;
|
||||||
use App\Models\Entry;
|
use App\Models\Entry;
|
||||||
use App\Models\Seat;
|
use App\Models\Seat;
|
||||||
use App\Services\AuditionService;
|
use App\Services\AuditionService;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Cache;
|
use Illuminate\Support\Facades\Cache;
|
||||||
use Illuminate\Support\Facades\View;
|
use Illuminate\Support\Facades\View;
|
||||||
|
|
@ -29,11 +28,9 @@ class ResultsPage extends Controller
|
||||||
*/
|
*/
|
||||||
public function __invoke(Request $request)
|
public function __invoke(Request $request)
|
||||||
{
|
{
|
||||||
Model::preventLazyLoading(false);
|
|
||||||
$cacheKey = 'publicResultsPage';
|
$cacheKey = 'publicResultsPage';
|
||||||
|
|
||||||
if (Cache::has($cacheKey)) {
|
if (Cache::has($cacheKey)) {
|
||||||
/** @codeCoverageIgnore */
|
|
||||||
return response(Cache::get($cacheKey));
|
return response(Cache::get($cacheKey));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -94,4 +91,9 @@ class ResultsPage extends Controller
|
||||||
|
|
||||||
return response($content);
|
return response($content);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function generateResultsPage()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,48 +2,93 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App\Actions\Fortify\CreateNewUser;
|
|
||||||
use App\Actions\Schools\AddSchoolEmailDomain;
|
|
||||||
use App\Actions\Schools\AssignUserToSchool;
|
|
||||||
use App\Actions\Schools\CreateSchool;
|
|
||||||
use App\Actions\Schools\SetHeadDirector;
|
use App\Actions\Schools\SetHeadDirector;
|
||||||
use App\Http\Requests\SchoolStoreRequest;
|
use App\Exceptions\AuditionAdminException;
|
||||||
use App\Mail\NewUserPassword;
|
use App\Mail\NewUserPassword;
|
||||||
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 Illuminate\Database\UniqueConstraintViolationException;
|
||||||
use Illuminate\Http\RedirectResponse;
|
use Illuminate\Http\RedirectResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Illuminate\Support\Facades\Hash;
|
||||||
use Illuminate\Support\Facades\Mail;
|
use Illuminate\Support\Facades\Mail;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
use function abort;
|
use function abort;
|
||||||
|
use function auditionLog;
|
||||||
use function redirect;
|
use function redirect;
|
||||||
use function request;
|
use function request;
|
||||||
|
|
||||||
class SchoolController extends Controller
|
class SchoolController extends Controller
|
||||||
{
|
{
|
||||||
public function store(SchoolStoreRequest $request, SetHeadDirector $headSetter): RedirectResponse
|
public function store(Request $request, SetHeadDirector $headSetter): RedirectResponse
|
||||||
{
|
{
|
||||||
$creator = app(CreateSchool::class);
|
if ($request->user()->cannot('create', School::class)) {
|
||||||
|
abort(403);
|
||||||
|
}
|
||||||
|
request()->validate([
|
||||||
|
'name' => ['required', 'min:3', 'max:30'],
|
||||||
|
'address' => ['required'],
|
||||||
|
'city' => ['required'],
|
||||||
|
'state' => ['required', 'min:2', 'max:2'],
|
||||||
|
'zip' => ['required', 'min:5', 'max:10'],
|
||||||
|
]);
|
||||||
|
|
||||||
$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]],
|
||||||
|
]);
|
||||||
|
|
||||||
$assigner = app(AssignUserToSchool::class);
|
if (! Auth::user()->school) {
|
||||||
$assigner(auth()->user(), $school);
|
Auth::user()->update([
|
||||||
|
'school_id' => $school->id,
|
||||||
|
]);
|
||||||
|
$message = 'Set user '.auth()->user()->full_name().' ('.auth()->user()->email.') as a director at '.$school->name.'(#'.$school->id.')';
|
||||||
|
AuditLogEntry::create([
|
||||||
|
'user' => auth()->user()->email,
|
||||||
|
'ip_address' => request()->ip(),
|
||||||
|
'message' => $message,
|
||||||
|
'affected' => [
|
||||||
|
'users' => [auth()->user()->id],
|
||||||
|
'schools' => [$school->id],
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
SchoolEmailDomain::create([
|
||||||
|
'school_id' => $school->id,
|
||||||
|
'domain' => Auth::user()->emailDomain(),
|
||||||
|
]);
|
||||||
|
$message = 'Added '.auth()->user()->emailDomain().' as an email domain for '.$school->name.' (#'.$school->id.')';
|
||||||
|
AuditLogEntry::create([
|
||||||
|
'user' => auth()->user()->email,
|
||||||
|
'ip_address' => request()->ip(),
|
||||||
|
'message' => $message,
|
||||||
|
'affected' => [
|
||||||
|
'schools' => [$school->id],
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
auth()->user()->refresh();
|
||||||
|
try {
|
||||||
|
$headSetter->setHeadDirector(auth()->user());
|
||||||
|
} catch (AuditionAdminException $e) {
|
||||||
|
redirect(route('schools.show', $school))->with('error', 'Could not set as head director');
|
||||||
|
}
|
||||||
|
|
||||||
auth()->user()->refresh();
|
}
|
||||||
|
|
||||||
$headSetter->setHeadDirector(auth()->user());
|
return redirect('/schools/'.$school->id);
|
||||||
|
|
||||||
return redirect(route('schools.show', $school));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function show(Request $request, School $school)
|
public function show(Request $request, School $school)
|
||||||
|
|
@ -73,14 +118,25 @@ class SchoolController extends Controller
|
||||||
return view('schools.edit', ['school' => $school]);
|
return view('schools.edit', ['school' => $school]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function update(SchoolStoreRequest $request, School $school)
|
public function update(Request $request, School $school)
|
||||||
{
|
{
|
||||||
|
if ($request->user()->cannot('update', $school)) {
|
||||||
|
abort(403);
|
||||||
|
}
|
||||||
|
request()->validate([
|
||||||
|
'name' => ['required', 'min:3', 'max:30'],
|
||||||
|
'address' => ['required'],
|
||||||
|
'city' => ['required'],
|
||||||
|
'state' => ['required', 'min:2', 'max:2'],
|
||||||
|
'zip' => ['required', 'min:5', 'max:10'],
|
||||||
|
]);
|
||||||
|
|
||||||
$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;
|
$message = 'Modified school #'.$school->id.' - '.$school->name.' with address <br>'.$school->address.'<br>'.$school->city.', '.$school->state.' '.$school->zip;
|
||||||
AuditLogEntry::create([
|
AuditLogEntry::create([
|
||||||
|
|
@ -93,26 +149,46 @@ class SchoolController extends Controller
|
||||||
return redirect()->route('schools.show', $school->id)->with('success', 'School details updated');
|
return redirect()->route('schools.show', $school->id)->with('success', 'School details updated');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function my_school()
|
||||||
|
{
|
||||||
|
if (Auth::user()->school) {
|
||||||
|
return redirect('/schools/'.Auth::user()->school->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return redirect('/schools/create');
|
||||||
|
}
|
||||||
|
|
||||||
public function addDirector(School $school)
|
public function addDirector(School $school)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (auth()->user()->school_id !== $school->id) {
|
if (auth()->user()->school_id !== $school->id) {
|
||||||
abort(403);
|
return redirect()->back()->with('error', 'No adding directors to another school');
|
||||||
}
|
}
|
||||||
if (! auth()->user()->hasFlag('head_director')) {
|
if (! auth()->user()->hasFlag('head_director')) {
|
||||||
abort(403);
|
return redirect()->back()->with('error', 'Only the head director can add directors to a school');
|
||||||
}
|
}
|
||||||
|
$validData = request()->validate([
|
||||||
$userCreator = app(CreateNewUser::class);
|
'first_name' => ['required'],
|
||||||
$randomPassword = Str::random(12);
|
'last_name' => ['required'],
|
||||||
$data = request()->all();
|
'email' => ['required', 'email', 'unique:users'],
|
||||||
$data['password'] = $randomPassword;
|
'cell_phone' => ['required'],
|
||||||
$data['password_confirmation'] = $randomPassword;
|
'judging_preference' => ['required'],
|
||||||
$newDirector = $userCreator->create($data);
|
|
||||||
$newDirector->update([
|
|
||||||
'school_id' => $school->id,
|
|
||||||
]);
|
]);
|
||||||
|
// Generate a random password
|
||||||
Mail::to($newDirector->email)->send(new NewUserPassword($newDirector, $randomPassword));
|
$randomPassword = Str::random(12);
|
||||||
|
$newUser = User::create([
|
||||||
|
'first_name' => $validData['first_name'],
|
||||||
|
'last_name' => $validData['last_name'],
|
||||||
|
'email' => $validData['email'],
|
||||||
|
'cell_phone' => $validData['cell_phone'],
|
||||||
|
'judging_preference' => $validData['judging_preference'],
|
||||||
|
'password' => Hash::make($randomPassword),
|
||||||
|
'school_id' => auth()->user()->school_id,
|
||||||
|
]);
|
||||||
|
$logMessage = 'Created user '.$newUser->full_name().' - '.$newUser->email.' as a director at '.$newUser->school->name;
|
||||||
|
$logAffected = ['users' => [$newUser->id], 'schools' => [$newUser->school_id]];
|
||||||
|
auditionLog($logMessage, $logAffected);
|
||||||
|
Mail::to($newUser->email)->send(new NewUserPassword($newUser, $randomPassword));
|
||||||
|
|
||||||
return redirect()->back()->with('success', 'Director added');
|
return redirect()->back()->with('success', 'Director added');
|
||||||
}
|
}
|
||||||
|
|
@ -120,49 +196,62 @@ class SchoolController extends Controller
|
||||||
public function setHeadDirector(School $school, User $user, SetHeadDirector $headSetter)
|
public function setHeadDirector(School $school, User $user, SetHeadDirector $headSetter)
|
||||||
{
|
{
|
||||||
if (auth()->user()->school_id !== $school->id) {
|
if (auth()->user()->school_id !== $school->id) {
|
||||||
abort(403);
|
return redirect()->back()->with('error', 'No setting the head director for another school');
|
||||||
}
|
}
|
||||||
if (! auth()->user()->hasFlag('head_director')) {
|
if (! auth()->user()->hasFlag('head_director')) {
|
||||||
abort(403);
|
return redirect()->back()->with('error', 'Only the head director can name a new head director');
|
||||||
}
|
}
|
||||||
if ($school->id !== $user->school_id) {
|
if ($school->id !== $user->school_id) {
|
||||||
abort(403);
|
return redirect()->back()->with('error', 'The proposed head director must be at your school');
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$headSetter->setHeadDirector($user);
|
||||||
|
} catch (AuditionAdminException $e) {
|
||||||
|
return redirect()->back()->with('error', $e->getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
$headSetter->setHeadDirector($user);
|
return redirect()->back()->with('success', 'New head director set');
|
||||||
|
|
||||||
return redirect()->route('schools.show', $school)->with('success', 'New head director set');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function addDomain(School $school)
|
public function addDomain(School $school)
|
||||||
{
|
{
|
||||||
if (auth()->user()->school_id !== $school->id) {
|
if (auth()->user()->school_id !== $school->id) {
|
||||||
abort(403);
|
return redirect()->back()->with('error', 'No adding domains for another school');
|
||||||
}
|
}
|
||||||
if (! auth()->user()->hasFlag('head_director')) {
|
if (! auth()->user()->hasFlag('head_director')) {
|
||||||
abort(403);
|
return redirect()->back()->with('error', 'Only the head director can add domains');
|
||||||
}
|
}
|
||||||
$verifiedData = request()->validate([
|
$verifiedData = request()->validate([
|
||||||
'domain' => ['required'],
|
'domain' => ['required'],
|
||||||
]);
|
]);
|
||||||
app(AddSchoolEmailDomain::class)->addDomain($school, $verifiedData['domain']);
|
try {
|
||||||
|
SchoolEmailDomain::create([
|
||||||
|
'school_id' => $school->id,
|
||||||
|
'domain' => $verifiedData['domain'],
|
||||||
|
]);
|
||||||
|
} catch (UniqueConstraintViolationException $e) {
|
||||||
|
return redirect()->back()->with('error', 'That domain is already associated with your school');
|
||||||
|
}
|
||||||
|
$logMessage = 'Added domain '.$verifiedData['domain'].' to school '.$school->name;
|
||||||
|
$logAffected = ['schools' => [$school->id]];
|
||||||
|
auditionLog($logMessage, $logAffected);
|
||||||
|
|
||||||
return redirect()->route('schools.show', $school)->with('success', 'Domain added');
|
return redirect()->back()->with('success', 'Domain added');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function deleteDomain(SchoolEmailDomain $domain)
|
public function deleteDomain(SchoolEmailDomain $domain)
|
||||||
{
|
{
|
||||||
if (auth()->user()->school_id !== $domain->school_id) {
|
if (auth()->user()->school_id !== $domain->school_id) {
|
||||||
abort(403);
|
return redirect()->back()->with('error', 'No deleting domains for another school');
|
||||||
}
|
}
|
||||||
if (! auth()->user()->hasFlag('head_director')) {
|
if (! auth()->user()->hasFlag('head_director')) {
|
||||||
abort(403);
|
return redirect()->back()->with('error', 'Only the head director can delete domains');
|
||||||
}
|
}
|
||||||
|
$logMessage = 'Deleted domain '.$domain->domain.' from school '.$domain->school->name;
|
||||||
|
$logAffected = ['schools' => [$domain->school_id]];
|
||||||
|
auditionLog($logMessage, $logAffected);
|
||||||
$domain->delete();
|
$domain->delete();
|
||||||
|
|
||||||
return redirect()
|
return redirect()->back()->with('success', 'Domain deleted');
|
||||||
->route('schools.show', auth()->user()->school)
|
|
||||||
->with('success', 'Domain deleted');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,10 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App\Actions\Students\CreateStudent;
|
|
||||||
use App\Http\Requests\StudentStoreRequest;
|
|
||||||
use App\Models\Audition;
|
use App\Models\Audition;
|
||||||
|
use App\Models\AuditLogEntry;
|
||||||
use App\Models\Student;
|
use App\Models\Student;
|
||||||
|
use App\Rules\UniqueFullNameAtSchool;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
|
|
@ -31,24 +31,70 @@ class StudentController extends Controller
|
||||||
['students' => $students, 'auditions' => $auditions, 'shirtSizes' => $shirtSizes]);
|
['students' => $students, 'auditions' => $auditions, 'shirtSizes' => $shirtSizes]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the form for creating a new resource.
|
||||||
|
*/
|
||||||
|
public function create()
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Store a newly created resource in storage.
|
* Store a newly created resource in storage.
|
||||||
*/
|
*/
|
||||||
public function store(StudentStoreRequest $request)
|
public function store(Request $request)
|
||||||
{
|
{
|
||||||
$creator = app(CreateStudent::class);
|
if ($request->user()->cannot('create', Student::class)) {
|
||||||
/** @noinspection PhpUnhandledExceptionInspection */
|
abort(403);
|
||||||
$creator([
|
}
|
||||||
'first_name' => $request['first_name'],
|
$request->validate([
|
||||||
'last_name' => $request['last_name'],
|
'first_name' => ['required'],
|
||||||
'grade' => $request['grade'],
|
'last_name' => [
|
||||||
'optional_data' => $request->optional_data,
|
'required',
|
||||||
|
new UniqueFullNameAtSchool(request('first_name'), request('last_name'), Auth::user()->school_id),
|
||||||
|
],
|
||||||
|
'grade' => ['required', 'integer'],
|
||||||
|
'shirt_size' => [
|
||||||
|
'nullable',
|
||||||
|
function ($attribute, $value, $fail) {
|
||||||
|
if (! array_key_exists($value, Student::$shirtSizes)) {
|
||||||
|
$fail("The selected $attribute is invalid.");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$student = Student::create([
|
||||||
|
'first_name' => request('first_name'),
|
||||||
|
'last_name' => request('last_name'),
|
||||||
|
'grade' => request('grade'),
|
||||||
|
'school_id' => Auth::user()->school_id,
|
||||||
|
]);
|
||||||
|
if (request('shirt_size') !== 'none') {
|
||||||
|
$student->update(['optional_data->shirt_size' => $request['shirt_size']]);
|
||||||
|
}
|
||||||
|
$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],
|
||||||
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
/** @codeCoverageIgnoreEnd */
|
|
||||||
return redirect('/students')->with('success', 'Student Created');
|
return redirect('/students')->with('success', 'Student Created');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display the specified resource.
|
||||||
|
*/
|
||||||
|
public function show(Request $request, Student $student)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show the form for editing the specified resource.
|
* Show the form for editing the specified resource.
|
||||||
*/
|
*/
|
||||||
|
|
@ -66,17 +112,52 @@ class StudentController extends Controller
|
||||||
/**
|
/**
|
||||||
* Update the specified resource in storage.
|
* Update the specified resource in storage.
|
||||||
*/
|
*/
|
||||||
public function update(StudentStoreRequest $request, Student $student)
|
public function update(Request $request, Student $student)
|
||||||
{
|
{
|
||||||
|
|
||||||
if ($request->user()->cannot('update', $student)) {
|
if ($request->user()->cannot('update', $student)) {
|
||||||
abort(403);
|
abort(403);
|
||||||
}
|
}
|
||||||
|
request()->validate([
|
||||||
|
'first_name' => ['required'],
|
||||||
|
'last_name' => ['required'],
|
||||||
|
'grade' => ['required', 'integer'],
|
||||||
|
'shirt_size' => [
|
||||||
|
'nullable',
|
||||||
|
function ($attribute, $value, $fail) {
|
||||||
|
if (! array_key_exists($value, Student::$shirtSizes)) {
|
||||||
|
$fail("The selected $attribute is invalid.");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (Student::where('first_name', request('first_name'))
|
||||||
|
->where('last_name', request('last_name'))
|
||||||
|
->where('school_id', Auth::user()->school_id)
|
||||||
|
->where('id', '!=', $student->id)
|
||||||
|
->exists()) {
|
||||||
|
return redirect()->route('students.edit', $student)->with('error',
|
||||||
|
'A student with that name already exists at your 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'),
|
||||||
'optional_data' => $request->optional_data,
|
]);
|
||||||
|
|
||||||
|
$student->update(['optional_data->shirt_size' => $request['shirt_size']]);
|
||||||
|
|
||||||
|
$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('/students')->with('success', 'Student updated successfully.');
|
return redirect('/students')->with('success', 'Student updated successfully.');
|
||||||
|
|
@ -90,7 +171,16 @@ class StudentController extends Controller
|
||||||
if ($request->user()->cannot('delete', $student)) {
|
if ($request->user()->cannot('delete', $student)) {
|
||||||
abort(403);
|
abort(403);
|
||||||
}
|
}
|
||||||
|
$message = 'Deleted 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],
|
||||||
|
],
|
||||||
|
]);
|
||||||
$student->delete();
|
$student->delete();
|
||||||
|
|
||||||
return redirect(route('students.index'));
|
return redirect(route('students.index'));
|
||||||
|
|
|
||||||
|
|
@ -2,64 +2,50 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers\Tabulation;
|
namespace App\Http\Controllers\Tabulation;
|
||||||
|
|
||||||
use App\Actions\Tabulation\CalculateAuditionScores;
|
|
||||||
use App\Actions\Tabulation\RankAuditionEntries;
|
use App\Actions\Tabulation\RankAuditionEntries;
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Models\Audition;
|
use App\Models\Audition;
|
||||||
use App\Models\EntryFlag;
|
use App\Models\Entry;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Cache;
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
|
||||||
use function is_null;
|
|
||||||
|
|
||||||
class AdvancementController extends Controller
|
class AdvancementController extends Controller
|
||||||
{
|
{
|
||||||
|
protected RankAuditionEntries $ranker;
|
||||||
|
|
||||||
|
public function __construct(RankAuditionEntries $ranker)
|
||||||
|
{
|
||||||
|
$this->ranker = $ranker;
|
||||||
|
}
|
||||||
|
|
||||||
public function status()
|
public function status()
|
||||||
{
|
{
|
||||||
// Total auditions scores if we haven't done it lately
|
|
||||||
if (! Cache::has('advancement_status_audition_totaler_throttle')) {
|
|
||||||
$lock = Cache::lock('advancement_status_audition_totaler_lock');
|
|
||||||
|
|
||||||
if ($lock->get()) {
|
|
||||||
try {
|
|
||||||
$totaler = app(CalculateAuditionScores::class);
|
|
||||||
foreach (Audition::forAdvancement()->with('judges')->get() as $audition) {
|
|
||||||
$totaler($audition);
|
|
||||||
}
|
|
||||||
|
|
||||||
// set throttle
|
|
||||||
Cache::put('advancement_status_audition_totaler_throttle', true, 15);
|
|
||||||
} finally {
|
|
||||||
$lock->release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$auditions = Audition::forAdvancement()
|
$auditions = Audition::forAdvancement()
|
||||||
->with('flags')
|
->with('flags')
|
||||||
->withCount([
|
->withCount([
|
||||||
'entries' => function ($query) {
|
'entries' => function ($query) {
|
||||||
$query->where('for_advancement', true);
|
$query->where('for_advancement', 1);
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
->withCount([
|
->withCount([
|
||||||
'unscoredEntries' => function ($query) {
|
'unscoredEntries' => function ($query) {
|
||||||
$query->where('for_advancement', true);
|
$query->where('for_advancement', 1);
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
->orderBy('score_order')
|
|
||||||
->get();
|
->get();
|
||||||
|
|
||||||
$auditionData = [];
|
$auditionData = [];
|
||||||
$auditions->each(function (Audition $audition) use (&$auditionData) {
|
$auditions->each(function ($audition) use (&$auditionData) {
|
||||||
|
$scoredPercent = ($audition->entries_count > 0) ?
|
||||||
|
round((($audition->entries_count - $audition->unscored_entries_count) / $audition->entries_count) * 100)
|
||||||
|
: 100;
|
||||||
$auditionData[] = [
|
$auditionData[] = [
|
||||||
'id' => $audition->id,
|
'id' => $audition->id,
|
||||||
'name' => $audition->name,
|
'name' => $audition->name,
|
||||||
'entries_count' => $audition->entries_count,
|
'entries_count' => $audition->entries_count,
|
||||||
'unscored_entries_count' => $audition->unscored_entries_count,
|
'unscored_entries_count' => $audition->unscored_entries_count,
|
||||||
'scored_entries_count' => $audition->entries_count - $audition->unscored_entries_count,
|
'scored_entries_count' => $audition->entries_count - $audition->unscored_entries_count,
|
||||||
'scored_percentage' => $audition->entries_count > 0 ? ((($audition->entries_count - $audition->unscored_entries_count) / $audition->entries_count) * 100) : 0,
|
'scored_percentage' => $scoredPercent,
|
||||||
'scoring_complete' => $audition->unscored_entries_count === 0,
|
'scoring_complete' => $audition->unscored_entries_count == 0,
|
||||||
'published' => $audition->hasFlag('advancement_published'),
|
'published' => $audition->hasFlag('advancement_published'),
|
||||||
];
|
];
|
||||||
});
|
});
|
||||||
|
|
@ -69,51 +55,29 @@ class AdvancementController extends Controller
|
||||||
|
|
||||||
public function ranking(Request $request, Audition $audition)
|
public function ranking(Request $request, Audition $audition)
|
||||||
{
|
{
|
||||||
$ranker = app(RankAuditionEntries::class);
|
$entries = $this->ranker->rank('advancement', $audition);
|
||||||
$entries = $ranker($audition, 'advancement');
|
$entries->load('advancementVotes');
|
||||||
$entries->load(['advancementVotes', 'totalScore', 'student.school']);
|
|
||||||
|
|
||||||
$unscoredEntries = $audition->entries()->where('for_advancement', true)->orderBy('draw_number')->get()->filter(function ($entry) {
|
$scoringComplete = $entries->every(function ($entry) {
|
||||||
return ! $entry->totalScore && ! $entry->hasFlag('no_show');
|
return $entry->score_totals[0] >= 0 || $entry->hasFlag('no_show');
|
||||||
});
|
});
|
||||||
|
|
||||||
$noShowEntries = $audition->entries()->orderBy('draw_number')->get()->filter(function ($entry) {
|
return view('tabulation.advancement.ranking', compact('audition', 'entries', 'scoringComplete'));
|
||||||
return $entry->hasFlag('no_show');
|
|
||||||
});
|
|
||||||
|
|
||||||
$scoringComplete = $audition->entries->where('for_advancement', true)->every(function ($entry) {
|
|
||||||
return $entry->totalScore || $entry->hasFlag('no_show');
|
|
||||||
});
|
|
||||||
|
|
||||||
return view('tabulation.advancement.ranking', compact('audition', 'entries', 'scoringComplete', 'unscoredEntries', 'noShowEntries'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setAuditionPassers(Request $request, Audition $audition)
|
public function setAuditionPassers(Request $request, Audition $audition)
|
||||||
{
|
{
|
||||||
|
|
||||||
$passingEntries = $request->input('pass');
|
$passingEntries = $request->input('pass');
|
||||||
if (is_null($passingEntries) || count($passingEntries) < 1) {
|
|
||||||
return redirect()->route('advancement.ranking', ['audition' => $audition->id])->with('error',
|
|
||||||
'Cannot publish advancement if no entries advance');
|
|
||||||
}
|
|
||||||
|
|
||||||
$audition->addFlag('advancement_published');
|
$audition->addFlag('advancement_published');
|
||||||
if (! is_null($passingEntries)) {
|
if (! is_null($passingEntries)) {
|
||||||
$passEntries = collect(array_keys($passingEntries));
|
$passingEntries = array_keys($passingEntries);
|
||||||
EntryFlag::insert(
|
$entries = Entry::whereIn('id', $passingEntries)->get();
|
||||||
$passEntries
|
foreach ($entries as $entry) {
|
||||||
->map(fn ($entryId) => [
|
$entry->addFlag('will_advance');
|
||||||
'entry_id' => $entryId,
|
}
|
||||||
'flag_name' => 'will_advance',
|
|
||||||
'created_at' => now(),
|
|
||||||
'updated_at' => now(),
|
|
||||||
])->toArray()
|
|
||||||
);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
Cache::forget('audition'.$audition->id.'advancement');
|
Cache::forget('audition'.$audition->id.'advancement');
|
||||||
Cache::forget('publicResultsPage');
|
Cache::forget('publicResultsPage');
|
||||||
Cache::forget('rank_advancement_'.$audition->id);
|
|
||||||
|
|
||||||
return redirect()->route('advancement.ranking', ['audition' => $audition->id])->with('success',
|
return redirect()->route('advancement.ranking', ['audition' => $audition->id])->with('success',
|
||||||
'Passers have been set successfully');
|
'Passers have been set successfully');
|
||||||
|
|
@ -122,10 +86,9 @@ class AdvancementController extends Controller
|
||||||
public function clearAuditionPassers(Request $request, Audition $audition)
|
public function clearAuditionPassers(Request $request, Audition $audition)
|
||||||
{
|
{
|
||||||
$audition->removeFlag('advancement_published');
|
$audition->removeFlag('advancement_published');
|
||||||
$audition->entries
|
foreach ($audition->entries as $entry) {
|
||||||
->filter(fn ($entry) => $entry->hasFlag('will_advance'))
|
$entry->removeFlag('will_advance');
|
||||||
->each(fn ($entry) => $entry->removeFlag('will_advance'));
|
}
|
||||||
|
|
||||||
Cache::forget('audition'.$audition->id.'advancement');
|
Cache::forget('audition'.$audition->id.'advancement');
|
||||||
Cache::forget('publicResultsPage');
|
Cache::forget('publicResultsPage');
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,14 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
/** @noinspection PhpUnhandledExceptionInspection */
|
|
||||||
|
|
||||||
namespace App\Http\Controllers\Tabulation;
|
namespace App\Http\Controllers\Tabulation;
|
||||||
|
|
||||||
use App\Actions\Tabulation\EnterBonusScore;
|
use App\Actions\Tabulation\EnterBonusScore;
|
||||||
use App\Actions\Tabulation\GetBonusScoreRelatedEntries;
|
use App\Actions\Tabulation\GetBonusScoreRelatedEntries;
|
||||||
use App\Exceptions\AuditionAdminException;
|
use App\Exceptions\ScoreEntryException;
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Models\BonusScore;
|
use App\Models\BonusScore;
|
||||||
use App\Models\Entry;
|
use App\Models\Entry;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use Exception;
|
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
use function request;
|
use function request;
|
||||||
|
|
@ -76,25 +73,26 @@ class BonusScoreController extends Controller
|
||||||
// Set the new score
|
// Set the new score
|
||||||
try {
|
try {
|
||||||
$saveBonusScore($judge, $entry, $validData['score']);
|
$saveBonusScore($judge, $entry, $validData['score']);
|
||||||
|
} catch (ScoreEntryException $ex) {
|
||||||
} catch (AuditionAdminException $ex) {
|
|
||||||
DB::rollBack();
|
DB::rollBack();
|
||||||
|
|
||||||
return redirect()->route('bonus-scores.entryBonusScoreSheet',
|
return redirect()->route('bonus-scores.entryBonusScoreSheet',
|
||||||
['entry_id' => $entry->id])->with('error', 'Error entering score - '.$ex->getMessage());
|
['entry_id' => $entry->id])->with('error', 'Error entering score - '.$ex->getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DB::commit();
|
DB::commit();
|
||||||
/* @codeCoverageIgnoreStart */
|
} catch (\Exception) {
|
||||||
} catch (Exception $ex) {
|
|
||||||
DB::rollBack();
|
DB::rollBack();
|
||||||
|
|
||||||
return redirect()->route('bonus-scores.entryBonusScoreSheet', ['entry_id' => $entry->id])->with('error', 'Error entering score - '.$ex->getMessage());
|
return redirect()->route('bonus-scores.entryBonusScoreSheet', ['entry_id' => $entry->id])->with('error', 'Error entering score - '.$ex->getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
/* @codeCoverageIgnoreEnd */
|
|
||||||
return redirect()->route('bonus-scores.entryBonusScoreSheet', ['entry_id' => $entry->id])->with('success', 'New bonus score entered');
|
return redirect()->route('bonus-scores.entryBonusScoreSheet', ['entry_id' => $entry->id])->with('success', 'New bonus score entered');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function destroyBonusScore()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,11 +34,7 @@ class DoublerDecisionController extends Controller
|
||||||
// $doublerEntry->addFlag('declined');
|
// $doublerEntry->addFlag('declined');
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
try {
|
$this->decider->accept($entry);
|
||||||
$this->decider->accept($entry);
|
|
||||||
} catch (AuditionAdminException $e) {
|
|
||||||
return redirect()->back()->with('error', $e->getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
$returnMessage = $entry->student->full_name().' accepted seating in '.$entry->audition->name;
|
$returnMessage = $entry->student->full_name().' accepted seating in '.$entry->audition->name;
|
||||||
$this->clearCache($entry);
|
$this->clearCache($entry);
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,6 @@ use Illuminate\Http\Request;
|
||||||
|
|
||||||
use function to_route;
|
use function to_route;
|
||||||
|
|
||||||
/**
|
|
||||||
* Used for tabulation enter noshow menu option
|
|
||||||
*/
|
|
||||||
class EntryFlagController extends Controller
|
class EntryFlagController extends Controller
|
||||||
{
|
{
|
||||||
public function noShowSelect()
|
public function noShowSelect()
|
||||||
|
|
@ -33,11 +30,11 @@ class EntryFlagController extends Controller
|
||||||
// If any results are published, get gone
|
// If any results are published, get gone
|
||||||
if ($entry->audition->hasFlag('seats_published')) {
|
if ($entry->audition->hasFlag('seats_published')) {
|
||||||
return to_route('entry-flags.noShowSelect')->with('error',
|
return to_route('entry-flags.noShowSelect')->with('error',
|
||||||
'Cannot enter a no-show or failed-prelim for an entry in an audition where seats are published');
|
'Cannot enter a no-show for an entry in an audition where seats are published');
|
||||||
}
|
}
|
||||||
if ($entry->audition->hasFlag('advancement_published')) {
|
if ($entry->audition->hasFlag('advancement_published')) {
|
||||||
return to_route('entry-flags.noShowSelect')->with('error',
|
return to_route('entry-flags.noShowSelect')->with('error',
|
||||||
'Cannot enter a no-show or failed-prelim for an entry in an audition where advancement is published');
|
'Cannot enter a no-show for an entry in an audition where advancement is published');
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($entry->hasFlag('no_show')) {
|
if ($entry->hasFlag('no_show')) {
|
||||||
|
|
@ -46,12 +43,6 @@ class EntryFlagController extends Controller
|
||||||
$submitRouteName = 'entry-flags.undoNoShow';
|
$submitRouteName = 'entry-flags.undoNoShow';
|
||||||
$cardHeading = 'Undo No-Show';
|
$cardHeading = 'Undo No-Show';
|
||||||
$method = 'DELETE';
|
$method = 'DELETE';
|
||||||
} elseif ($entry->hasFlag('failed_prelim')) {
|
|
||||||
$formId = 'no-show-cancellation-form';
|
|
||||||
$buttonName = 'Remove Failed Prelim';
|
|
||||||
$submitRouteName = 'entry-flags.undoNoShow';
|
|
||||||
$cardHeading = 'Undo Failed-Prelim';
|
|
||||||
$method = 'DELETE';
|
|
||||||
} else {
|
} else {
|
||||||
$formId = 'no-show-confirmation-form';
|
$formId = 'no-show-confirmation-form';
|
||||||
$buttonName = 'Confirm No Show';
|
$buttonName = 'Confirm No Show';
|
||||||
|
|
@ -94,32 +85,21 @@ class EntryFlagController extends Controller
|
||||||
{
|
{
|
||||||
if ($entry->audition->hasFlag('seats_published')) {
|
if ($entry->audition->hasFlag('seats_published')) {
|
||||||
return to_route('entry-flags.noShowSelect')->with('error',
|
return to_route('entry-flags.noShowSelect')->with('error',
|
||||||
'Cannot undo a no-show or failed-prelim for an entry in an audition where seats are published');
|
'Cannot undo a no-show for an entry in an audition where seats are published');
|
||||||
}
|
}
|
||||||
if ($entry->audition->hasFlag('advancement_published')) {
|
if ($entry->audition->hasFlag('advancement_published')) {
|
||||||
return to_route('entry-flags.noShowSelect')->with('error',
|
return to_route('entry-flags.noShowSelect')->with('error',
|
||||||
'Cannot undo a no-show or failed-prelim for an entry in an audition where advancement is published');
|
'Cannot undo a no-show for an entry in an audition where advancement is published');
|
||||||
}
|
}
|
||||||
|
|
||||||
$entry->removeFlag('no_show');
|
$entry->removeFlag('no_show');
|
||||||
$entry->removeFlag('failed_prelim');
|
|
||||||
|
|
||||||
return to_route('entry-flags.noShowSelect')->with('success',
|
return to_route('entry-flags.noShowSelect')->with('success',
|
||||||
$entry->audition->name.' #'.$entry->draw_number.' (ID: '.$entry->id.') may now be scored.');
|
'No Show status has been removed for '.$entry->audition->name.' #'.$entry->draw_number.' (ID: '.$entry->id.').');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function undoDecline(Entry $entry)
|
public function undoDecline(Entry $entry)
|
||||||
{
|
{
|
||||||
if ($entry->audition->hasFlag('seats_published')) {
|
|
||||||
return redirect()->back()
|
|
||||||
->with('error', 'Cannot undo a decline for an entry in an audition where seats are published');
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($entry->audition->hasFlag('advancement_published')) {
|
|
||||||
return redirect()->back()
|
|
||||||
->with('error', 'Cannot undo a no-show or failed-prelim for an entry in an audition where advancement is published');
|
|
||||||
}
|
|
||||||
|
|
||||||
$entry->removeFlag('declined');
|
$entry->removeFlag('declined');
|
||||||
|
|
||||||
return redirect()->back()->with('success', 'Decline cleared');
|
return redirect()->back()->with('success', 'Decline cleared');
|
||||||
|
|
|
||||||
|
|
@ -2,21 +2,16 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers\Tabulation;
|
namespace App\Http\Controllers\Tabulation;
|
||||||
|
|
||||||
use App\Actions\Tabulation\EnterPrelimScore;
|
|
||||||
use App\Actions\Tabulation\EnterScore;
|
use App\Actions\Tabulation\EnterScore;
|
||||||
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 App\Models\EntryTotalScore;
|
use App\Models\EntryTotalScore;
|
||||||
use App\Models\PrelimScoreSheet;
|
|
||||||
use App\Models\ScoreSheet;
|
use App\Models\ScoreSheet;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Session;
|
use Illuminate\Support\Facades\Session;
|
||||||
|
|
||||||
/**
|
|
||||||
* Provides functionality for entering judge scores from the admin side.
|
|
||||||
*/
|
|
||||||
class ScoreController extends Controller
|
class ScoreController extends Controller
|
||||||
{
|
{
|
||||||
public function chooseEntry()
|
public function chooseEntry()
|
||||||
|
|
@ -30,6 +25,7 @@ class ScoreController extends Controller
|
||||||
|
|
||||||
public function destroyScore(ScoreSheet $score)
|
public function destroyScore(ScoreSheet $score)
|
||||||
{
|
{
|
||||||
|
EntryTotalScore::where('entry_id', $score->entry_id)->delete();
|
||||||
if ($score->entry->audition->hasFlag('seats_published')) {
|
if ($score->entry->audition->hasFlag('seats_published')) {
|
||||||
return redirect()->back()->with('error', 'Cannot delete scores for an entry where seats are published');
|
return redirect()->back()->with('error', 'Cannot delete scores for an entry where seats are published');
|
||||||
}
|
}
|
||||||
|
|
@ -37,7 +33,6 @@ class ScoreController extends Controller
|
||||||
return redirect()->back()->with('error',
|
return redirect()->back()->with('error',
|
||||||
'Cannot delete scores for an entry where advancement is published');
|
'Cannot delete scores for an entry where advancement is published');
|
||||||
}
|
}
|
||||||
EntryTotalScore::where('entry_id', $score->entry_id)->delete();
|
|
||||||
$score->delete();
|
$score->delete();
|
||||||
|
|
||||||
return redirect()->back()->with('success', 'Score Deleted');
|
return redirect()->back()->with('success', 'Score Deleted');
|
||||||
|
|
@ -46,7 +41,7 @@ class ScoreController extends Controller
|
||||||
public function entryScoreSheet(Request $request)
|
public function entryScoreSheet(Request $request)
|
||||||
{
|
{
|
||||||
$existing_sheets = [];
|
$existing_sheets = [];
|
||||||
$entry = Entry::with(['student', 'audition.room.judges'])->findOrFail($request->input('entry_id'));
|
$entry = Entry::with(['student', 'audition.room.judges'])->find($request->input('entry_id'));
|
||||||
|
|
||||||
$publishedCheck = $this->checkIfPublished($entry);
|
$publishedCheck = $this->checkIfPublished($entry);
|
||||||
if ($publishedCheck) {
|
if ($publishedCheck) {
|
||||||
|
|
@ -63,31 +58,16 @@ class ScoreController extends Controller
|
||||||
}
|
}
|
||||||
$scoring_guide = $entry->audition->scoringGuide;
|
$scoring_guide = $entry->audition->scoringGuide;
|
||||||
$subscores = $entry->audition->scoringGuide->subscores->sortBy('display_order');
|
$subscores = $entry->audition->scoringGuide->subscores->sortBy('display_order');
|
||||||
|
if (! $entry) {
|
||||||
|
return redirect()->route('tabulation.chooseEntry')->with('error', 'Entry not found');
|
||||||
|
}
|
||||||
if ($entry->hasFlag('no_show')) {
|
if ($entry->hasFlag('no_show')) {
|
||||||
session()->flash('error',
|
session()->flash('error',
|
||||||
'This entry is marked as a no-show. Entering a score will remove the no-show flag');
|
'This entry is marked as a no-show. Entering a score will remove the no-show flag');
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($entry->audition->prelimDefinition) {
|
|
||||||
$existing_prelim_sheets = [];
|
|
||||||
$prelim_subscores = $entry->audition->prelimDefinition->scoringGuide->subscores->sortBy('display_order');
|
|
||||||
$prelim_judges = $entry->audition->prelimDefinition->room->judges;
|
|
||||||
foreach ($prelim_judges as $judge) {
|
|
||||||
$prelim_scoreSheet = PrelimScoreSheet::where('entry_id', $entry->id)->where('user_id', $judge->id)->first();
|
|
||||||
if ($prelim_scoreSheet) {
|
|
||||||
Session::flash('caution', 'Prelim scores exist for this entry. Now editing existing scores');
|
|
||||||
$existing_prelim_sheets[$judge->id] = $prelim_scoreSheet;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$prelim_subscores = null;
|
|
||||||
$prelim_judges = null;
|
|
||||||
$existing_prelim_sheets = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return view('tabulation.entry_score_sheet',
|
return view('tabulation.entry_score_sheet',
|
||||||
compact('entry', 'judges', 'scoring_guide', 'subscores', 'existing_sheets', 'prelim_subscores', 'prelim_judges', 'existing_prelim_sheets'));
|
compact('entry', 'judges', 'scoring_guide', 'subscores', 'existing_sheets'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function saveEntryScoreSheet(Request $request, Entry $entry, EnterScore $scoreRecorder)
|
public function saveEntryScoreSheet(Request $request, Entry $entry, EnterScore $scoreRecorder)
|
||||||
|
|
@ -96,32 +76,20 @@ class ScoreController extends Controller
|
||||||
if ($publishedCheck) {
|
if ($publishedCheck) {
|
||||||
return $publishedCheck;
|
return $publishedCheck;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Here we process the submission from the scoring form.
|
|
||||||
* We're expecting submitted data to include an array for each judge.
|
|
||||||
* Each array should be called judge+ the judges ID number
|
|
||||||
* The array should have a key for each subscore and the value of the score submitted
|
|
||||||
*/
|
|
||||||
foreach ($request->all() as $key => $value) {
|
foreach ($request->all() as $key => $value) {
|
||||||
// We're not interested in submission values that don't have judge in the name
|
|
||||||
if (! str_contains($key, 'judge')) {
|
if (! str_contains($key, 'judge')) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// Extract the judge ID from the field name and load the user
|
|
||||||
$judge_id = str_replace('judge', '', $key);
|
$judge_id = str_replace('judge', '', $key);
|
||||||
$judge = User::find($judge_id);
|
$judge = User::find($judge_id);
|
||||||
|
|
||||||
// Check for existing scores, if so, tell EnterScores action that we're updating it, otherwise a new score
|
|
||||||
$existingScore = ScoreSheet::where('entry_id', $entry->id)
|
$existingScore = ScoreSheet::where('entry_id', $entry->id)
|
||||||
->where('user_id', $judge->id)->first();
|
->where('user_id', $judge->id)->first();
|
||||||
if ($existingScore === null) {
|
if ($existingScore === null) {
|
||||||
$existingScore = false;
|
$existingScore = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$scoreRecorder($judge, $entry, $value, $existingScore);
|
$scoreRecorder($judge, $entry, $value, $existingScore);
|
||||||
} catch (AuditionAdminException $e) {
|
} catch (ScoreEntryException $e) {
|
||||||
return redirect()->route('scores.entryScoreSheet', ['entry_id' => $entry->id])
|
return redirect()->route('scores.entryScoreSheet', ['entry_id' => $entry->id])
|
||||||
->with('error', $e->getMessage());
|
->with('error', $e->getMessage());
|
||||||
}
|
}
|
||||||
|
|
@ -133,50 +101,6 @@ class ScoreController extends Controller
|
||||||
return redirect()->route('scores.chooseEntry')->with('success', 'Scores saved');
|
return redirect()->route('scores.chooseEntry')->with('success', 'Scores saved');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function savePrelimEntryScoreSheet(Request $request, Entry $entry, EnterPrelimScore $scoreRecorder)
|
|
||||||
{
|
|
||||||
$publishedCheck = $this->checkIfPublished($entry);
|
|
||||||
if ($publishedCheck) {
|
|
||||||
return $publishedCheck;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Here we process the submission from the scoring form.
|
|
||||||
* We're expecting submitted data to include an array for each judge.
|
|
||||||
* Each array should be called judge+ the judges ID number
|
|
||||||
* The array should have a key for each subscore and the value of the score submitted
|
|
||||||
*/
|
|
||||||
foreach ($request->all() as $key => $value) {
|
|
||||||
// We're not interested in submission values that don't have judge in the name
|
|
||||||
if (! str_contains($key, 'judge')) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// Extract the judge ID from the field name and load the user
|
|
||||||
$judge_id = str_replace('judge', '', $key);
|
|
||||||
$judge = User::find($judge_id);
|
|
||||||
|
|
||||||
// Check for existing scores, if so, tell EnterScores action that we're updating it, otherwise a new score
|
|
||||||
$existingScore = PrelimScoreSheet::where('entry_id', $entry->id)
|
|
||||||
->where('user_id', $judge->id)->first();
|
|
||||||
if ($existingScore === null) {
|
|
||||||
$existingScore = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
$scoreRecorder($judge, $entry, $value, $existingScore);
|
|
||||||
} catch (AuditionAdminException $e) {
|
|
||||||
return redirect()->route('scores.entryScoreSheet', ['entry_id' => $entry->id])
|
|
||||||
->with('error', $e->getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Since we're entering a score, this apparently isn't a no show.
|
|
||||||
$entry->removeFlag('no_show');
|
|
||||||
|
|
||||||
return redirect()->route('scores.chooseEntry')->with('success', 'Prelim Scores Saved');
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function checkIfPublished($entry)
|
protected function checkIfPublished($entry)
|
||||||
{
|
{
|
||||||
// We're not going to enter scores if seats are published
|
// We're not going to enter scores if seats are published
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,302 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Tabulation;
|
||||||
|
|
||||||
|
use App\Actions\Tabulation\GetAuditionSeats;
|
||||||
|
use App\Actions\Tabulation\RankAuditionEntries;
|
||||||
|
use App\Exceptions\AuditionAdminException;
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Models\Audition;
|
||||||
|
use App\Models\Doubler;
|
||||||
|
use App\Models\Ensemble;
|
||||||
|
use App\Models\Entry;
|
||||||
|
use App\Models\Seat;
|
||||||
|
use Debugbar;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Arr;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
|
||||||
|
use function redirect;
|
||||||
|
|
||||||
|
class SeatAuditionFormController extends Controller
|
||||||
|
{
|
||||||
|
public function showForm(Request $request, Audition $audition)
|
||||||
|
{
|
||||||
|
$seatingProposal = (session('proposedSeatingArray-'.$audition->id));
|
||||||
|
if ($audition->hasFlag('seats_published')) {
|
||||||
|
$publishedSeats = Seat::where('audition_id', $audition->id)
|
||||||
|
->join('ensembles', 'seats.ensemble_id', '=', 'ensembles.id')
|
||||||
|
->orderBy('ensembles.rank')
|
||||||
|
->orderBy('seats.seat')
|
||||||
|
->select('seats.*')
|
||||||
|
->with(['ensemble', 'entry.student.school'])
|
||||||
|
->get();
|
||||||
|
} else {
|
||||||
|
$publishedSeats = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$ranker = app(RankAuditionEntries::class);
|
||||||
|
// Get scored entries in order
|
||||||
|
$scored_entries = $ranker($audition, 'seating');
|
||||||
|
$scored_entries->load(['student.doublers', 'student.school']);
|
||||||
|
// Get unscored entries sorted by draw number
|
||||||
|
$unscored_entries = $audition->entries()
|
||||||
|
->whereDoesntHave('totalScore')
|
||||||
|
->whereDoesntHave('flags', function ($query) {
|
||||||
|
$query->where('flag_name', 'no_show');
|
||||||
|
})
|
||||||
|
->whereDoesntHave('flags', function ($query) {
|
||||||
|
$query->where('flag_name', 'failed_prelim');
|
||||||
|
})
|
||||||
|
->with('student.school')
|
||||||
|
->orderBy('draw_number', 'asc')
|
||||||
|
->get();
|
||||||
|
|
||||||
|
// Get no show entries sorted by draw number
|
||||||
|
$noshow_entries = $audition->entries()
|
||||||
|
->whereDoesntHave('totalScore')
|
||||||
|
->whereHas('flags', function ($query) {
|
||||||
|
$query->where('flag_name', 'no_show');
|
||||||
|
})
|
||||||
|
->with('student.school')
|
||||||
|
->orderBy('draw_number', 'asc')
|
||||||
|
->get();
|
||||||
|
|
||||||
|
// Get failed prelim entries sorted by draw number
|
||||||
|
$failed_prelim_entries = $audition->entries()
|
||||||
|
->whereDoesntHave('totalScore')
|
||||||
|
->whereHas('flags', function ($query) {
|
||||||
|
$query->where('flag_name', 'failed_prelim');
|
||||||
|
})
|
||||||
|
->with('student.school')
|
||||||
|
->orderBy('draw_number', 'asc')
|
||||||
|
->get();
|
||||||
|
|
||||||
|
// Get Doublers
|
||||||
|
$doublerData = Doubler::where('event_id', $audition->event_id)
|
||||||
|
->whereIn('student_id', $scored_entries->pluck('student_id'))
|
||||||
|
->get()
|
||||||
|
->keyBy('student_id');
|
||||||
|
|
||||||
|
$auditionHasUnresolvedDoublers = false;
|
||||||
|
foreach ($doublerData as $doubler) {
|
||||||
|
if (! is_null($doubler->accepted_entry)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
foreach ($doubler->entries() as $entry) {
|
||||||
|
if ($entry->audition_id === $audition->id && $entry->hasFlag('declined')) {
|
||||||
|
continue 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$auditionHasUnresolvedDoublers = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$canSeat = ! $auditionHasUnresolvedDoublers && $unscored_entries->count() === 0;
|
||||||
|
|
||||||
|
return view('tabulation.auditionSeating',
|
||||||
|
compact('audition',
|
||||||
|
'scored_entries',
|
||||||
|
'unscored_entries',
|
||||||
|
'noshow_entries',
|
||||||
|
'failed_prelim_entries',
|
||||||
|
'doublerData',
|
||||||
|
'auditionHasUnresolvedDoublers',
|
||||||
|
'canSeat',
|
||||||
|
'seatingProposal',
|
||||||
|
'publishedSeats',
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function declineSeat(Audition $audition, Entry $entry)
|
||||||
|
{
|
||||||
|
$entry->addFlag('declined');
|
||||||
|
Cache::forget('rank_seating_'.$entry->audition_id);
|
||||||
|
|
||||||
|
return redirect()->route('seating.audition', ['audition' => $audition->id])->with('success',
|
||||||
|
$entry->student->full_name().' has declined '.$audition->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function massDecline(Audition $audition)
|
||||||
|
{
|
||||||
|
$validData = request()->validate([
|
||||||
|
'decline-below' => ['required', 'integer', 'min:0'],
|
||||||
|
]);
|
||||||
|
$ranker = app(RankAuditionEntries::class);
|
||||||
|
// Get scored entries in order
|
||||||
|
$scored_entries = $ranker($audition, 'seating');
|
||||||
|
$scored_entries->load(['student.doublers', 'student.school']);
|
||||||
|
foreach ($scored_entries as $entry) {
|
||||||
|
Debugbar::info('Starting entry '.$entry->student->full_name());
|
||||||
|
if ($entry->hasFlag('declined')) {
|
||||||
|
Debugbar::info('Skipping '.$entry->student->full_name().' because they have already been declined');
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (! $entry->student->isDoublerInEvent($audition->event_id)) {
|
||||||
|
Debugbar::info('Skipping '.$entry->student->full_name().' because they are not a doubler');
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ($entry->student->doublers->where('event_id', $audition->event_id)->first()->accepted_entry) {
|
||||||
|
Debugbar::info('Skipping '.$entry->student->full_name().' because they have already accepted a seat');
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$entry->addFlag('declined');
|
||||||
|
}
|
||||||
|
Cache::forget('rank_seating_'.$entry->audition_id);
|
||||||
|
|
||||||
|
return redirect()->route('seating.audition', ['audition' => $audition->id]);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function acceptSeat(
|
||||||
|
Audition $audition,
|
||||||
|
Entry $entry
|
||||||
|
) {
|
||||||
|
$doublerData = Doubler::findDoubler($entry->student_id, $audition->event_id);
|
||||||
|
foreach ($doublerData->entries() as $doublerEntry) {
|
||||||
|
if (! $doublerEntry->totalScore && ! $doublerEntry->hasFlag('declined') && ! $doublerEntry->hasFlag('no_show') && ! $doublerEntry->hasFlag('failed_prelim')) {
|
||||||
|
return redirect()->route('seating.audition', ['audition' => $audition->id])->with('error',
|
||||||
|
'Cannot accept seating for '.$entry->student->full_name().' because student has unscored 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')) {
|
||||||
|
$doublerEntry->addFlag('declined');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return redirect()->route('seating.audition', ['audition' => $audition->id])->with('success',
|
||||||
|
$entry->student->full_name().' has accepted '.$audition->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function noshow(
|
||||||
|
Audition $audition,
|
||||||
|
Entry $entry
|
||||||
|
) {
|
||||||
|
$recorder = app('App\Actions\Tabulation\EnterNoShow');
|
||||||
|
try {
|
||||||
|
$msg = $recorder($entry);
|
||||||
|
} catch (AuditionAdminException $e) {
|
||||||
|
return redirect()->back()->with('error', $e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return redirect()->route('seating.audition', [$audition])->with('success', $msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function draftSeats(
|
||||||
|
Audition $audition,
|
||||||
|
Request $request
|
||||||
|
) {
|
||||||
|
$ranker = app(RankAuditionEntries::class);
|
||||||
|
$validated = $request->validate([
|
||||||
|
'ensemble' => ['required', 'array'],
|
||||||
|
'ensemble.*' => ['required', 'integer', 'min:0'],
|
||||||
|
]);
|
||||||
|
$proposedSeatingArray = [];
|
||||||
|
$rankedEntries = $ranker($audition, 'seating');
|
||||||
|
$rankedEntries = $rankedEntries->reject(function ($entry) {
|
||||||
|
return $entry->hasFlag('declined');
|
||||||
|
});
|
||||||
|
|
||||||
|
$rankedEntries->load(['student.school']);
|
||||||
|
$rankedEnembles = Ensemble::orderBy('rank')->where('event_id', $audition->event_id)->get();
|
||||||
|
$ensembleRankOn = 1;
|
||||||
|
foreach ($rankedEnembles as $ensemble) {
|
||||||
|
if (! Arr::has($validated['ensemble'], $ensemble->id)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$proposedSeatingArray[$ensembleRankOn]['ensemble_id'] = $ensemble->id;
|
||||||
|
$proposedSeatingArray[$ensembleRankOn]['ensemble_name'] = $ensemble->name;
|
||||||
|
$proposedSeatingArray[$ensembleRankOn]['accept_count'] = $validated['ensemble'][$ensemble->id];
|
||||||
|
for ($n = 1; $n <= $validated['ensemble'][$ensemble->id]; $n++) {
|
||||||
|
// Escape the loop if we're out of entries
|
||||||
|
if ($rankedEntries->isEmpty()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$thisEntry = $rankedEntries->shift();
|
||||||
|
$proposedSeatingArray[$ensembleRankOn]['seats'][$n]['seat'] = $n;
|
||||||
|
$proposedSeatingArray[$ensembleRankOn]['seats'][$n]['entry_id'] = $thisEntry->id;
|
||||||
|
$proposedSeatingArray[$ensembleRankOn]['seats'][$n]['entry_name'] = $thisEntry->student->full_name();
|
||||||
|
$proposedSeatingArray[$ensembleRankOn]['seats'][$n]['entry_school'] = $thisEntry->student->school->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
$ensembleRankOn++;
|
||||||
|
}
|
||||||
|
$sessionKeyName = 'proposedSeatingArray-'.$audition->id;
|
||||||
|
$request->session()->put($sessionKeyName, $proposedSeatingArray, 10);
|
||||||
|
|
||||||
|
return redirect()->route('seating.audition', ['audition' => $audition->id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function clearDraft(
|
||||||
|
Audition $audition
|
||||||
|
) {
|
||||||
|
session()->forget('proposedSeatingArray-'.$audition->id);
|
||||||
|
|
||||||
|
return redirect()->route('seating.audition', ['audition' => $audition->id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function publishSeats(
|
||||||
|
Audition $audition
|
||||||
|
) {
|
||||||
|
$publisher = app('App\Actions\Tabulation\PublishSeats');
|
||||||
|
$seatingProposal = (session('proposedSeatingArray-'.$audition->id));
|
||||||
|
$proposal = [];
|
||||||
|
foreach ($seatingProposal as $ensemble) {
|
||||||
|
$ensembleId = $ensemble['ensemble_id'];
|
||||||
|
if (isset($ensemble['seats'])) {
|
||||||
|
foreach ($ensemble['seats'] as $seat) {
|
||||||
|
$proposal[] = [
|
||||||
|
'ensemble_id' => $ensembleId,
|
||||||
|
'audition_id' => $audition->id,
|
||||||
|
'seat' => $seat['seat'],
|
||||||
|
'entry_id' => $seat['entry_id'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$publisher($audition, $proposal);
|
||||||
|
session()->forget('proposedSeatingArray-'.$audition->id);
|
||||||
|
|
||||||
|
return redirect()->route('seating.audition', [$audition]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function unpublishSeats(
|
||||||
|
Audition $audition
|
||||||
|
) {
|
||||||
|
$unpublisher = app('App\Actions\Tabulation\UnpublishSeats');
|
||||||
|
$unpublisher($audition);
|
||||||
|
session()->forget('proposedSeatingArray-'.$audition->id);
|
||||||
|
|
||||||
|
return redirect()->route('seating.audition', [$audition]);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function pickRightPanel(
|
||||||
|
Audition $audition,
|
||||||
|
array $seatable
|
||||||
|
) {
|
||||||
|
if ($audition->hasFlag('seats_published')) {
|
||||||
|
$resultsWindow = new GetAuditionSeats;
|
||||||
|
$rightPanel['view'] = 'tabulation.auditionSeating-show-published-seats';
|
||||||
|
$rightPanel['data'] = $resultsWindow($audition);
|
||||||
|
|
||||||
|
return $rightPanel;
|
||||||
|
}
|
||||||
|
if ($seatable['allScored'] == false || $seatable['doublersResolved'] == false) {
|
||||||
|
$rightPanel['view'] = 'tabulation.auditionSeating-unable-to-seat-card';
|
||||||
|
$rightPanel['data'] = $seatable;
|
||||||
|
|
||||||
|
return $rightPanel;
|
||||||
|
}
|
||||||
|
|
||||||
|
$rightPanel['view'] = 'tabulation.auditionSeating-right-complete-not-published';
|
||||||
|
$rightPanel['data'] = $this->auditionService->getSeatingLimits($audition);
|
||||||
|
|
||||||
|
return $rightPanel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,180 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Tabulation;
|
||||||
|
|
||||||
|
use App\Actions\Entries\DoublerDecision;
|
||||||
|
use App\Actions\Tabulation\CalculateEntryScore;
|
||||||
|
use App\Actions\Tabulation\GetAuditionSeats;
|
||||||
|
use App\Actions\Tabulation\RankAuditionEntries;
|
||||||
|
use App\Exceptions\AuditionAdminException;
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Models\Audition;
|
||||||
|
use App\Services\AuditionService;
|
||||||
|
use App\Services\DoublerService;
|
||||||
|
use App\Services\EntryService;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
|
||||||
|
use function redirect;
|
||||||
|
|
||||||
|
class SeatAuditionFormControllerOLD extends Controller
|
||||||
|
{
|
||||||
|
protected CalculateEntryScore $calc;
|
||||||
|
protected DoublerService $doublerService;
|
||||||
|
protected RankAuditionEntries $ranker;
|
||||||
|
protected EntryService $entryService;
|
||||||
|
protected AuditionService $auditionService;
|
||||||
|
protected DoublerDecision $decider;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
CalculateEntryScore $calc,
|
||||||
|
RankAuditionEntries $ranker,
|
||||||
|
DoublerService $doublerService,
|
||||||
|
EntryService $entryService,
|
||||||
|
AuditionService $auditionService,
|
||||||
|
DoublerDecision $decider,
|
||||||
|
) {
|
||||||
|
$this->calc = $calc;
|
||||||
|
$this->ranker = $ranker;
|
||||||
|
$this->doublerService = $doublerService;
|
||||||
|
$this->entryService = $entryService;
|
||||||
|
$this->auditionService = $auditionService;
|
||||||
|
$this->decider = $decider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __invoke(Request $request, Audition $audition)
|
||||||
|
{
|
||||||
|
// If a seating proposal was posted, deal wth it
|
||||||
|
if ($request->method() == 'POST' && $request->input('ensembleAccept')) {
|
||||||
|
$requestedEnsembleAccepts = $request->input('ensembleAccept');
|
||||||
|
} else {
|
||||||
|
$requestedEnsembleAccepts = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deal with a mass no-show request
|
||||||
|
if ($request->input('mass-no-show')) {
|
||||||
|
$entries = $audition->entries()->forSeating()->withCount('scoreSheets')->with('flags')->get();
|
||||||
|
foreach ($entries as $entry) {
|
||||||
|
if ($entry->scoreSheets_count == 0 && ! $entry->hasFlag('no_show')) {
|
||||||
|
$entry->addFlag('no_show');
|
||||||
|
}
|
||||||
|
Cache::forget('entryScore-'.$entry->id.'-seating');
|
||||||
|
Cache::forget('entryScore-'.$entry->id.'-advancement');
|
||||||
|
}
|
||||||
|
Cache::forget('audition'.$audition->id.'seating');
|
||||||
|
Cache::forget('audition'.$audition->id.'advancement');
|
||||||
|
}
|
||||||
|
|
||||||
|
$entryData = [];
|
||||||
|
$entries = $this->ranker->rank('seating', $audition);
|
||||||
|
|
||||||
|
// Deal with mass decline doubler request
|
||||||
|
if ($request->input('decline-below')) {
|
||||||
|
Cache::forget('audition'.$audition->id.'seating');
|
||||||
|
|
||||||
|
$changes_made = false;
|
||||||
|
foreach ($entries as $entry) {
|
||||||
|
$doublerData = $this->doublerService->entryDoublerData($entry);
|
||||||
|
if ($doublerData && ! $entry->hasFlag('declined') && $entry->rank > $request->input('decline-below')) {
|
||||||
|
try {
|
||||||
|
$this->decider->decline($entry);
|
||||||
|
$changes_made = true;
|
||||||
|
} catch (AuditionAdminException $e) {
|
||||||
|
return redirect()->back()->with('error', $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($changes_made) {
|
||||||
|
$cache_key = 'event'.$audition->event_id.'doublers-seating';
|
||||||
|
Cache::forget($cache_key);
|
||||||
|
|
||||||
|
return redirect()->back();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$entries->load('student.school');
|
||||||
|
$entries->load('student.doublerRequests');
|
||||||
|
$seatable = [
|
||||||
|
'allScored' => true,
|
||||||
|
'doublersResolved' => true,
|
||||||
|
];
|
||||||
|
foreach ($entries as $entry) {
|
||||||
|
$totalScoreColumn = 'No Score';
|
||||||
|
$fullyScored = false;
|
||||||
|
if ($entry->score_totals) {
|
||||||
|
$totalScoreColumn = $entry->score_totals[0] >= 0 ? $entry->score_totals[0] : $entry->score_message;
|
||||||
|
$fullyScored = $entry->score_totals[0] >= 0;
|
||||||
|
}
|
||||||
|
// No Shows are fully scored
|
||||||
|
if ($entry->hasFlag('no_show')) {
|
||||||
|
$fullyScored = true;
|
||||||
|
}
|
||||||
|
$doublerData = $this->doublerService->entryDoublerData($entry);
|
||||||
|
|
||||||
|
$entryData[] = [
|
||||||
|
'rank' => $entry->rank,
|
||||||
|
'id' => $entry->id,
|
||||||
|
'studentName' => $entry->student->full_name(),
|
||||||
|
'schoolName' => $entry->student->school->name,
|
||||||
|
'drawNumber' => $entry->draw_number,
|
||||||
|
'totalScore' => $totalScoreColumn,
|
||||||
|
'fullyScored' => $fullyScored,
|
||||||
|
'hasBonusScores' => $entry->bonus_scores_count > 0,
|
||||||
|
'doubleData' => $doublerData,
|
||||||
|
'doublerRequest' => $entry->student->doublerRequests()->where('event_id',
|
||||||
|
$audition->event_id)->first()?->request,
|
||||||
|
];
|
||||||
|
// If this entries double decision isn't made, block seating
|
||||||
|
if ($doublerData && $doublerData[$entry->id]['status'] == 'undecided') {
|
||||||
|
$seatable['doublersResolved'] = false;
|
||||||
|
}
|
||||||
|
// If entry is unscored, block seating
|
||||||
|
if (! $fullyScored) {
|
||||||
|
$seatable['allScored'] = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$rightPanel = $this->pickRightPanel($audition, $seatable);
|
||||||
|
$seatableEntries = [];
|
||||||
|
if ($seatable['doublersResolved'] && $seatable['allScored']) {
|
||||||
|
$seatableEntries = $entries->reject(function ($entry) {
|
||||||
|
if ($entry->hasFlag('declined')) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if ($entry->hasFlag('no_show')) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if ($entry->hasFlag('failed_prelim')) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return view('tabulation.auditionSeating',
|
||||||
|
compact('entryData', 'audition', 'rightPanel', 'seatableEntries', 'requestedEnsembleAccepts'));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function pickRightPanel(Audition $audition, array $seatable)
|
||||||
|
{
|
||||||
|
if ($audition->hasFlag('seats_published')) {
|
||||||
|
$resultsWindow = new GetAuditionSeats;
|
||||||
|
$rightPanel['view'] = 'tabulation.auditionSeating-show-published-seats';
|
||||||
|
$rightPanel['data'] = $resultsWindow($audition);
|
||||||
|
|
||||||
|
return $rightPanel;
|
||||||
|
}
|
||||||
|
if ($seatable['allScored'] == false || $seatable['doublersResolved'] == false) {
|
||||||
|
$rightPanel['view'] = 'tabulation.auditionSeating-unable-to-seat-card';
|
||||||
|
$rightPanel['data'] = $seatable;
|
||||||
|
|
||||||
|
return $rightPanel;
|
||||||
|
}
|
||||||
|
|
||||||
|
$rightPanel['view'] = 'tabulation.auditionSeating-right-complete-not-published';
|
||||||
|
$rightPanel['data'] = $this->auditionService->getSeatingLimits($audition);
|
||||||
|
|
||||||
|
return $rightPanel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,99 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Controllers\Tabulation\Seating;
|
|
||||||
|
|
||||||
use App\Actions\Entries\DoublerDecision;
|
|
||||||
use App\Actions\Tabulation\RankAuditionEntries;
|
|
||||||
use App\Exceptions\AuditionAdminException;
|
|
||||||
use App\Http\Controllers\Controller;
|
|
||||||
use App\Models\Audition;
|
|
||||||
use App\Models\Entry;
|
|
||||||
use Illuminate\Support\Facades\Cache;
|
|
||||||
|
|
||||||
use function redirect;
|
|
||||||
|
|
||||||
class EnterDoublerDecisionsController extends Controller
|
|
||||||
{
|
|
||||||
public function noshow(
|
|
||||||
Audition $audition,
|
|
||||||
Entry $entry
|
|
||||||
) {
|
|
||||||
$recorder = app('App\Actions\Tabulation\EnterNoShow');
|
|
||||||
try {
|
|
||||||
$msg = $recorder($entry);
|
|
||||||
} catch (AuditionAdminException $e) {
|
|
||||||
return redirect()->back()->with('error', $e->getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
return redirect()->route('seating.audition', [$audition])->with('success', $msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function declineSeat(Audition $audition, Entry $entry)
|
|
||||||
{
|
|
||||||
$decider = app(DoublerDecision::class);
|
|
||||||
try {
|
|
||||||
$decider->decline($entry);
|
|
||||||
} catch (AuditionAdminException $e) {
|
|
||||||
return redirect()->route('seating.audition', ['audition' => $audition->id])
|
|
||||||
->with('error', $e->getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
return redirect()->route('seating.audition', ['audition' => $audition->id])->with('success',
|
|
||||||
$entry->student->full_name().' has declined '.$audition->name);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function massDecline(Audition $audition)
|
|
||||||
{
|
|
||||||
$decider = app(DoublerDecision::class);
|
|
||||||
$validData = request()->validate([
|
|
||||||
'decline-below' => ['required', 'integer', 'min:0'],
|
|
||||||
]);
|
|
||||||
$ranker = app(RankAuditionEntries::class);
|
|
||||||
// Get scored entries in order
|
|
||||||
try {
|
|
||||||
$scored_entries = $ranker($audition, 'seating');
|
|
||||||
} catch (AuditionAdminException $e) {
|
|
||||||
return redirect()->route('seating.audition', ['audition' => $audition->id])
|
|
||||||
->with('error', $e->getMessage());
|
|
||||||
}
|
|
||||||
$scored_entries->load(['student.doublers', 'student.school']);
|
|
||||||
foreach ($scored_entries as $entry) {
|
|
||||||
if ($entry->seatingRank < $validData['decline-below']) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if ($entry->hasFlag('declined')) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (! $entry->student->isDoublerInEvent($audition->event_id)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if ($entry->student->doublers->where('event_id', $audition->event_id)->first()->accepted_entry) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
$decider->decline($entry);
|
|
||||||
} catch (AuditionAdminException $e) {
|
|
||||||
return redirect()->route('seating.audition', ['audition' => $audition->id])
|
|
||||||
->with('error', $e->getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Cache::forget('rank_seating_'.$audition->id);
|
|
||||||
|
|
||||||
return redirect()->route('seating.audition', ['audition' => $audition->id]);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public function acceptSeat(Audition $audition, Entry $entry)
|
|
||||||
{
|
|
||||||
$decider = app(DoublerDecision::class);
|
|
||||||
try {
|
|
||||||
$decider->accept($entry);
|
|
||||||
} catch (AuditionAdminException $e) {
|
|
||||||
return redirect()->route('seating.audition', ['audition' => $audition->id])
|
|
||||||
->with('error', $e->getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
return redirect()->route('seating.audition', ['audition' => $audition->id])->with('success',
|
|
||||||
$entry->student->full_name().' has accepted '.$audition->name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,90 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Controllers\Tabulation\Seating;
|
|
||||||
|
|
||||||
use App\Actions\Tabulation\RankAuditionEntries;
|
|
||||||
use App\Exceptions\AuditionAdminException;
|
|
||||||
use App\Http\Controllers\Controller;
|
|
||||||
use App\Models\Audition;
|
|
||||||
use App\Models\Ensemble;
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
use Illuminate\Support\Arr;
|
|
||||||
|
|
||||||
use function redirect;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Selects entries for seating in an audition and saves them to the session
|
|
||||||
* for later formal seating.
|
|
||||||
*/
|
|
||||||
class MakeSeatingDecisionsController extends Controller
|
|
||||||
{
|
|
||||||
public function draftSeats(
|
|
||||||
Audition $audition,
|
|
||||||
Request $request
|
|
||||||
) {
|
|
||||||
$ranker = app(RankAuditionEntries::class);
|
|
||||||
$validated = $request->validate([
|
|
||||||
'ensemble' => ['required', 'array'],
|
|
||||||
'ensemble.*' => ['required', 'integer', 'min:0'],
|
|
||||||
]);
|
|
||||||
$proposedSeatingArray = [];
|
|
||||||
try {
|
|
||||||
$rankedEntries = $ranker($audition, 'seating');
|
|
||||||
} catch (AuditionAdminException $e) {
|
|
||||||
return redirect()->route('seating.audition', ['audition' => $audition->id])
|
|
||||||
->with('error', $e->getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pull out entries that have declined a seat in this audition
|
|
||||||
$rankedEntries = $rankedEntries->reject(function ($entry) {
|
|
||||||
return $entry->hasFlag('declined');
|
|
||||||
});
|
|
||||||
|
|
||||||
$rankedEntries->load(['student.school']);
|
|
||||||
$rankedEnsembles = Ensemble::orderBy('rank')->where('event_id', $audition->event_id)->get();
|
|
||||||
$ensembleRankOn = 1;
|
|
||||||
|
|
||||||
// Iterate over all ensembles that exist for the event
|
|
||||||
foreach ($rankedEnsembles as $ensemble) {
|
|
||||||
// If the user didn't ask for any seats in this ensemble, skip it
|
|
||||||
if (! Arr::has($validated['ensemble'], $ensemble->id)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set up an entry in the session for each ensemble we're going to seat
|
|
||||||
$proposedSeatingArray[$ensembleRankOn]['ensemble_id'] = $ensemble->id;
|
|
||||||
$proposedSeatingArray[$ensembleRankOn]['ensemble_name'] = $ensemble->name;
|
|
||||||
$proposedSeatingArray[$ensembleRankOn]['accept_count'] = $validated['ensemble'][$ensemble->id];
|
|
||||||
|
|
||||||
// Pull the top rated entry for each seat in order
|
|
||||||
for ($n = 1; $n <= $validated['ensemble'][$ensemble->id]; $n++) {
|
|
||||||
// Escape the loop if we're out of entries
|
|
||||||
if ($rankedEntries->isEmpty()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
$thisEntry = $rankedEntries->shift();
|
|
||||||
$proposedSeatingArray[$ensembleRankOn]['seats'][$n]['seat'] = $n;
|
|
||||||
$proposedSeatingArray[$ensembleRankOn]['seats'][$n]['entry_id'] = $thisEntry->id;
|
|
||||||
$proposedSeatingArray[$ensembleRankOn]['seats'][$n]['entry_name'] = $thisEntry->student->full_name();
|
|
||||||
$proposedSeatingArray[$ensembleRankOn]['seats'][$n]['entry_school'] = $thisEntry->student->school->name;
|
|
||||||
}
|
|
||||||
|
|
||||||
$ensembleRankOn++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save the data to the session
|
|
||||||
$sessionKeyName = 'proposedSeatingArray-'.$audition->id;
|
|
||||||
$request->session()->put($sessionKeyName, $proposedSeatingArray);
|
|
||||||
|
|
||||||
return redirect()->route('seating.audition', ['audition' => $audition->id]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function clearDraft(
|
|
||||||
Audition $audition
|
|
||||||
) {
|
|
||||||
session()->forget('proposedSeatingArray-'.$audition->id);
|
|
||||||
|
|
||||||
return redirect()->route('seating.audition', ['audition' => $audition->id]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,51 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Controllers\Tabulation\Seating;
|
|
||||||
|
|
||||||
use App\Exceptions\AuditionAdminException;
|
|
||||||
use App\Http\Controllers\Controller;
|
|
||||||
use App\Models\Audition;
|
|
||||||
|
|
||||||
use function redirect;
|
|
||||||
|
|
||||||
class PublishSeatingController extends Controller
|
|
||||||
{
|
|
||||||
public function publishSeats(
|
|
||||||
Audition $audition
|
|
||||||
) {
|
|
||||||
$publisher = app('App\Actions\Tabulation\PublishSeats');
|
|
||||||
$seatingProposal = (session('proposedSeatingArray-'.$audition->id));
|
|
||||||
$proposal = [];
|
|
||||||
foreach ($seatingProposal as $ensemble) {
|
|
||||||
$ensembleId = $ensemble['ensemble_id'];
|
|
||||||
if (isset($ensemble['seats'])) {
|
|
||||||
foreach ($ensemble['seats'] as $seat) {
|
|
||||||
$proposal[] = [
|
|
||||||
'ensemble_id' => $ensembleId,
|
|
||||||
'audition_id' => $audition->id,
|
|
||||||
'seat' => $seat['seat'],
|
|
||||||
'entry_id' => $seat['entry_id'],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
$publisher($audition, $proposal);
|
|
||||||
} catch (AuditionAdminException $e) {
|
|
||||||
return redirect()->route('seating.audition', [$audition])->with('error', $e->getMessage());
|
|
||||||
}
|
|
||||||
session()->forget('proposedSeatingArray-'.$audition->id);
|
|
||||||
|
|
||||||
return redirect()->route('seating.audition', [$audition]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function unpublishSeats(
|
|
||||||
Audition $audition
|
|
||||||
) {
|
|
||||||
$unpublisher = app('App\Actions\Tabulation\UnpublishSeats');
|
|
||||||
$unpublisher($audition);
|
|
||||||
session()->forget('proposedSeatingArray-'.$audition->id);
|
|
||||||
|
|
||||||
return redirect()->route('seating.audition', [$audition]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue