From c06d653195edde4337971d441b48d14316858857 Mon Sep 17 00:00:00 2001 From: Daniel Friesel Date: Sat, 30 Jul 2022 10:47:18 +0200 Subject: Traveling: sort helpers <-> controllers --- lib/Travelynx/Controller/Traveling.pm | 352 +++++++++++++++++----------------- 1 file changed, 178 insertions(+), 174 deletions(-) (limited to 'lib/Travelynx/Controller') diff --git a/lib/Travelynx/Controller/Traveling.pm b/lib/Travelynx/Controller/Traveling.pm index 132b439..10f7278 100755 --- a/lib/Travelynx/Controller/Traveling.pm +++ b/lib/Travelynx/Controller/Traveling.pm @@ -14,6 +14,184 @@ use List::MoreUtils qw(first_index); use Text::CSV; 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 { my ($self) = @_; 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 { 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; -- cgit v1.2.3