diff --git a/lib/Bracket/Controller/Admin.pm b/lib/Bracket/Controller/Admin.pm index 3d6cc13..d225d23 100644 --- a/lib/Bracket/Controller/Admin.pm +++ b/lib/Bracket/Controller/Admin.pm @@ -130,23 +130,17 @@ sub continuity_audit : Global { sub incomplete_submissions : Global { my ($self, $c) = @_; - my $pick_targets = Bracket::Service::BracketStructure->pick_targets( - $c->model('DBIC')->schema + my @regions = $c->model('DBIC')->schema->resultset('Region')->search({})->all; + my %region_name_for_id = map { $_->id => $_->name } @regions; + my $targets = Bracket::Service::BracketStructure->submission_targets( + $c->model('DBIC')->schema, + [ map { $_->id } @regions ], ); - my $expected_total_picks = $pick_targets->{total_picks} || 63; - my $expected_final4_picks = $pick_targets->{final4_picks} || 3; - my $expected_region_picks_by_region = $pick_targets->{region_picks_by_region} || {}; - my @region_ids = sort { $a <=> $b } keys %{$expected_region_picks_by_region}; - if (!@region_ids) { - @region_ids = (1 .. 4); - $expected_region_picks_by_region = { - map { $_ => 15 } @region_ids - }; - } + my $expected_total_picks = $targets->{expected_total_picks}; + my $expected_final4_picks = $targets->{expected_final4_picks}; + my $expected_region_picks_by_region = $targets->{expected_region_picks_by_region}; + my @region_ids = @{$targets->{region_ids} || []}; - my %region_name_for_id = map { - $_->id => $_->name - } $c->model('DBIC')->schema->resultset('Region')->search({})->all; my $total_picks_by_player = $c->model('DBIC')->count_player_picks || {}; my @active_players = $c->model('DBIC::Player')->search( diff --git a/lib/Bracket/Controller/Player.pm b/lib/Bracket/Controller/Player.pm index 6f7537b..9adaf8d 100644 --- a/lib/Bracket/Controller/Player.pm +++ b/lib/Bracket/Controller/Player.pm @@ -4,7 +4,6 @@ use Moose; BEGIN { extends 'Catalyst::Controller' } use Bracket::Service::BracketStructure; use Bracket::Service::EquityProjection; -use Bracket::Service::BracketStructure; =head1 NAME @@ -34,23 +33,13 @@ sub home : Path('/player') { my @regions = $c->model('DBIC::Region')->search({},{order_by => 'id'})->all; $c->stash->{regions} = \@regions; - my $pick_targets = Bracket::Service::BracketStructure->pick_targets( - $c->model('DBIC')->schema + my $targets = Bracket::Service::BracketStructure->submission_targets( + $c->model('DBIC')->schema, + [ map { $_->id } @regions ], ); - my $expected_total_picks = $pick_targets->{total_picks} || 63; - my $expected_final4_picks = $pick_targets->{final4_picks} || 3; - my $expected_region_picks_by_region = $pick_targets->{region_picks_by_region} || {}; - my %region_targets = map { - my $region_id = $_->id; - $region_id => ( - exists $expected_region_picks_by_region->{$region_id} - ? $expected_region_picks_by_region->{$region_id} - : 15 - ) - } @regions; - $c->stash->{expected_total_picks} = $expected_total_picks; - $c->stash->{expected_final4_picks} = $expected_final4_picks; - $c->stash->{expected_region_picks_by_region} = \%region_targets; + $c->stash->{expected_total_picks} = $targets->{expected_total_picks}; + $c->stash->{expected_final4_picks} = $targets->{expected_final4_picks}; + $c->stash->{expected_region_picks_by_region} = $targets->{expected_region_picks_by_region}; # Picks made per region my $number_of_picks_per_region = $c->model('DBIC')->count_region_picks($player_id); @@ -85,10 +74,10 @@ sub all : Global { my @players = $c->model('DBIC::Player')->search( { active => 1 } )->all; my $picks_per_player = $c->model('DBIC')->count_player_picks; $c->stash->{picks_per_player} = $picks_per_player; - my $pick_targets = Bracket::Service::BracketStructure->pick_targets( + my $targets = Bracket::Service::BracketStructure->submission_targets( $c->model('DBIC')->schema ); - my $expected_total_picks = $pick_targets->{total_picks} || 63; + my $expected_total_picks = $targets->{expected_total_picks}; $c->stash->{expected_total_picks} = $expected_total_picks; my @regions = $c->model('DBIC::Region')->search({},{order_by => 'id'})->all; $c->stash->{regions} = \@regions; diff --git a/lib/Bracket/Service/BracketStructure.pm b/lib/Bracket/Service/BracketStructure.pm index a657a34..b2d399a 100644 --- a/lib/Bracket/Service/BracketStructure.pm +++ b/lib/Bracket/Service/BracketStructure.pm @@ -64,6 +64,34 @@ sub pick_targets { }; } +sub submission_targets { + my ($class, $schema, $region_ids) = @_; + my $pick_targets = $class->pick_targets($schema) || {}; + + my @normalized_region_ids = _normalize_region_ids($region_ids); + if (!@normalized_region_ids) { + @normalized_region_ids = _normalize_region_ids( + [ keys %{ $pick_targets->{region_picks_by_region} || {} } ] + ); + } + @normalized_region_ids = (1 .. 4) if !@normalized_region_ids; + + my $expected_total_picks = _positive_int_or_default($pick_targets->{total_picks}, 63); + my $expected_final4_picks = _positive_int_or_default($pick_targets->{final4_picks}, 3); + + my $raw_region_targets = $pick_targets->{region_picks_by_region} || {}; + my %expected_region_picks_by_region = map { + $_ => _positive_int_or_default($raw_region_targets->{$_}, 15) + } @normalized_region_ids; + + return { + expected_total_picks => $expected_total_picks, + expected_final4_picks => $expected_final4_picks, + expected_region_picks_by_region => \%expected_region_picks_by_region, + region_ids => \@normalized_region_ids, + }; +} + sub _derive_structure { my ($schema) = @_; return { @@ -234,4 +262,19 @@ sub _round1_ancestor_games { return sort { $a <=> $b } keys %round1; } +sub _normalize_region_ids { + my ($region_ids) = @_; + $region_ids ||= []; + my %seen; + return sort { $a <=> $b } grep { + defined $_ && $_ =~ /^\d+$/ && $_ > 0 && !$seen{$_}++ + } @{$region_ids}; +} + +sub _positive_int_or_default { + my ($value, $default) = @_; + return $default if !defined $value || $value !~ /^\d+$/ || $value < 1; + return $value; +} + 1; diff --git a/t/bracket_structure.t b/t/bracket_structure.t index 41e3057..28ade4a 100644 --- a/t/bracket_structure.t +++ b/t/bracket_structure.t @@ -45,4 +45,54 @@ is_deeply( 'pick targets derive expected region pick counts from topology', ); +my $targets = Bracket::Service::BracketStructure->submission_targets($schema); +is($targets->{expected_total_picks}, 63, 'submission targets expose total expected picks'); +is($targets->{expected_final4_picks}, 3, 'submission targets expose final4 expected picks'); +is_deeply( + $targets->{region_ids}, + [1, 2, 3, 4], + 'submission targets expose normalized region ids', +); +is_deeply( + $targets->{expected_region_picks_by_region}, + { + 1 => 15, + 2 => 15, + 3 => 15, + 4 => 15, + }, + 'submission targets expose per-region expected picks', +); + +{ + no warnings 'redefine'; + local *Bracket::Service::BracketStructure::pick_targets = sub { + return { + total_picks => 0, + final4_picks => 'x', + region_picks_by_region => { + 2 => 0, + 5 => 7, + }, + }; + }; + + my $fallback = Bracket::Service::BracketStructure->submission_targets( + undef, + [2, 5, 9] + ); + is($fallback->{expected_total_picks}, 63, 'submission targets fallback total to 63'); + is($fallback->{expected_final4_picks}, 3, 'submission targets fallback final4 to 3'); + is_deeply($fallback->{region_ids}, [2, 5, 9], 'submission targets preserve provided region ids'); + is_deeply( + $fallback->{expected_region_picks_by_region}, + { + 2 => 15, + 5 => 7, + 9 => 15, + }, + 'submission targets fallback per-region expected picks to 15', + ); +} + done_testing();