Merge branch 'bonusScoreRewrite' into scoringRewrite
This commit is contained in:
commit
f2cb96dc0d
|
|
@ -6,7 +6,6 @@ namespace App\Actions\Tabulation;
|
|||
|
||||
use App\Exceptions\ScoreEntryException;
|
||||
use App\Models\BonusScore;
|
||||
use App\Models\CalculatedScore;
|
||||
use App\Models\Entry;
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
|
|
@ -29,7 +28,6 @@ class EnterBonusScore
|
|||
// Create the score for each related entry
|
||||
foreach ($entries as $relatedEntry) {
|
||||
// Also delete any cached scores
|
||||
CalculatedScore::where('entry_id', $relatedEntry->id)->delete();
|
||||
BonusScore::create([
|
||||
'entry_id' => $relatedEntry->id,
|
||||
'user_id' => $judge->id,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace App\Actions\Tabulation;
|
||||
|
||||
use App\Models\Entry;
|
||||
|
||||
class ForceRecalculateTotalScores
|
||||
{
|
||||
public function __invoke(): void
|
||||
{
|
||||
$calculator = app(TotalEntryScores::class);
|
||||
foreach (Entry::all() as $entry) {
|
||||
$calculator($entry, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -42,13 +42,19 @@ class RankAuditionEntries
|
|||
|
||||
private function get_seating_ranks(Audition $audition): Collection
|
||||
{
|
||||
if ($audition->bonusScore()->count() > 0) {
|
||||
$totalColumn = 'seating_total_with_bonus';
|
||||
} else {
|
||||
$totalColumn = 'seating_total';
|
||||
}
|
||||
|
||||
$sortedEntries = $audition->entries()
|
||||
->whereHas('totalScore')
|
||||
->with('totalScore')
|
||||
->with('student.school')
|
||||
->with('audition')
|
||||
->join('entry_total_scores', 'entries.id', '=', 'entry_total_scores.entry_id')
|
||||
->orderBy('entry_total_scores.seating_total', 'desc')
|
||||
->orderBy('entry_total_scores.'.$totalColumn, 'desc')
|
||||
->orderByRaw('COALESCE(JSON_EXTRACT(entry_total_scores.seating_subscore_totals, "$[0]"), -999999) DESC')
|
||||
->orderByRaw('COALESCE(JSON_EXTRACT(entry_total_scores.seating_subscore_totals, "$[1]"), -999999) DESC')
|
||||
->orderByRaw('COALESCE(JSON_EXTRACT(entry_total_scores.seating_subscore_totals, "$[2]"), -999999) DESC')
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace App\Actions\Tabulation;
|
||||
|
||||
use App\Models\BonusScore;
|
||||
use App\Models\Entry;
|
||||
use App\Models\EntryTotalScore;
|
||||
use App\Models\ScoreSheet;
|
||||
|
|
@ -75,6 +76,14 @@ class TotalEntryScores
|
|||
$total_advancement_subscores[] = $runningTotal / $scoreSheets->count();
|
||||
}
|
||||
$newTotaledScore->advancement_subscore_totals = $total_advancement_subscores;
|
||||
|
||||
// pull in bonus scores
|
||||
$bonusScores = BonusScore::where('entry_id', $entry->id)
|
||||
->selectRaw('SUM(score) as total')
|
||||
->value('total');
|
||||
|
||||
$newTotaledScore->bonus_total = $bonusScores;
|
||||
|
||||
$newTotaledScore->save();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Actions\Tabulation\ForceRecalculateTotalScores;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class RecalculateScores extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'audition:recalculate-scores';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Forces the recalculation of total scores for all entries';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle(ForceRecalculateTotalScores $action): void
|
||||
{
|
||||
$this->info('Starting score recalculation...');
|
||||
|
||||
$action();
|
||||
|
||||
$this->info('Score recalculation completed successfully.');
|
||||
}
|
||||
}
|
||||
|
|
@ -13,7 +13,7 @@ class SyncDoublers extends Command
|
|||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'doublers:sync {event? : Optional event ID}';
|
||||
protected $signature = 'audition:sync-doublers {event? : Optional event ID}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\School;
|
||||
use App\Models\Student;
|
||||
use App\Models\User;
|
||||
use Faker\Factory;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class fictionalize extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'audition:fictionalize';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Command description';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$faker = Factory::create();
|
||||
foreach (Student::all() as $student) {
|
||||
$student->first_name = $faker->firstName();
|
||||
$student->last_name = $faker->lastName();
|
||||
$student->save();
|
||||
}
|
||||
|
||||
foreach (School::all() as $school) {
|
||||
$school->name = $faker->city().' High School';
|
||||
$school->save();
|
||||
}
|
||||
|
||||
foreach (User::where('email', '!=', 'matt@mattyoung.us')->get() as $user) {
|
||||
$user->email = $faker->email();
|
||||
$user->first_name = $faker->firstName();
|
||||
$user->last_name = $faker->lastName();
|
||||
$user->cell_phone = $faker->phoneNumber();
|
||||
$user->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -11,6 +11,7 @@ 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;
|
||||
|
|
@ -116,8 +117,44 @@ class SeatAuditionFormController extends Controller
|
|||
$entry->student->full_name().' has declined '.$audition->name);
|
||||
}
|
||||
|
||||
public function acceptSeat(Audition $audition, Entry $entry)
|
||||
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')) {
|
||||
|
|
@ -136,8 +173,10 @@ class SeatAuditionFormController extends Controller
|
|||
$entry->student->full_name().' has accepted '.$audition->name);
|
||||
}
|
||||
|
||||
public function noshow(Audition $audition, Entry $entry)
|
||||
{
|
||||
public function noshow(
|
||||
Audition $audition,
|
||||
Entry $entry
|
||||
) {
|
||||
$recorder = app('App\Actions\Tabulation\EnterNoShow');
|
||||
try {
|
||||
$msg = $recorder($entry);
|
||||
|
|
@ -148,8 +187,10 @@ class SeatAuditionFormController extends Controller
|
|||
return redirect()->route('seating.audition', [$audition])->with('success', $msg);
|
||||
}
|
||||
|
||||
public function draftSeats(Audition $audition, Request $request)
|
||||
{
|
||||
public function draftSeats(
|
||||
Audition $audition,
|
||||
Request $request
|
||||
) {
|
||||
$ranker = app(RankAuditionEntries::class);
|
||||
$validated = $request->validate([
|
||||
'ensemble' => ['required', 'array'],
|
||||
|
|
@ -192,15 +233,17 @@ class SeatAuditionFormController extends Controller
|
|||
return redirect()->route('seating.audition', ['audition' => $audition->id]);
|
||||
}
|
||||
|
||||
public function clearDraft(Audition $audition)
|
||||
{
|
||||
public function clearDraft(
|
||||
Audition $audition
|
||||
) {
|
||||
session()->forget('proposedSeatingArray-'.$audition->id);
|
||||
|
||||
return redirect()->route('seating.audition', ['audition' => $audition->id]);
|
||||
}
|
||||
|
||||
public function publishSeats(Audition $audition)
|
||||
{
|
||||
public function publishSeats(
|
||||
Audition $audition
|
||||
) {
|
||||
$publisher = app('App\Actions\Tabulation\PublishSeats');
|
||||
$seatingProposal = (session('proposedSeatingArray-'.$audition->id));
|
||||
$proposal = [];
|
||||
|
|
@ -223,8 +266,9 @@ class SeatAuditionFormController extends Controller
|
|||
return redirect()->route('seating.audition', [$audition]);
|
||||
}
|
||||
|
||||
public function unpublishSeats(Audition $audition)
|
||||
{
|
||||
public function unpublishSeats(
|
||||
Audition $audition
|
||||
) {
|
||||
$unpublisher = app('App\Actions\Tabulation\UnpublishSeats');
|
||||
$unpublisher($audition);
|
||||
session()->forget('proposedSeatingArray-'.$audition->id);
|
||||
|
|
@ -232,8 +276,10 @@ class SeatAuditionFormController extends Controller
|
|||
return redirect()->route('seating.audition', [$audition]);
|
||||
}
|
||||
|
||||
protected function pickRightPanel(Audition $audition, array $seatable)
|
||||
{
|
||||
protected function pickRightPanel(
|
||||
Audition $audition,
|
||||
array $seatable
|
||||
) {
|
||||
if ($audition->hasFlag('seats_published')) {
|
||||
$resultsWindow = new GetAuditionSeats;
|
||||
$rightPanel['view'] = 'tabulation.auditionSeating-show-published-seats';
|
||||
|
|
|
|||
|
|
@ -4,28 +4,11 @@ namespace App\Models;
|
|||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
|
||||
class BonusScore extends Model
|
||||
{
|
||||
protected $guarded = [];
|
||||
|
||||
protected static function boot()
|
||||
{
|
||||
parent::boot();
|
||||
static::created(function ($bonusScore) {
|
||||
$bonusScore->deleteRelatedCalculatedScores();
|
||||
});
|
||||
|
||||
static::updated(function ($bonusScore) {
|
||||
$bonusScore->deleteRelatedCalculatedScores();
|
||||
});
|
||||
|
||||
static::deleted(function ($bonusScore) {
|
||||
$bonusScore->deleteRelatedCalculatedScores();
|
||||
});
|
||||
}
|
||||
|
||||
public function entry(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Entry::class);
|
||||
|
|
@ -40,16 +23,4 @@ class BonusScore extends Model
|
|||
{
|
||||
return $this->belongsTo(Entry::class, 'originally_scored_entry');
|
||||
}
|
||||
|
||||
public function deleteRelatedCalculatedScores(): void
|
||||
{
|
||||
$entry = $this->entry;
|
||||
if ($entry) {
|
||||
$entry->calculatedScores()->delete();
|
||||
Cache::forget('entryScore-'.$entry->id.'-seating');
|
||||
Cache::forget('entryScore-'.$entry->id.'-advancement');
|
||||
Cache::forget('audition'.$entry->audition_id.'seating');
|
||||
Cache::forget('audition'.$entry->audition_id.'advancement');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
|
||||
namespace App\Observers;
|
||||
|
||||
use App\Actions\Tabulation\TotalEntryScores;
|
||||
use App\Models\BonusScore;
|
||||
|
||||
class BonusScoreObserver
|
||||
{
|
||||
/**
|
||||
* Handle the ScoreSheet "created" event.
|
||||
*/
|
||||
public function created(BonusScore $bonusScore): void
|
||||
{
|
||||
$calculator = app(TotalEntryScores::class);
|
||||
$calculator($bonusScore->entry, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the ScoreSheet "updated" event.
|
||||
*/
|
||||
public function updated(BonusScore $bonusScore): void
|
||||
{
|
||||
$calculator = app(TotalEntryScores::class);
|
||||
$calculator($bonusScore->entry, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the ScoreSheet "deleted" event.
|
||||
*/
|
||||
public function deleted(BonusScore $bonusScore): void
|
||||
{
|
||||
$calculator = app(TotalEntryScores::class);
|
||||
$calculator($bonusScore->entry, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the ScoreSheet "restored" event.
|
||||
*/
|
||||
public function restored(BonusScore $bonusScore): void
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the ScoreSheet "force deleted" event.
|
||||
*/
|
||||
public function forceDeleted(BonusScore $bonusScore): void
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
|
|
@ -21,7 +21,8 @@ class ScoreSheetObserver
|
|||
*/
|
||||
public function updated(ScoreSheet $scoreSheet): void
|
||||
{
|
||||
//
|
||||
$calculator = app(TotalEntryScores::class);
|
||||
$calculator($scoreSheet->entry, true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -29,7 +30,8 @@ class ScoreSheetObserver
|
|||
*/
|
||||
public function deleted(ScoreSheet $scoreSheet): void
|
||||
{
|
||||
//
|
||||
$calculator = app(TotalEntryScores::class);
|
||||
$calculator($scoreSheet->entry, true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ use App\Http\Controllers\NominationEnsembles\ScobdaNominationEnsembleController;
|
|||
use App\Http\Controllers\NominationEnsembles\ScobdaNominationEnsembleEntryController;
|
||||
use App\Http\Controllers\NominationEnsembles\ScobdaNominationSeatingController;
|
||||
use App\Models\Audition;
|
||||
use App\Models\BonusScore;
|
||||
use App\Models\Entry;
|
||||
use App\Models\EntryFlag;
|
||||
use App\Models\Room;
|
||||
|
|
@ -30,6 +31,7 @@ use App\Models\Student;
|
|||
use App\Models\SubscoreDefinition;
|
||||
use App\Models\User;
|
||||
use App\Observers\AuditionObserver;
|
||||
use App\Observers\BonusScoreObserver;
|
||||
use App\Observers\EntryFlagObserver;
|
||||
use App\Observers\EntryObserver;
|
||||
use App\Observers\RoomObserver;
|
||||
|
|
@ -83,6 +85,7 @@ class AppServiceProvider extends ServiceProvider
|
|||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
BonusScore::observe(BonusScoreObserver::class);
|
||||
Entry::observe(EntryObserver::class);
|
||||
Audition::observe(AuditionObserver::class);
|
||||
Room::observe(RoomObserver::class);
|
||||
|
|
@ -96,6 +99,6 @@ class AppServiceProvider extends ServiceProvider
|
|||
SeatingLimit::observe(SeatingLimitObserver::class);
|
||||
EntryFlag::observe(EntryFlagObserver::class);
|
||||
|
||||
// Model::preventLazyLoading(! app()->isProduction());
|
||||
Model::preventLazyLoading(! app()->isProduction());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
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::table('entry_total_scores', function (Blueprint $table) {
|
||||
$table->decimal('bonus_total', 9, 6)->nullable()->after('advancement_subscore_totals');
|
||||
$table->decimal('seating_total_with_bonus', 9, 6)
|
||||
->storedAs('seating_total + COALESCE(bonus_total, 0)')
|
||||
->after('bonus_total');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('entry_total_scores', function (Blueprint $table) {
|
||||
$table->dropColumn('bonus_total');
|
||||
$table->dropColumn('seating_total_with_bonus');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
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::table('audit_log_entries', function (Blueprint $table) {
|
||||
$table->text('message')->change();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('audit_log_entries', function (Blueprint $table) {
|
||||
$table->string('message')->change();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
@ -17,8 +17,7 @@
|
|||
@if($audition->bonusScore()->count() > 0)
|
||||
<br>
|
||||
<div class="display: flex">
|
||||
<x-icons.checkmark color="green"/>
|
||||
Has Bonus
|
||||
<span class="text-yellow-500">No Bonus Score</span>
|
||||
</div>
|
||||
@endif
|
||||
</x-table.th>
|
||||
|
|
@ -65,7 +64,17 @@
|
|||
|
||||
|
||||
</x-table.td>
|
||||
<x-table.td class="align-top">{{ $entry->totalScore->seating_total }}</x-table.td>
|
||||
<x-table.td class="align-top">
|
||||
@if($audition->bonusScore()->count() > 0)
|
||||
@if($entry->totalScore->bonus_total)
|
||||
<span>{{ $entry->totalScore->seating_total_with_bonus }}</span>
|
||||
@else
|
||||
<span class="text-yellow-500">{{ $entry->totalScore->seating_total_with_bonus }}</span>
|
||||
@endif
|
||||
@else
|
||||
{{ $entry->totalScore->seating_total }}
|
||||
@endif
|
||||
</x-table.td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
|
|
@ -230,7 +239,6 @@
|
|||
@endif
|
||||
@else
|
||||
<div class="ml-4">
|
||||
{{-- TODO: Add in bulk decline doubler option --}}
|
||||
@if($unscored_entries->count() > 0)
|
||||
<x-card.card class="p-3 text-red-500 mb-3">
|
||||
Cannot seat the audition while entries are unscored.
|
||||
|
|
@ -238,8 +246,12 @@
|
|||
@endif
|
||||
|
||||
@if($auditionHasUnresolvedDoublers)
|
||||
<x-card.card class="p-3 text-red-500">
|
||||
Cannot seat the audition while there are unresolved doublers.
|
||||
<x-card.card class="p-3">
|
||||
<p class="text-red-500">Cannot seat the audition while there are unresolved doublers.</p>
|
||||
<x-form.form method="POST" action="{{ route('seating.audition.mass_decline',[$audition]) }}">
|
||||
<x-form.field type="number" name="decline-below" label_text="Decline doubler ranked lower than:" />
|
||||
<x-form.button>Decline</x-form.button>
|
||||
</x-form.form>
|
||||
</x-card.card>
|
||||
@endif
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ Route::middleware(['auth', 'verified', CheckIfCanTab::class])->group(function ()
|
|||
Route::post('/{audition}/draftSeats', [SeatAuditionFormController::class, 'draftSeats'])->name('seating.audition.draftSeats');
|
||||
Route::post('/{audition}/clearDraft', [SeatAuditionFormController::class, 'clearDraft'])->name('seating.audition.clearDraft');
|
||||
Route::post('/{audition}/{entry}/decline', [SeatAuditionFormController::class, 'declineSeat'])->name('seating.audition.decline');
|
||||
Route::post('/{audition}/mass_decline', [SeatAuditionFormController::class, 'massDecline'])->name('seating.audition.mass_decline');
|
||||
Route::post('/{audition}/{entry}/accept', [SeatAuditionFormController::class, 'acceptSeat'])->name('seating.audition.accept');
|
||||
Route::post('/{audition}/{entry}/noshow', [SeatAuditionFormController::class, 'noshow'])->name('seating.audition.noshow');
|
||||
Route::post('/{audition}/publish',
|
||||
|
|
|
|||
Loading…
Reference in New Issue