Merge pull request #85 from okorpheus/auditionadmin-68
Auditionadmin 68 Closes #68
This commit is contained in:
commit
daedc05646
|
|
@ -0,0 +1,40 @@
|
||||||
|
<?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('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';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -6,6 +6,7 @@ namespace App\Actions\Tabulation;
|
||||||
|
|
||||||
use App\Exceptions\TabulationException;
|
use App\Exceptions\TabulationException;
|
||||||
use App\Models\BonusScore;
|
use App\Models\BonusScore;
|
||||||
|
use App\Models\CalculatedScore;
|
||||||
use App\Models\Entry;
|
use App\Models\Entry;
|
||||||
use App\Services\AuditionService;
|
use App\Services\AuditionService;
|
||||||
use App\Services\EntryService;
|
use App\Services\EntryService;
|
||||||
|
|
@ -34,6 +35,10 @@ class AllowForOlympicScoring implements CalculateEntryScore
|
||||||
|
|
||||||
public function calculate(string $mode, Entry $entry): array
|
public function calculate(string $mode, Entry $entry): array
|
||||||
{
|
{
|
||||||
|
$calculated = CalculatedScore::where('entry_id', $entry->id)->where('mode', $mode)->first();
|
||||||
|
if ($calculated) {
|
||||||
|
return $calculated->calculatedScore;
|
||||||
|
}
|
||||||
|
|
||||||
$cacheKey = 'entryScore-'.$entry->id.'-'.$mode;
|
$cacheKey = 'entryScore-'.$entry->id.'-'.$mode;
|
||||||
|
|
||||||
|
|
@ -42,8 +47,15 @@ class AllowForOlympicScoring implements CalculateEntryScore
|
||||||
$this->isEntryANoShow($entry);
|
$this->isEntryANoShow($entry);
|
||||||
$this->areAllJudgesIn($entry);
|
$this->areAllJudgesIn($entry);
|
||||||
$this->areAllJudgesValid($entry);
|
$this->areAllJudgesValid($entry);
|
||||||
|
$calculatedScores = $this->getJudgeTotals($mode, $entry);
|
||||||
|
CalculatedScore::create([
|
||||||
|
'entry_id' => $entry->id,
|
||||||
|
'mode' => $mode,
|
||||||
|
'calculatedScore' => $calculatedScores,
|
||||||
|
]);
|
||||||
|
|
||||||
return $this->getJudgeTotals($mode, $entry);
|
return $calculatedScores;
|
||||||
|
// return $this->getJudgeTotals($mode, $entry);
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ namespace App\Actions\Tabulation;
|
||||||
|
|
||||||
use App\Exceptions\ScoreEntryException;
|
use App\Exceptions\ScoreEntryException;
|
||||||
use App\Models\BonusScore;
|
use App\Models\BonusScore;
|
||||||
|
use App\Models\CalculatedScore;
|
||||||
use App\Models\Entry;
|
use App\Models\Entry;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use Illuminate\Database\Eloquent\Collection;
|
use Illuminate\Database\Eloquent\Collection;
|
||||||
|
|
@ -27,6 +28,8 @@ class EnterBonusScore
|
||||||
|
|
||||||
// Create the score for each related entry
|
// Create the score for each related entry
|
||||||
foreach ($entries as $relatedEntry) {
|
foreach ($entries as $relatedEntry) {
|
||||||
|
// Also delete any cached scores
|
||||||
|
CalculatedScore::where('entry_id', $relatedEntry->id)->delete();
|
||||||
BonusScore::create([
|
BonusScore::create([
|
||||||
'entry_id' => $relatedEntry->id,
|
'entry_id' => $relatedEntry->id,
|
||||||
'user_id' => $judge->id,
|
'user_id' => $judge->id,
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
namespace App\Actions\Tabulation;
|
namespace App\Actions\Tabulation;
|
||||||
|
|
||||||
use App\Exceptions\ScoreEntryException;
|
use App\Exceptions\ScoreEntryException;
|
||||||
|
use App\Models\CalculatedScore;
|
||||||
use App\Models\Entry;
|
use App\Models\Entry;
|
||||||
use App\Models\ScoreSheet;
|
use App\Models\ScoreSheet;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
|
|
@ -24,6 +25,7 @@ class EnterScore
|
||||||
*/
|
*/
|
||||||
public function __invoke(User $user, Entry $entry, array $scores, ScoreSheet|false $scoreSheet = false): ScoreSheet
|
public function __invoke(User $user, Entry $entry, array $scores, ScoreSheet|false $scoreSheet = false): ScoreSheet
|
||||||
{
|
{
|
||||||
|
CalculatedScore::where('entry_id', $entry->id)->delete();
|
||||||
$scores = collect($scores);
|
$scores = collect($scores);
|
||||||
$this->basicChecks($user, $entry, $scores);
|
$this->basicChecks($user, $entry, $scores);
|
||||||
$this->checkJudgeAssignment($user, $entry);
|
$this->checkJudgeAssignment($user, $entry);
|
||||||
|
|
|
||||||
|
|
@ -66,8 +66,10 @@ class RankAuditionEntries
|
||||||
return 0;
|
return 0;
|
||||||
});
|
});
|
||||||
$rank = 1;
|
$rank = 1;
|
||||||
|
$rawRank = 1;
|
||||||
foreach ($entries as $entry) {
|
foreach ($entries as $entry) {
|
||||||
$entry->rank = $rank;
|
$entry->rank = $rank;
|
||||||
|
$entry->raw_rank = $rawRank;
|
||||||
// We don't really get a rank for seating if we have certain flags
|
// We don't really get a rank for seating if we have certain flags
|
||||||
if ($mode === 'seating') {
|
if ($mode === 'seating') {
|
||||||
if ($entry->hasFlag('declined')) {
|
if ($entry->hasFlag('declined')) {
|
||||||
|
|
@ -82,6 +84,7 @@ class RankAuditionEntries
|
||||||
if (is_numeric($entry->rank)) {
|
if (is_numeric($entry->rank)) {
|
||||||
$rank++;
|
$rank++;
|
||||||
}
|
}
|
||||||
|
$rawRank++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $entries;
|
return $entries;
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,9 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Actions\Entries\GetEntrySeatingResult;
|
||||||
|
use App\Actions\Tabulation\CalculateEntryScore;
|
||||||
|
use App\Actions\Tabulation\RankAuditionEntries;
|
||||||
use App\Models\School;
|
use App\Models\School;
|
||||||
use App\Services\Invoice\InvoiceDataService;
|
use App\Services\Invoice\InvoiceDataService;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
@ -22,9 +25,31 @@ class DashboardController extends Controller
|
||||||
return view('dashboard.profile');
|
return view('dashboard.profile');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function dashboard()
|
public function dashboard(
|
||||||
{
|
CalculateEntryScore $scoreCalc,
|
||||||
return view('dashboard.dashboard');
|
GetEntrySeatingResult $resultGenerator,
|
||||||
|
RankAuditionEntries $ranker
|
||||||
|
) {
|
||||||
|
$entries = Auth::user()->entries;
|
||||||
|
$entries = $entries->filter(function ($entry) {
|
||||||
|
return $entry->audition->hasFlag('seats_published');
|
||||||
|
});
|
||||||
|
$entries = $entries->sortBy(function ($entry) {
|
||||||
|
return $entry->student->full_name(true);
|
||||||
|
});
|
||||||
|
$scores = [];
|
||||||
|
$results = [];
|
||||||
|
$ranks = [];
|
||||||
|
foreach ($entries as $entry) {
|
||||||
|
$results[$entry->id] = $resultGenerator->getResult($entry);
|
||||||
|
if (! $entry->hasFlag('no_show') && ! $entry->hasFlag('failed_prelim')) {
|
||||||
|
$scores[$entry->id] = $scoreCalc->calculate('seating', $entry);
|
||||||
|
$auditionResults = $ranker->rank('seating', $entry->audition);
|
||||||
|
$ranks[$entry->id] = $auditionResults->firstWhere('id', $entry->id)->raw_rank;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return view('dashboard.dashboard', compact('entries', 'scores', 'results', 'ranks'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function my_school()
|
public function my_school()
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,9 @@
|
||||||
namespace App\Http\Controllers\Tabulation;
|
namespace App\Http\Controllers\Tabulation;
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Models\CalculatedScore;
|
||||||
use App\Models\Entry;
|
use App\Models\Entry;
|
||||||
|
use App\Models\ScoreSheet;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
|
|
@ -79,7 +81,8 @@ class EntryFlagController extends Controller
|
||||||
DB::table('score_sheets')->where('entry_id', $entry->id)->delete();
|
DB::table('score_sheets')->where('entry_id', $entry->id)->delete();
|
||||||
|
|
||||||
$entry->addFlag('no_show');
|
$entry->addFlag('no_show');
|
||||||
|
ScoreSheet::where('entry_id', $entry->id)->delete();
|
||||||
|
CalculatedScore::where('entry_id', $entry->id)->delete();
|
||||||
$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.').';
|
||||||
|
|
||||||
return to_route('entry-flags.noShowSelect')->with('success', $msg);
|
return to_route('entry-flags.noShowSelect')->with('success', $msg);
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
namespace App\Http\Controllers\Tabulation;
|
namespace App\Http\Controllers\Tabulation;
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Models\CalculatedScore;
|
||||||
use App\Models\Entry;
|
use App\Models\Entry;
|
||||||
use App\Models\ScoreSheet;
|
use App\Models\ScoreSheet;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
|
@ -21,6 +22,7 @@ class ScoreController extends Controller
|
||||||
|
|
||||||
public function destroyScore(ScoreSheet $score)
|
public function destroyScore(ScoreSheet $score)
|
||||||
{
|
{
|
||||||
|
CalculatedScore::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');
|
||||||
}
|
}
|
||||||
|
|
@ -66,6 +68,7 @@ class ScoreController extends Controller
|
||||||
|
|
||||||
public function saveEntryScoreSheet(Request $request, Entry $entry)
|
public function saveEntryScoreSheet(Request $request, Entry $entry)
|
||||||
{
|
{
|
||||||
|
CalculatedScore::where('entry_id', $entry->id)->delete();
|
||||||
$publishedCheck = $this->checkIfPublished($entry);
|
$publishedCheck = $this->checkIfPublished($entry);
|
||||||
if ($publishedCheck) {
|
if ($publishedCheck) {
|
||||||
return $publishedCheck;
|
return $publishedCheck;
|
||||||
|
|
@ -105,6 +108,7 @@ class ScoreController extends Controller
|
||||||
['subscores' => $sheet['scores']]
|
['subscores' => $sheet['scores']]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
// TODO rewrite to use EnterScore action or clear score cache
|
||||||
|
|
||||||
return redirect()->route('scores.chooseEntry')->with('success', count($preparedScoreSheets).' Scores saved');
|
return redirect()->route('scores.chooseEntry')->with('success', count($preparedScoreSheets).' Scores saved');
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class CalculatedScore extends Model
|
||||||
|
{
|
||||||
|
use HasFactory;
|
||||||
|
|
||||||
|
protected $guarded = [];
|
||||||
|
|
||||||
|
protected $casts = ['calculatedScore' => 'json'];
|
||||||
|
}
|
||||||
|
|
@ -121,6 +121,11 @@ class Entry extends Model
|
||||||
return $this->hasOne(Seat::class);
|
return $this->hasOne(Seat::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function calculatedScores(): HasMany
|
||||||
|
{
|
||||||
|
return $this->hasMany(CalculatedScore::class);
|
||||||
|
}
|
||||||
|
|
||||||
public function scopeForSeating(Builder $query): void
|
public function scopeForSeating(Builder $query): void
|
||||||
{
|
{
|
||||||
$query->where('for_seating', 1);
|
$query->where('for_seating', 1);
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,22 @@ class ScoreSheet extends Model
|
||||||
|
|
||||||
protected $casts = ['subscores' => 'json'];
|
protected $casts = ['subscores' => 'json'];
|
||||||
|
|
||||||
|
protected static function boot()
|
||||||
|
{
|
||||||
|
parent::boot();
|
||||||
|
static::created(function ($scoreSheet) {
|
||||||
|
$scoreSheet->deleteRelatedCalculatedScores();
|
||||||
|
});
|
||||||
|
|
||||||
|
static::updated(function ($scoreSheet) {
|
||||||
|
$scoreSheet->deleteRelatedCalculatedScores();
|
||||||
|
});
|
||||||
|
|
||||||
|
static::deleted(function ($scoreSheet) {
|
||||||
|
$scoreSheet->deleteRelatedCalculatedScores();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public function entry(): BelongsTo
|
public function entry(): BelongsTo
|
||||||
{
|
{
|
||||||
return $this->belongsTo(Entry::class);
|
return $this->belongsTo(Entry::class);
|
||||||
|
|
@ -37,9 +53,18 @@ class ScoreSheet extends Model
|
||||||
'audition_id' // Local key on the intermediate model (Entry)
|
'audition_id' // Local key on the intermediate model (Entry)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getSubscore($id)
|
public function getSubscore($id)
|
||||||
{
|
{
|
||||||
return $this->subscores[$id]['score'] ?? false;
|
return $this->subscores[$id]['score'] ?? false;
|
||||||
// this function is used at resources/views/tabulation/entry_score_sheet.blade.php
|
// this function is used at resources/views/tabulation/entry_score_sheet.blade.php
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function deleteRelatedCalculatedScores(): void
|
||||||
|
{
|
||||||
|
$entry = $this->entry;
|
||||||
|
if ($entry) {
|
||||||
|
$entry->calculatedScores()->delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Models\Entry;
|
||||||
|
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('calculated_scores', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->foreignIdFor(Entry::class)->constrained()->cascadeOnDelete()->cascadeOnUpdate();
|
||||||
|
$table->string('mode');
|
||||||
|
$table->json('calculatedScore');
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('calculated_scores');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -2,7 +2,8 @@
|
||||||
<x-layout.app>
|
<x-layout.app>
|
||||||
<x-slot:page_title>Dashboard</x-slot:page_title>
|
<x-slot:page_title>Dashboard</x-slot:page_title>
|
||||||
@if(! Auth::user()->school_id)
|
@if(! Auth::user()->school_id)
|
||||||
<p class="pb-5">You aren't currently associated with a school. <a href="/my_school" class="text-blue-600">Click here to choose or create one.</a></p>
|
<p class="pb-5">You aren't currently associated with a school. <a href="/my_school" class="text-blue-600">Click
|
||||||
|
here to choose or create one.</a></p>
|
||||||
@endif
|
@endif
|
||||||
<div class="grid sm:grid-cols-2 md:grid-cols-4">
|
<div class="grid sm:grid-cols-2 md:grid-cols-4">
|
||||||
<div>{{-- Column 1 --}}
|
<div>{{-- Column 1 --}}
|
||||||
|
|
@ -29,6 +30,15 @@
|
||||||
</x-card.list.body>
|
</x-card.list.body>
|
||||||
</x-card.card>
|
</x-card.card>
|
||||||
</div>
|
</div>
|
||||||
|
@if(Auth::user()->school_id)
|
||||||
|
<div class="md:col-span-3 pl-3">{{-- Column 2 Results --}}
|
||||||
|
<x-card.card>
|
||||||
|
<x-card.heading>My Results</x-card.heading>
|
||||||
|
@include('dashboard.results-table')
|
||||||
|
</x-card.card>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</x-layout.app>
|
</x-layout.app>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
<x-table.table>
|
||||||
|
<thead>
|
||||||
|
<x-table.th>Student</x-table.th>
|
||||||
|
<x-table.th>Audition</x-table.th>
|
||||||
|
<x-table.th>Score</x-table.th>
|
||||||
|
<x-table.th>Rank</x-table.th>
|
||||||
|
<x-table.th>Result</x-table.th>
|
||||||
|
</thead>
|
||||||
|
<x-table.body>
|
||||||
|
@foreach($entries as $entry)
|
||||||
|
<tr>
|
||||||
|
<x-table.td>{{ $entry->student->full_name() }}</x-table.td>
|
||||||
|
<x-table.td>{{ $entry->audition->name }}</x-table.td>
|
||||||
|
@if(! $entry->audition->hasFlag('seats_published'))
|
||||||
|
<x-table.td colspan="3">Results not available</x-table.td>
|
||||||
|
@else
|
||||||
|
<x-table.td> {{ $scores[$entry->id ][0] ?? '--' }}</x-table.td>
|
||||||
|
<x-table.td> {{ $ranks[$entry->id ] ?? '--' }}</x-table.td>
|
||||||
|
<x-table.td> {{ $results[$entry->id ] ?? '--' }}</x-table.td>
|
||||||
|
@endif
|
||||||
|
</tr>
|
||||||
|
@endforeach
|
||||||
|
</x-table.body>
|
||||||
|
</x-table.table>
|
||||||
Loading…
Reference in New Issue