Traveling: sort helpers <-> controllers

This commit is contained in:
Daniel Friesel 2022-07-30 10:47:18 +02:00
parent de73024f1b
commit c06d653195
No known key found for this signature in database
GPG key ID: 100D5BFB5166E005

View file

@ -14,6 +14,184 @@ use List::MoreUtils qw(first_index);
use Text::CSV; use Text::CSV;
use Travel::Status::DE::IRIS::Stations; use Travel::Status::DE::IRIS::Stations;
# Internal Helpers
sub has_str_in_list {
my ( $str, @strs ) = @_;
if ( List::Util::any { $str eq $_ } @strs ) {
return 1;
}
return;
}
sub get_connecting_trains {
my ( $self, %opt ) = @_;
my $uid = $opt{uid} //= $self->current_user->{id};
my $use_history = $self->users->use_history( uid => $uid );
my ( $eva, $exclude_via, $exclude_train_id, $exclude_before );
my $now = $self->now->epoch;
my ( $stationinfo, $arr_epoch, $arr_platform );
if ( $opt{eva} ) {
if ( $use_history & 0x01 ) {
$eva = $opt{eva};
}
elsif ( $opt{destination_name} ) {
$eva = $opt{eva};
}
}
else {
if ( $use_history & 0x02 ) {
my $status = $self->get_user_status;
$eva = $status->{arr_eva};
$exclude_via = $status->{dep_name};
$exclude_train_id = $status->{train_id};
$arr_platform = $status->{arr_platform};
$stationinfo = $status->{extra_data}{stationinfo_arr};
if ( $status->{real_arrival} ) {
$exclude_before = $arr_epoch = $status->{real_arrival}->epoch;
}
}
}
$exclude_before //= $now - 300;
if ( not $eva ) {
return;
}
my @destinations = $self->journeys->get_connection_targets(%opt);
if ($exclude_via) {
@destinations = grep { $_ ne $exclude_via } @destinations;
}
if ( not @destinations ) {
return;
}
my $stationboard = $self->iris->get_departures(
station => $eva,
lookbehind => 10,
lookahead => 40,
with_related => 1
);
if ( $stationboard->{errstr} ) {
return;
}
@{ $stationboard->{results} } = map { $_->[0] }
sort { $a->[1] <=> $b->[1] }
map { [ $_, $_->departure ? $_->departure->epoch : 0 ] }
@{ $stationboard->{results} };
my @results;
my @cancellations;
my %via_count = map { $_ => 0 } @destinations;
for my $train ( @{ $stationboard->{results} } ) {
if ( not $train->departure ) {
next;
}
if ( $exclude_before
and $train->departure
and $train->departure->epoch < $exclude_before )
{
next;
}
if ( $exclude_train_id
and $train->train_id eq $exclude_train_id )
{
next;
}
# In general, this function is meant to return feasible
# connections. However, cancelled connections may also be of
# interest and are also useful for logging cancellations.
# To satisfy both demands with (hopefully) little confusion and
# UI clutter, this function returns two concatenated arrays:
# actual connections (ordered by actual departure time) followed
# by cancelled connections (ordered by scheduled departure time).
# This is easiest to achieve in two separate loops.
#
# Note that a cancelled train may still have a matching destination
# in its route_post, e.g. if it leaves out $eva due to
# unscheduled route changes but continues on schedule afterwards
# -- so it is only cancelled at $eva, not on the remainder of
# the route. Also note that this specific case is not yet handled
# properly by the cancellation logic etc.
if ( $train->departure_is_cancelled ) {
my @via = ( $train->sched_route_post, $train->sched_route_end );
for my $dest (@destinations) {
if ( List::Util::any { $_ eq $dest } @via ) {
push( @cancellations, [ $train, $dest ] );
next;
}
}
}
else {
my @via = ( $train->route_post, $train->route_end );
for my $dest (@destinations) {
if ( $via_count{$dest} < 2
and List::Util::any { $_ eq $dest } @via )
{
push( @results, [ $train, $dest ] );
# Show all past and up to two future departures per destination
if ( not $train->departure
or $train->departure->epoch >= $now )
{
$via_count{$dest}++;
}
next;
}
}
}
}
@results = map { $_->[0] }
sort { $a->[1] <=> $b->[1] }
map {
[ $_, $_->[0]->departure->epoch // $_->[0]->sched_departure->epoch ]
} @results;
@cancellations = map { $_->[0] }
sort { $a->[1] <=> $b->[1] }
map { [ $_, $_->[0]->sched_departure->epoch ] } @cancellations;
for my $result (@results) {
my $train = $result->[0];
my @message_ids = List::Util::uniq map { $_->[1] } $train->raw_messages;
$train->{message_id} = { map { $_ => 1 } @message_ids };
my $interchange_duration;
if ( exists $stationinfo->{i} ) {
$interchange_duration
= $stationinfo->{i}{$arr_platform}{ $train->platform };
$interchange_duration //= $stationinfo->{i}{"*"};
}
if ( defined $interchange_duration ) {
my $interchange_time
= ( $train->departure->epoch - $arr_epoch ) / 60;
if ( $interchange_time < $interchange_duration ) {
$train->{interchange_text} = 'Anschluss knapp';
$train->{interchange_icon} = 'warning';
}
elsif ( $interchange_time == $interchange_duration ) {
$train->{interchange_text} = 'Anschluss könnte knapp werden';
$train->{interchange_icon} = 'directions_run';
}
#else {
# $train->{interchange_text} = 'Anschluss wird voraussichtlich erreicht';
# $train->{interchange_icon} = 'check';
#}
}
}
return ( @results, @cancellations );
}
# Controllers
sub homepage { sub homepage {
my ($self) = @_; my ($self) = @_;
if ( $self->is_user_authenticated ) { if ( $self->is_user_authenticated ) {
@ -852,14 +1030,6 @@ sub commute {
); );
} }
sub has_str_in_list {
my ( $str, @strs ) = @_;
if ( List::Util::any { $str eq $_ } @strs ) {
return 1;
}
return;
}
sub map_history { sub map_history {
my ($self) = @_; my ($self) = @_;
@ -1476,170 +1646,4 @@ sub add_journey_form {
} }
} }
sub get_connecting_trains {
my ( $self, %opt ) = @_;
my $uid = $opt{uid} //= $self->current_user->{id};
my $use_history = $self->users->use_history( uid => $uid );
my ( $eva, $exclude_via, $exclude_train_id, $exclude_before );
my $now = $self->now->epoch;
my ( $stationinfo, $arr_epoch, $arr_platform );
if ( $opt{eva} ) {
if ( $use_history & 0x01 ) {
$eva = $opt{eva};
}
elsif ( $opt{destination_name} ) {
$eva = $opt{eva};
}
}
else {
if ( $use_history & 0x02 ) {
my $status = $self->get_user_status;
$eva = $status->{arr_eva};
$exclude_via = $status->{dep_name};
$exclude_train_id = $status->{train_id};
$arr_platform = $status->{arr_platform};
$stationinfo = $status->{extra_data}{stationinfo_arr};
if ( $status->{real_arrival} ) {
$exclude_before = $arr_epoch = $status->{real_arrival}->epoch;
}
}
}
$exclude_before //= $now - 300;
if ( not $eva ) {
return;
}
my @destinations = $self->journeys->get_connection_targets(%opt);
if ($exclude_via) {
@destinations = grep { $_ ne $exclude_via } @destinations;
}
if ( not @destinations ) {
return;
}
my $stationboard = $self->iris->get_departures(
station => $eva,
lookbehind => 10,
lookahead => 40,
with_related => 1
);
if ( $stationboard->{errstr} ) {
return;
}
@{ $stationboard->{results} } = map { $_->[0] }
sort { $a->[1] <=> $b->[1] }
map { [ $_, $_->departure ? $_->departure->epoch : 0 ] }
@{ $stationboard->{results} };
my @results;
my @cancellations;
my %via_count = map { $_ => 0 } @destinations;
for my $train ( @{ $stationboard->{results} } ) {
if ( not $train->departure ) {
next;
}
if ( $exclude_before
and $train->departure
and $train->departure->epoch < $exclude_before )
{
next;
}
if ( $exclude_train_id
and $train->train_id eq $exclude_train_id )
{
next;
}
# In general, this function is meant to return feasible
# connections. However, cancelled connections may also be of
# interest and are also useful for logging cancellations.
# To satisfy both demands with (hopefully) little confusion and
# UI clutter, this function returns two concatenated arrays:
# actual connections (ordered by actual departure time) followed
# by cancelled connections (ordered by scheduled departure time).
# This is easiest to achieve in two separate loops.
#
# Note that a cancelled train may still have a matching destination
# in its route_post, e.g. if it leaves out $eva due to
# unscheduled route changes but continues on schedule afterwards
# -- so it is only cancelled at $eva, not on the remainder of
# the route. Also note that this specific case is not yet handled
# properly by the cancellation logic etc.
if ( $train->departure_is_cancelled ) {
my @via = ( $train->sched_route_post, $train->sched_route_end );
for my $dest (@destinations) {
if ( List::Util::any { $_ eq $dest } @via ) {
push( @cancellations, [ $train, $dest ] );
next;
}
}
}
else {
my @via = ( $train->route_post, $train->route_end );
for my $dest (@destinations) {
if ( $via_count{$dest} < 2
and List::Util::any { $_ eq $dest } @via )
{
push( @results, [ $train, $dest ] );
# Show all past and up to two future departures per destination
if ( not $train->departure
or $train->departure->epoch >= $now )
{
$via_count{$dest}++;
}
next;
}
}
}
}
@results = map { $_->[0] }
sort { $a->[1] <=> $b->[1] }
map {
[ $_, $_->[0]->departure->epoch // $_->[0]->sched_departure->epoch ]
} @results;
@cancellations = map { $_->[0] }
sort { $a->[1] <=> $b->[1] }
map { [ $_, $_->[0]->sched_departure->epoch ] } @cancellations;
for my $result (@results) {
my $train = $result->[0];
my @message_ids = List::Util::uniq map { $_->[1] } $train->raw_messages;
$train->{message_id} = { map { $_ => 1 } @message_ids };
my $interchange_duration;
if ( exists $stationinfo->{i} ) {
$interchange_duration
= $stationinfo->{i}{$arr_platform}{ $train->platform };
$interchange_duration //= $stationinfo->{i}{"*"};
}
if ( defined $interchange_duration ) {
my $interchange_time
= ( $train->departure->epoch - $arr_epoch ) / 60;
if ( $interchange_time < $interchange_duration ) {
$train->{interchange_text} = 'Anschluss knapp';
$train->{interchange_icon} = 'warning';
}
elsif ( $interchange_time == $interchange_duration ) {
$train->{interchange_text} = 'Anschluss könnte knapp werden';
$train->{interchange_icon} = 'directions_run';
}
#else {
# $train->{interchange_text} = 'Anschluss wird voraussichtlich erreicht';
# $train->{interchange_icon} = 'check';
#}
}
}
return ( @results, @cancellations );
}
1; 1;