End year procedures implementation #111
|
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
|
||||
namespace App\Actions\YearEndProcedures;
|
||||
|
||||
use App\Exceptions\AuditionAdminException;
|
||||
use App\Models\HistoricalSeat;
|
||||
use App\Models\Seat;
|
||||
use Carbon\Carbon;
|
||||
|
||||
class RecordHistoricalSeats
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
public function __invoke(): void
|
||||
{
|
||||
$this->saveSeats();
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws AuditionAdminException
|
||||
*/
|
||||
public function saveSeats()
|
||||
{
|
||||
if (! auth()->user() or ! auth()->user()->is_admin) {
|
||||
throw new AuditionAdminException('Only administrators may perform this action');
|
||||
}
|
||||
$seats = Seat::all();
|
||||
if ($seats->count() > 0) {
|
||||
foreach ($seats as $seat) {
|
||||
$student_id = $seat->student->id;
|
||||
$year = Carbon::now()->year;
|
||||
$seat_description = $seat->ensemble->name.' - '.$seat->audition->name.' - '.$seat->seat;
|
||||
HistoricalSeat::create([
|
||||
'student_id' => $student_id,
|
||||
'year' => $year,
|
||||
'seat_description' => $seat_description,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
<?php
|
||||
|
||||
namespace App\Actions\YearEndProcedures;
|
||||
|
||||
use App\Exceptions\AuditionAdminException;
|
||||
use App\Models\AuditionFlag;
|
||||
use App\Models\AuditLogEntry;
|
||||
use App\Models\BonusScore;
|
||||
use App\Models\CalculatedScore;
|
||||
use App\Models\DoublerRequest;
|
||||
use App\Models\EntryFlag;
|
||||
use App\Models\JudgeAdvancementVote;
|
||||
use App\Models\NominationEnsembleEntry;
|
||||
use App\Models\ScoreSheet;
|
||||
use App\Models\Seat;
|
||||
use App\Models\Student;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
use function auth;
|
||||
|
||||
class YearEndCleanup
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
public function __invoke(?array $options = []): void
|
||||
{
|
||||
$this->cleanup($options);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $options array array of reset options - possible values are deleteRooms
|
||||
* removeAuditionsFromRoom unassignJudges
|
||||
*
|
||||
* @throws AuditionAdminException
|
||||
*/
|
||||
public function cleanup(?array $options = []): true
|
||||
{
|
||||
|
||||
if (! auth()->user() or ! auth()->user()->is_admin) {
|
||||
throw new AuditionAdminException('Only administrators may perform this action');
|
||||
}
|
||||
|
||||
$historian = new RecordHistoricalSeats;
|
||||
$historian();
|
||||
|
||||
// Delete all records in the audit_log_entries table
|
||||
AuditLogEntry::truncate();
|
||||
AuditionFlag::truncate();
|
||||
BonusScore::truncate();
|
||||
CalculatedScore::truncate();
|
||||
DoublerRequest::truncate();
|
||||
EntryFlag::truncate();
|
||||
ScoreSheet::truncate();
|
||||
Seat::truncate();
|
||||
JudgeAdvancementVote::truncate();
|
||||
DB::table('entries')->delete();
|
||||
NominationEnsembleEntry::truncate();
|
||||
|
||||
Student::query()->increment('grade');
|
||||
|
||||
if (is_array($options)) {
|
||||
if (in_array('deleteRooms', $options)) {
|
||||
DB::table('auditions')->update(['room_id' => null]);
|
||||
DB::table('auditions')->update(['order_in_room' => '0']);
|
||||
DB::table('room_user')->truncate();
|
||||
DB::table('rooms')->delete();
|
||||
}
|
||||
|
||||
if (in_array('removeAuditionsFromRoom', $options)) {
|
||||
DB::table('auditions')->update(['room_id' => null]);
|
||||
DB::table('auditions')->update(['order_in_room' => '0']);
|
||||
}
|
||||
|
||||
if (in_array('unassignJudges', $options)) {
|
||||
DB::table('room_user')->truncate();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Actions\YearEndProcedures\YearEndCleanup;
|
||||
use App\Http\Controllers\Controller;
|
||||
|
||||
use function auditionLog;
|
||||
|
||||
class YearEndResetController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
return view('admin.year_end_reset');
|
||||
}
|
||||
|
||||
public function execute()
|
||||
{
|
||||
$cleanUpProcedure = new YearEndCleanup;
|
||||
$options = request()->options;
|
||||
$cleanUpProcedure($options);
|
||||
auditionLog('Executed year end reset.', []);
|
||||
|
||||
return redirect()->route('dashboard')->with('success', 'Year end reset completed');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class HistoricalSeat extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $guarded = [];
|
||||
|
||||
public function student(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Student::class);
|
||||
}
|
||||
}
|
||||
|
|
@ -45,6 +45,11 @@ class Student extends Model
|
|||
return $this->belongsTo(School::class);
|
||||
}
|
||||
|
||||
public function historicalSeats(): HasMany
|
||||
{
|
||||
return $this->hasMany(HistoricalSeat::class);
|
||||
}
|
||||
|
||||
public function users(): HasManyThrough
|
||||
{
|
||||
return $this->hasManyThrough(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
use App\Models\Student;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('historical_seats', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->timestamps();
|
||||
$table->foreignIdFor(Student::class)->constrained()->onDelete('cascade');
|
||||
$table->integer('year');
|
||||
$table->string('seat_description');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('historical_seats');
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
<x-layout.app>
|
||||
<x-layout.page-header>Year End Reset</x-layout.page-header>
|
||||
<x-card.card class="mt-5 max-w-xl m-auto">
|
||||
<x-card.heading>Reset Options</x-card.heading>
|
||||
<x-form.form action="{{ route('admin.year_end_procedures') }}">
|
||||
<x-form.checkbox name="options[]" label="Delete Rooms" value="deleteRooms" />
|
||||
<x-form.checkbox name="options[]" label="Remove Auditions From Rooms" value="removeAuditionsFromRoom" />
|
||||
<x-form.checkbox name="options[]" label="Unassign Judges" value="unassignJudges" />
|
||||
<x-form.footer class="mb-3" x-data="{ 'showModal': false }" @keydown.escape="showModal = false">
|
||||
<x-form.button type="button" @click="showModal = true">
|
||||
Complete Year End Reset
|
||||
</x-form.button>
|
||||
<x-modal-body>
|
||||
<x-slot:title>Confirm Year End Reset</x-slot:title>
|
||||
Confirm you would like to perform a year end reset. This will delete all seats, scores, entries, and log entries,
|
||||
as well as any optional data you chose. It will also increment the grade of all students in the database.
|
||||
This action will result in data loss and cannot be undone.
|
||||
<x-form.button class="mt-3">Confirm Reset</x-form.button>
|
||||
</x-modal-body>
|
||||
</x-form.footer>
|
||||
</x-form.form>
|
||||
</x-card.card>
|
||||
</x-layout.app>
|
||||
|
|
@ -32,6 +32,7 @@
|
|||
<a href="{{route('admin.export_results')}}" class="block p-2 hover:text-indigo-600">Export Results</a>
|
||||
<a href="{{route('admin.export_entries')}}" class="block p-2 hover:text-indigo-600">Export Entries</a>
|
||||
<a href="{{route('admin.print_stand_name_tags')}}" class="block p-2 hover:text-indigo-600">Print Stand Name Tags</a>
|
||||
<a href="{{route('admin.year_end_procedures')}}" class="block p-2 hover:text-indigo-600">Year End Reset</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ use App\Http\Controllers\Admin\SchoolController;
|
|||
use App\Http\Controllers\Admin\ScoringGuideController;
|
||||
use App\Http\Controllers\Admin\StudentController;
|
||||
use App\Http\Controllers\Admin\UserController;
|
||||
use App\Http\Controllers\Admin\YearEndResetController;
|
||||
use App\Http\Middleware\CheckIfAdmin;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
|
|
@ -33,6 +34,10 @@ Route::middleware(['auth', 'verified', CheckIfAdmin::class])->prefix('admin/')->
|
|||
Route::get('/recap', [RecapController::class, 'selectAudition'])->name('admin.recap.selectAudition');
|
||||
Route::get('/recap/{audition}', [RecapController::class, 'showRecap'])->name('admin.recap.recap');
|
||||
|
||||
// Year end prodecures
|
||||
Route::get('/year_end_procedures', [YearEndResetController::class, 'index'])->name('admin.year_end_procedures');
|
||||
Route::post('/year_end_procedures', [YearEndResetController::class, 'execute'])->name('admin.year_end_procedures');
|
||||
|
||||
Route::post('/auditions/roomUpdate', [
|
||||
AuditionController::class, 'roomUpdate',
|
||||
]); // Endpoint for JS assigning auditions to rooms
|
||||
|
|
|
|||
|
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
|
||||
use App\Actions\YearEndProcedures\RecordHistoricalSeats;
|
||||
use App\Exceptions\AuditionAdminException;
|
||||
use App\Models\Ensemble;
|
||||
use App\Models\Entry;
|
||||
use App\Models\HistoricalSeat;
|
||||
use App\Models\Seat;
|
||||
use App\Models\Student;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
|
||||
uses(RefreshDatabase::class);
|
||||
|
||||
it('only executes for an admin user', function () {
|
||||
$action = new RecordHistoricalSeats();
|
||||
expect(fn () => $action())->toThrow(
|
||||
AuditionAdminException::class,
|
||||
'Only administrators may perform this action'
|
||||
);
|
||||
|
||||
actAsNormal();
|
||||
expect(fn () => $action())->toThrow(
|
||||
AuditionAdminException::class,
|
||||
'Only administrators may perform this action'
|
||||
);
|
||||
|
||||
actAsAdmin();
|
||||
expect($action->saveSeats())->toBeTrue();
|
||||
|
||||
});
|
||||
|
||||
it('saves a seated student to the historical table', function () {
|
||||
actAsAdmin();
|
||||
$entry = Entry::factory()->create();
|
||||
Entry::factory(5)->create();
|
||||
$action = new RecordHistoricalSeats();
|
||||
$ensemble = Ensemble::create([
|
||||
'event_id' => $entry->audition->event_id,
|
||||
'name' => 'Test Ensemble',
|
||||
'code' => 'te',
|
||||
'rank' => 1,
|
||||
]);
|
||||
$seat = Seat::create([
|
||||
'ensemble_id' => $ensemble->id,
|
||||
'audition_id' => $entry->audition_id,
|
||||
'seat' => '1',
|
||||
'entry_id' => $entry->id,
|
||||
]);
|
||||
$action->saveSeats();
|
||||
$historical_seats = HistoricalSeat::all();
|
||||
$test_seat = $historical_seats->first();
|
||||
expect($test_seat->student_id)->toBe($entry->student_id)
|
||||
->and($historical_seats)->toHaveCount(1)
|
||||
->and($test_seat->seat_description)->toBe($ensemble->name.' - '.$entry->audition->name.' - '.$seat->seat)
|
||||
->and(Student::count())->toBe(6);
|
||||
});
|
||||
Loading…
Reference in New Issue