Create tests for app/actions/tabulation/RankAuditionEntries
This commit is contained in:
parent
157e2f496a
commit
1a3d88bfa8
|
|
@ -27,7 +27,7 @@ class RankAuditionEntries
|
||||||
public function __invoke(Audition $audition, string $rank_type): Collection|Entry
|
public function __invoke(Audition $audition, string $rank_type): Collection|Entry
|
||||||
{
|
{
|
||||||
if ($rank_type !== 'seating' && $rank_type !== 'advancement') {
|
if ($rank_type !== 'seating' && $rank_type !== 'advancement') {
|
||||||
throw new AuditionAdminException('Invalid rank type: '.$rank_type.' (must be seating or advancement)');
|
throw new AuditionAdminException('Invalid rank type (must be seating or advancement)');
|
||||||
}
|
}
|
||||||
|
|
||||||
$cache_duration = 15;
|
$cache_duration = 15;
|
||||||
|
|
@ -87,7 +87,7 @@ class RankAuditionEntries
|
||||||
|
|
||||||
private function get_advancement_ranks(Audition $audition): Collection|Entry
|
private function get_advancement_ranks(Audition $audition): Collection|Entry
|
||||||
{
|
{
|
||||||
return $audition->entries()
|
$sortedEntries = $audition->entries()
|
||||||
->whereHas('totalScore')
|
->whereHas('totalScore')
|
||||||
->with('totalScore')
|
->with('totalScore')
|
||||||
->with('student.school')
|
->with('student.school')
|
||||||
|
|
@ -106,5 +106,12 @@ 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,8 @@ class EntryTotalScore extends Model
|
||||||
{
|
{
|
||||||
use HasFactory;
|
use HasFactory;
|
||||||
|
|
||||||
|
protected $guarded = [];
|
||||||
|
|
||||||
protected $casts = [
|
protected $casts = [
|
||||||
'seating_subscore_totals' => 'json',
|
'seating_subscore_totals' => 'json',
|
||||||
'advancement_subscore_totals' => 'json',
|
'advancement_subscore_totals' => 'json',
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,360 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/** @noinspection PhpUnhandledExceptionInspection */
|
||||||
|
|
||||||
|
use App\Actions\Tabulation\RankAuditionEntries;
|
||||||
|
use App\Exceptions\AuditionAdminException;
|
||||||
|
use App\Models\Audition;
|
||||||
|
use App\Models\BonusScoreDefinition;
|
||||||
|
use App\Models\Entry;
|
||||||
|
use App\Models\EntryTotalScore;
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
|
||||||
|
uses(RefreshDatabase::class);
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
$this->audition = Audition::factory()->create(['minimum_grade' => 1, 'maximum_grade' => 14]);
|
||||||
|
$this->entries = Entry::factory()->count(10)->create(['audition_id' => $this->audition->id]);
|
||||||
|
$this->ranker = app(RankAuditionEntries::class);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(function () {
|
||||||
|
cache()->flush();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws an exception if an invalid rank type is specified', function () {
|
||||||
|
($this->ranker)($this->audition, 'bababoey');
|
||||||
|
})->throws(AuditionAdminException::class, 'Invalid rank type (must be seating or advancement)');
|
||||||
|
|
||||||
|
// Test Rank for Seating
|
||||||
|
it('ranks entries for seating if there are no ties', function () {
|
||||||
|
// entry 0 will be second place
|
||||||
|
$score0 = EntryTotalScore::create([
|
||||||
|
'entry_id' => $this->entries[0]->id,
|
||||||
|
'seating_total' => 50,
|
||||||
|
'advancement_total' => 50,
|
||||||
|
'seating_subscore_totals' => [5, 5],
|
||||||
|
'advancement_subscore_totals' => [5, 5],
|
||||||
|
]);
|
||||||
|
// entry 1 will be third place
|
||||||
|
$score1 = EntryTotalScore::create([
|
||||||
|
'entry_id' => $this->entries[1]->id,
|
||||||
|
'seating_total' => 25,
|
||||||
|
'advancement_total' => 75,
|
||||||
|
'seating_subscore_totals' => [5, 5],
|
||||||
|
'advancement_subscore_totals' => [5, 5],
|
||||||
|
]);
|
||||||
|
// entry 2 will be first place
|
||||||
|
$score1 = EntryTotalScore::create([
|
||||||
|
'entry_id' => $this->entries[2]->id,
|
||||||
|
'seating_total' => 75,
|
||||||
|
'advancement_total' => 25,
|
||||||
|
'seating_subscore_totals' => [5, 5],
|
||||||
|
'advancement_subscore_totals' => [5, 5],
|
||||||
|
]);
|
||||||
|
$sortedEntries = ($this->ranker)($this->audition, 'seating');
|
||||||
|
expect($sortedEntries[0]->id)->toEqual($this->entries[2]->id)
|
||||||
|
->and($sortedEntries[1]->id)->toEqual($this->entries[0]->id)
|
||||||
|
->and($sortedEntries[2]->id)->toEqual($this->entries[1]->id);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('makes use of bonus scores when set', function () {
|
||||||
|
$bonusScoreDefinition = BonusScoreDefinition::create([
|
||||||
|
'name' => 'bonus',
|
||||||
|
'max_score' => 100,
|
||||||
|
'weight' => 1,
|
||||||
|
'for_seating' => 1,
|
||||||
|
'for_attendance' => 0,
|
||||||
|
]);
|
||||||
|
$bonusScoreDefinition->auditions()->attach($this->audition);
|
||||||
|
DB::table('bonus_scores')->insert([
|
||||||
|
'entry_id' => $this->entries[2]->id,
|
||||||
|
'user_id' => 1,
|
||||||
|
'originally_scored_entry' => $this->entries[2]->id,
|
||||||
|
'score' => 100,
|
||||||
|
'created_at' => now(),
|
||||||
|
'updated_at' => now(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
// entry 0 will be second place
|
||||||
|
$score0 = EntryTotalScore::create([
|
||||||
|
'entry_id' => $this->entries[0]->id,
|
||||||
|
'seating_total' => 50,
|
||||||
|
'advancement_total' => 50,
|
||||||
|
'seating_subscore_totals' => [5, 5],
|
||||||
|
'advancement_subscore_totals' => [5, 5],
|
||||||
|
]);
|
||||||
|
// entry 1 will be third place
|
||||||
|
$score1 = EntryTotalScore::create([
|
||||||
|
'entry_id' => $this->entries[1]->id,
|
||||||
|
'seating_total' => 25,
|
||||||
|
'advancement_total' => 75,
|
||||||
|
'seating_subscore_totals' => [5, 5],
|
||||||
|
'advancement_subscore_totals' => [5, 5],
|
||||||
|
]);
|
||||||
|
// entry 2 will be first place
|
||||||
|
$score2 = EntryTotalScore::create([
|
||||||
|
'entry_id' => $this->entries[2]->id,
|
||||||
|
'seating_total' => 2,
|
||||||
|
'advancement_total' => 25,
|
||||||
|
'seating_subscore_totals' => [5, 5],
|
||||||
|
'advancement_subscore_totals' => [5, 5],
|
||||||
|
'bonus_total' => 100,
|
||||||
|
]);
|
||||||
|
$sortedEntries = ($this->ranker)($this->audition, 'seating');
|
||||||
|
expect($sortedEntries[0]->id)->toEqual($this->entries[2]->id)
|
||||||
|
->and($sortedEntries[1]->id)->toEqual($this->entries[0]->id)
|
||||||
|
->and($sortedEntries[2]->id)->toEqual($this->entries[1]->id);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('assigns a seatingRank property to each entry that is scored', function () {
|
||||||
|
// entry 0 will be second place
|
||||||
|
$score0 = EntryTotalScore::create([
|
||||||
|
'entry_id' => $this->entries[0]->id,
|
||||||
|
'seating_total' => 50,
|
||||||
|
'advancement_total' => 50,
|
||||||
|
'seating_subscore_totals' => [5, 5],
|
||||||
|
'advancement_subscore_totals' => [5, 5],
|
||||||
|
]);
|
||||||
|
// entry 1 will be third place
|
||||||
|
$score1 = EntryTotalScore::create([
|
||||||
|
'entry_id' => $this->entries[1]->id,
|
||||||
|
'seating_total' => 25,
|
||||||
|
'advancement_total' => 75,
|
||||||
|
'seating_subscore_totals' => [5, 5],
|
||||||
|
'advancement_subscore_totals' => [5, 5],
|
||||||
|
]);
|
||||||
|
// entry 2 will be first place
|
||||||
|
$score1 = EntryTotalScore::create([
|
||||||
|
'entry_id' => $this->entries[2]->id,
|
||||||
|
'seating_total' => 75,
|
||||||
|
'advancement_total' => 25,
|
||||||
|
'seating_subscore_totals' => [5, 5],
|
||||||
|
'advancement_subscore_totals' => [5, 5],
|
||||||
|
]);
|
||||||
|
$sortedEntries = ($this->ranker)($this->audition, 'seating');
|
||||||
|
expect($sortedEntries[0]->seatingRank)->toEqual(1)
|
||||||
|
->and($sortedEntries[1]->seatingRank)->toEqual(2)
|
||||||
|
->and($sortedEntries[2]->seatingRank)->toEqual(3)
|
||||||
|
->and($sortedEntries->count())->toEqual(3);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('skips a declined entry when assigning seatingRank properties', function () {
|
||||||
|
// entry 0 will be second place
|
||||||
|
$score0 = EntryTotalScore::create([
|
||||||
|
'entry_id' => $this->entries[0]->id,
|
||||||
|
'seating_total' => 50,
|
||||||
|
'advancement_total' => 50,
|
||||||
|
'seating_subscore_totals' => [5, 5],
|
||||||
|
'advancement_subscore_totals' => [5, 5],
|
||||||
|
]);
|
||||||
|
// entry 1 will be third place
|
||||||
|
$score1 = EntryTotalScore::create([
|
||||||
|
'entry_id' => $this->entries[1]->id,
|
||||||
|
'seating_total' => 25,
|
||||||
|
'advancement_total' => 75,
|
||||||
|
'seating_subscore_totals' => [5, 5],
|
||||||
|
'advancement_subscore_totals' => [5, 5],
|
||||||
|
]);
|
||||||
|
// entry 2 will be first place
|
||||||
|
$score1 = EntryTotalScore::create([
|
||||||
|
'entry_id' => $this->entries[2]->id,
|
||||||
|
'seating_total' => 75,
|
||||||
|
'advancement_total' => 25,
|
||||||
|
'seating_subscore_totals' => [5, 5],
|
||||||
|
'advancement_subscore_totals' => [5, 5],
|
||||||
|
]);
|
||||||
|
$this->entries[0]->addFlag('declined');
|
||||||
|
$sortedEntries = ($this->ranker)($this->audition, 'seating');
|
||||||
|
expect($sortedEntries[0]->seatingRank)->toEqual(1)
|
||||||
|
->and($sortedEntries[1]->seatingRank)->toEqual('declined')
|
||||||
|
->and($sortedEntries[2]->seatingRank)->toEqual(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('uses the second subscore as a second tiebreaker for seating', function () {
|
||||||
|
// entry 0 will be second place
|
||||||
|
$score0 = EntryTotalScore::create([
|
||||||
|
'entry_id' => $this->entries[0]->id,
|
||||||
|
'seating_total' => 50,
|
||||||
|
'advancement_total' => 50,
|
||||||
|
'seating_subscore_totals' => [5, 5],
|
||||||
|
'advancement_subscore_totals' => [5, 5],
|
||||||
|
]);
|
||||||
|
// entry 1 will be third place
|
||||||
|
$score1 = EntryTotalScore::create([
|
||||||
|
'entry_id' => $this->entries[1]->id,
|
||||||
|
'seating_total' => 50,
|
||||||
|
'advancement_total' => 75,
|
||||||
|
'seating_subscore_totals' => [5, 4],
|
||||||
|
'advancement_subscore_totals' => [5, 5],
|
||||||
|
]);
|
||||||
|
// entry 2 will be first place
|
||||||
|
$score1 = EntryTotalScore::create([
|
||||||
|
'entry_id' => $this->entries[2]->id,
|
||||||
|
'seating_total' => 50,
|
||||||
|
'advancement_total' => 25,
|
||||||
|
'seating_subscore_totals' => [5, 6],
|
||||||
|
'advancement_subscore_totals' => [5, 5],
|
||||||
|
]);
|
||||||
|
$sortedEntries = ($this->ranker)($this->audition, 'seating');
|
||||||
|
expect($sortedEntries[0]->id)->toEqual($this->entries[2]->id)
|
||||||
|
->and($sortedEntries[1]->id)->toEqual($this->entries[0]->id)
|
||||||
|
->and($sortedEntries[2]->id)->toEqual($this->entries[1]->id);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('uses the first subscore as a tiebreaker for seating', function () {
|
||||||
|
// entry 0 will be second place
|
||||||
|
$score0 = EntryTotalScore::create([
|
||||||
|
'entry_id' => $this->entries[0]->id,
|
||||||
|
'seating_total' => 50,
|
||||||
|
'advancement_total' => 50,
|
||||||
|
'seating_subscore_totals' => [5, 5],
|
||||||
|
'advancement_subscore_totals' => [5, 5],
|
||||||
|
]);
|
||||||
|
// entry 1 will be third place
|
||||||
|
$score1 = EntryTotalScore::create([
|
||||||
|
'entry_id' => $this->entries[1]->id,
|
||||||
|
'seating_total' => 50,
|
||||||
|
'advancement_total' => 75,
|
||||||
|
'seating_subscore_totals' => [4, 5],
|
||||||
|
'advancement_subscore_totals' => [5, 5],
|
||||||
|
]);
|
||||||
|
// entry 2 will be first place
|
||||||
|
$score1 = EntryTotalScore::create([
|
||||||
|
'entry_id' => $this->entries[2]->id,
|
||||||
|
'seating_total' => 50,
|
||||||
|
'advancement_total' => 25,
|
||||||
|
'seating_subscore_totals' => [6, 5],
|
||||||
|
'advancement_subscore_totals' => [5, 5],
|
||||||
|
]);
|
||||||
|
$sortedEntries = ($this->ranker)($this->audition, 'seating');
|
||||||
|
expect($sortedEntries[0]->id)->toEqual($this->entries[2]->id)
|
||||||
|
->and($sortedEntries[1]->id)->toEqual($this->entries[0]->id)
|
||||||
|
->and($sortedEntries[2]->id)->toEqual($this->entries[1]->id);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test Rank for Advancement
|
||||||
|
it('ranks entries for advancement if there are no ties', function () {
|
||||||
|
// entry 0 will be second place
|
||||||
|
$score0 = EntryTotalScore::create([
|
||||||
|
'entry_id' => $this->entries[0]->id,
|
||||||
|
'seating_total' => 50,
|
||||||
|
'advancement_total' => 50,
|
||||||
|
'seating_subscore_totals' => [5, 5],
|
||||||
|
'advancement_subscore_totals' => [5, 5],
|
||||||
|
]);
|
||||||
|
// entry 1 will be third place
|
||||||
|
$score1 = EntryTotalScore::create([
|
||||||
|
'entry_id' => $this->entries[1]->id,
|
||||||
|
'seating_total' => 75,
|
||||||
|
'advancement_total' => 25,
|
||||||
|
'seating_subscore_totals' => [5, 5],
|
||||||
|
'advancement_subscore_totals' => [5, 5],
|
||||||
|
]);
|
||||||
|
// entry 2 will be first place
|
||||||
|
$score1 = EntryTotalScore::create([
|
||||||
|
'entry_id' => $this->entries[2]->id,
|
||||||
|
'seating_total' => 25,
|
||||||
|
'advancement_total' => 75,
|
||||||
|
'seating_subscore_totals' => [5, 5],
|
||||||
|
'advancement_subscore_totals' => [5, 5],
|
||||||
|
]);
|
||||||
|
$sortedEntries = ($this->ranker)($this->audition, 'advancement');
|
||||||
|
expect($sortedEntries[0]->id)->toEqual($this->entries[2]->id)
|
||||||
|
->and($sortedEntries[1]->id)->toEqual($this->entries[0]->id)
|
||||||
|
->and($sortedEntries[2]->id)->toEqual($this->entries[1]->id);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('assigns a advancementRank property to each entry that is scored', function () {
|
||||||
|
// entry 0 will be second place
|
||||||
|
$score0 = EntryTotalScore::create([
|
||||||
|
'entry_id' => $this->entries[0]->id,
|
||||||
|
'seating_total' => 50,
|
||||||
|
'advancement_total' => 50,
|
||||||
|
'seating_subscore_totals' => [5, 5],
|
||||||
|
'advancement_subscore_totals' => [5, 5],
|
||||||
|
]);
|
||||||
|
// entry 1 will be third place
|
||||||
|
$score1 = EntryTotalScore::create([
|
||||||
|
'entry_id' => $this->entries[1]->id,
|
||||||
|
'seating_total' => 75,
|
||||||
|
'advancement_total' => 25,
|
||||||
|
'seating_subscore_totals' => [5, 5],
|
||||||
|
'advancement_subscore_totals' => [5, 5],
|
||||||
|
]);
|
||||||
|
// entry 2 will be first place
|
||||||
|
$score1 = EntryTotalScore::create([
|
||||||
|
'entry_id' => $this->entries[2]->id,
|
||||||
|
'seating_total' => 25,
|
||||||
|
'advancement_total' => 75,
|
||||||
|
'seating_subscore_totals' => [5, 5],
|
||||||
|
'advancement_subscore_totals' => [5, 5],
|
||||||
|
]);
|
||||||
|
$sortedEntries = ($this->ranker)($this->audition, 'advancement');
|
||||||
|
expect($sortedEntries[0]->advancementRank)->toEqual(1)
|
||||||
|
->and($sortedEntries[1]->advancementRank)->toEqual(2)
|
||||||
|
->and($sortedEntries[2]->advancementRank)->toEqual(3)
|
||||||
|
->and($sortedEntries->count())->toEqual(3);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('uses the second subscore as a second tiebreaker for advancement', function () {
|
||||||
|
// entry 0 will be second place
|
||||||
|
$score0 = EntryTotalScore::create([
|
||||||
|
'entry_id' => $this->entries[0]->id,
|
||||||
|
'seating_total' => 50,
|
||||||
|
'advancement_total' => 50,
|
||||||
|
'seating_subscore_totals' => [5, 5],
|
||||||
|
'advancement_subscore_totals' => [5, 5],
|
||||||
|
]);
|
||||||
|
// entry 1 will be third place
|
||||||
|
$score1 = EntryTotalScore::create([
|
||||||
|
'entry_id' => $this->entries[1]->id,
|
||||||
|
'seating_total' => 75,
|
||||||
|
'advancement_total' => 50,
|
||||||
|
'seating_subscore_totals' => [5, 5],
|
||||||
|
'advancement_subscore_totals' => [5, 4],
|
||||||
|
]);
|
||||||
|
// entry 2 will be first place
|
||||||
|
$score1 = EntryTotalScore::create([
|
||||||
|
'entry_id' => $this->entries[2]->id,
|
||||||
|
'seating_total' => 25,
|
||||||
|
'advancement_total' => 50,
|
||||||
|
'seating_subscore_totals' => [5, 5],
|
||||||
|
'advancement_subscore_totals' => [5, 6],
|
||||||
|
]);
|
||||||
|
$sortedEntries = ($this->ranker)($this->audition, 'advancement');
|
||||||
|
expect($sortedEntries[0]->id)->toEqual($this->entries[2]->id)
|
||||||
|
->and($sortedEntries[1]->id)->toEqual($this->entries[0]->id)
|
||||||
|
->and($sortedEntries[2]->id)->toEqual($this->entries[1]->id);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('uses the first subscore as a tiebreaker for advancement', function () {
|
||||||
|
// entry 0 will be second place
|
||||||
|
$score0 = EntryTotalScore::create([
|
||||||
|
'entry_id' => $this->entries[0]->id,
|
||||||
|
'seating_total' => 50,
|
||||||
|
'advancement_total' => 50,
|
||||||
|
'seating_subscore_totals' => [5, 5],
|
||||||
|
'advancement_subscore_totals' => [5, 5],
|
||||||
|
]);
|
||||||
|
// entry 1 will be third place
|
||||||
|
$score1 = EntryTotalScore::create([
|
||||||
|
'entry_id' => $this->entries[1]->id,
|
||||||
|
'seating_total' => 50,
|
||||||
|
'advancement_total' => 50,
|
||||||
|
'seating_subscore_totals' => [5, 5],
|
||||||
|
'advancement_subscore_totals' => [4, 5],
|
||||||
|
]);
|
||||||
|
// entry 2 will be first place
|
||||||
|
$score1 = EntryTotalScore::create([
|
||||||
|
'entry_id' => $this->entries[2]->id,
|
||||||
|
'seating_total' => 50,
|
||||||
|
'advancement_total' => 50,
|
||||||
|
'seating_subscore_totals' => [5, 5],
|
||||||
|
'advancement_subscore_totals' => [6, 5],
|
||||||
|
]);
|
||||||
|
$sortedEntries = ($this->ranker)($this->audition, 'advancement');
|
||||||
|
expect($sortedEntries[0]->id)->toEqual($this->entries[2]->id)
|
||||||
|
->and($sortedEntries[1]->id)->toEqual($this->entries[0]->id)
|
||||||
|
->and($sortedEntries[2]->id)->toEqual($this->entries[1]->id);
|
||||||
|
});
|
||||||
Loading…
Reference in New Issue