From de074342de0bdc3604a0610a0c5f9cce5338bb85 Mon Sep 17 00:00:00 2001 From: Birte Kristina Friesel Date: Sun, 24 Sep 2023 18:19:17 +0200 Subject: add support for HAFAS connection suggestions; drop manual destination list Still TODO: handle stations that have several EVAs --- lib/Travelynx/Command/database.pm | 14 ++++ lib/Travelynx/Controller/Account.pm | 30 ++----- lib/Travelynx/Controller/Traveling.pm | 146 ++++++++++++++++----------------- lib/Travelynx/Model/Journeys.pm | 3 +- lib/Travelynx/Model/Users.pm | 27 +----- templates/_cancelled_departure.html.ep | 2 +- templates/_checked_in.html.ep | 14 ++-- templates/_checked_out.html.ep | 11 ++- templates/_connections.html.ep | 6 +- templates/_connections_hafas.html.ep | 59 +++++++++++++ templates/_transit_fyi.html.ep | 22 ----- templates/account.html.ep | 2 +- templates/departures.html.ep | 9 +- templates/use_history.html.ep | 24 ------ 14 files changed, 177 insertions(+), 192 deletions(-) create mode 100644 templates/_connections_hafas.html.ep delete mode 100644 templates/_transit_fyi.html.ep diff --git a/lib/Travelynx/Command/database.pm b/lib/Travelynx/Command/database.pm index c6880d0..b9074bc 100644 --- a/lib/Travelynx/Command/database.pm +++ b/lib/Travelynx/Command/database.pm @@ -1915,6 +1915,20 @@ my @migrations = ( } ); }, + + # v49 -> v50 + # travelynx 2.0 introduced proper HAFAS support, so there is no need for + # the 'FYI, here is some hAFAS data' kludge anymore. + sub { + my ($db) = @_; + $db->query( + qq{ + drop view user_transit; + drop table localtransit; + update schema_version set version = 50; + } + ); + }, ); sub sync_stations { diff --git a/lib/Travelynx/Controller/Account.pm b/lib/Travelynx/Controller/Account.pm index 2664329..0d12fb8 100644 --- a/lib/Travelynx/Controller/Account.pm +++ b/lib/Travelynx/Controller/Account.pm @@ -799,11 +799,8 @@ sub profile { sub insight { my ($self) = @_; - my $user = $self->current_user; - my ( $use_history, $destinations ) = $self->users->use_history( - uid => $user->{id}, - with_local_transit => 1 - ); + my $user = $self->current_user; + my $use_history = $self->users->use_history( uid => $user->{id} ); if ( $self->param('action') and $self->param('action') eq 'save' ) { if ( $self->param('on_departure') ) { @@ -820,31 +817,16 @@ sub insight { $use_history &= ~0x02; } - if ( $self->param('local_transit') ) { - $use_history |= 0x04; - } - else { - $use_history &= ~0x04; - } - - if ( $self->param('destinations') ) { - $destinations - = [ split( qr{\r?\n\r?}, $self->param('destinations') ) ]; - } - $self->users->use_history( - uid => $user->{id}, - set => $use_history, - destinations => $destinations + uid => $user->{id}, + set => $use_history ); $self->flash( success => 'use_history' ); $self->redirect_to('account'); } - $self->param( on_departure => $use_history & 0x01 ? 1 : 0 ); - $self->param( on_arrival => $use_history & 0x02 ? 1 : 0 ); - $self->param( local_transit => $use_history & 0x04 ? 1 : 0 ); - $self->param( destinations => join( "\n", @{$destinations} ) ); + $self->param( on_departure => $use_history & 0x01 ? 1 : 0 ); + $self->param( on_arrival => $use_history & 0x02 ? 1 : 0 ); $self->render('use_history'); } diff --git a/lib/Travelynx/Controller/Traveling.pm b/lib/Travelynx/Controller/Traveling.pm index 28427ad..35e8222 100755 --- a/lib/Travelynx/Controller/Traveling.pm +++ b/lib/Travelynx/Controller/Traveling.pm @@ -27,11 +27,8 @@ sub has_str_in_list { sub get_connecting_trains_p { my ( $self, %opt ) = @_; - my $uid = $opt{uid} //= $self->current_user->{id}; - my ( $use_history, $lt_stops ) = $self->users->use_history( - uid => $uid, - with_local_transit => 1 - ); + 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; @@ -68,13 +65,14 @@ sub get_connecting_trains_p { return $promise->reject; } - my @destinations = $self->journeys->get_connection_targets(%opt); + my @destinations + = uniq_by { $_->{name} } $self->journeys->get_connection_targets(%opt); if ($exclude_via) { - @destinations = grep { $_ ne $exclude_via } @destinations; + @destinations = grep { $_->{name} ne $exclude_via } @destinations; } - if ( not( @destinations or $use_history & 0x04 and @{$lt_stops} ) ) { + if ( not @destinations ) { return $promise->reject; } @@ -83,8 +81,11 @@ sub get_connecting_trains_p { = $can_check_in ? 40 : ( ( ${arr_countdown} // 0 ) / 60 + 40 ); my $iris_promise = Mojo::Promise->new; + my %via_count = map { $_->{name} => 0 } @destinations; - if (@destinations) { + if ( $eva >= 8000000 + and List::Util::any { $_->{eva} >= 8000000 } @destinations ) + { $self->iris->get_departures_p( station => $eva, lookbehind => 10, @@ -94,7 +95,7 @@ sub get_connecting_trains_p { sub { my ($stationboard) = @_; if ( $stationboard->{errstr} ) { - $iris_promise->reject( $stationboard->{errstr} ); + $iris_promise->resolve( [] ); return; } @@ -105,7 +106,6 @@ sub get_connecting_trains_p { my @results; my @cancellations; my $excluded_train; - my %via_count = map { $_ => 0 } @destinations; for my $train ( @{ $stationboard->{results} } ) { if ( not $train->departure ) { next; @@ -144,7 +144,7 @@ sub get_connecting_trains_p { $train->sched_route_post, $train->sched_route_end ); for my $dest (@destinations) { - if ( has_str_in_list( $dest, @via ) ) { + if ( has_str_in_list( $dest->{name}, @via ) ) { push( @cancellations, [ $train, $dest ] ); next; } @@ -153,8 +153,8 @@ sub get_connecting_trains_p { else { my @via = ( $train->route_post, $train->route_end ); for my $dest (@destinations) { - if ( $via_count{$dest} < 2 - and has_str_in_list( $dest, @via ) ) + if ( $via_count{ $dest->{name} } < 2 + and has_str_in_list( $dest->{name}, @via ) ) { push( @results, [ $train, $dest ] ); @@ -162,7 +162,7 @@ sub get_connecting_trains_p { if ( not $train->departure or $train->departure->epoch >= $now ) { - $via_count{$dest}++; + $via_count{ $dest->{name} }++; } next; } @@ -234,7 +234,7 @@ sub get_connecting_trains_p { } )->catch( sub { - $iris_promise->reject(@_); + $iris_promise->resolve( [] ); return; } )->wait; @@ -266,9 +266,9 @@ sub get_connecting_trains_p { Mojo::Promise->all( $iris_promise, $hafas_promise )->then( sub { my ( $iris, $hafas ) = @_; - my @iris_trains = @{ $iris->[0] }; - my @hafas_trains = @{ $hafas->[0] }; - my @transit_fyi; + my @iris_trains = @{ $iris->[0] }; + my @all_hafas_trains = @{ $hafas->[0] }; + my @hafas_trains; # We've already got a list of connecting trains; this function # only adds further information to them. We ignore errors, as @@ -278,11 +278,12 @@ sub get_connecting_trains_p { if ( $iris_train->[0]->departure_is_cancelled ) { next; } - for my $hafas_train (@hafas_trains) { + for my $hafas_train (@all_hafas_trains) { if ( $hafas_train->number and $hafas_train->number == $iris_train->[0]->train_no ) { + $hafas_train->{iris_seen} = 1; if ( $hafas_train->load and $hafas_train->load->{SECOND} ) { @@ -290,7 +291,8 @@ sub get_connecting_trains_p { } for my $stop ( $hafas_train->route ) { if ( $stop->{name} - and $stop->{name} eq $iris_train->[1] + and $stop->{name} eq + $iris_train->[1]->{name} and $stop->{arr} ) { $iris_train->[2] = $stop->{arr}; @@ -308,39 +310,29 @@ sub get_connecting_trains_p { } } } - if ( $use_history & 0x04 and @{$lt_stops} ) { - my %via_count = map { $_ => 0 } @{$lt_stops}; - for my $hafas_train (@hafas_trains) { - for my $stop ( $hafas_train->route ) { - for my $dest ( @{$lt_stops} ) { - if ( $stop->{name} - and $stop->{name} eq $dest - and $via_count{$dest} < 2 - and $hafas_train->datetime ) + for my $hafas_train (@all_hafas_trains) { + if ( $hafas_train->{iris_seen} ) { + next; + } + for my $stop ( $hafas_train->route ) { + for my $dest (@destinations) { + if ( $stop->{name} + and $stop->{name} eq $dest->{name} + and $via_count{ $dest->{name} } < 2 + and $hafas_train->datetime ) + { + my $departure = $hafas_train->datetime; + my $arrival = $stop->{arr}; + my $delay = $hafas_train->delay; + if ( $delay + and $stop->{arr} == $stop->{sched_arr} ) { - my $departure = $hafas_train->datetime; - my $arrival = $stop->{arr}; - my $delay = $hafas_train->delay; - if ( $delay - and $stop->{arr} == $stop->{sched_arr} ) - { - $arrival->add( minutes => $delay ); - } - if ( $departure->epoch >= $exclude_before ) - { - $via_count{$dest}++; - push( - @transit_fyi, - [ - { - line => $hafas_train->line, - departure => $departure, - departure_delay => $delay - }, - $dest, $arrival - ] - ); - } + $arrival->add( minutes => $delay ); + } + if ( $departure->epoch >= $exclude_before ) { + $via_count{ $dest->{name} }++; + push( @hafas_trains, + [ $hafas_train, $dest, $arrival ] ); } } } @@ -353,14 +345,12 @@ sub get_connecting_trains_p { ); } - $promise->resolve( \@iris_trains, \@transit_fyi ); + $promise->resolve( \@iris_trains, \@hafas_trains ); return; } )->catch( sub { my ($err) = @_; - - # TODO logging. HAFAS errors should never happen, IRIS errors are noteworthy too. $promise->reject($err); return; } @@ -401,13 +391,13 @@ sub homepage { $self->render_later; $self->get_connecting_trains_p->then( sub { - my ( $connecting_trains, $transit_fyi ) = @_; + my ( $connections_iris, $connections_hafas ) = @_; $self->render( 'landingpage', user_status => $status, journey_visibility => $journey_visibility, - connections => $connecting_trains, - transit_fyi => $transit_fyi, + connections_iris => $connections_iris, + connections_hafas => $connections_hafas, ); $self->users->mark_seen( uid => $uid ); } @@ -474,13 +464,13 @@ sub status_card { $self->render_later; $self->get_connecting_trains_p->then( sub { - my ( $connecting_trains, $transit_fyi ) = @_; + my ( $connections_iris, $connections_hafas ) = @_; $self->render( '_checked_in', journey => $status, journey_visibility => $journey_visibility, - connections => $connecting_trains, - transit_fyi => $transit_fyi + connections_iris => $connections_iris, + connections_hafas => $connections_hafas, ); } )->catch( @@ -510,8 +500,8 @@ sub status_card { my ($connecting_trains) = @_; $self->render( '_cancelled_departure', - journey => $status->{cancellation}, - connections => $connecting_trains + journey => $status->{cancellation}, + connections_iris => $connecting_trains ); } )->catch( @@ -529,11 +519,12 @@ sub status_card { $self->render_later; $self->get_connecting_trains_p->then( sub { - my ($connecting_trains) = @_; + my ( $connections_iris, $connections_hafas ) = @_; $self->render( '_checked_out', - journey => $status, - connections => $connecting_trains + journey => $status, + connections_iris => $connections_iris, + connections_hafas => $connections_hafas, ); } )->catch( @@ -1028,18 +1019,19 @@ sub station { if ($connections_p) { $connections_p->then( sub { - my ($connecting_trains) = @_; + my ( $connections_iris, $connections_hafas ) = @_; $self->render( 'departures', - eva => $status->{station_eva}, - results => \@results, - hafas => $use_hafas, - station => $status->{station_name}, - related_stations => $status->{related_stations}, - user_status => $user_status, - can_check_out => $can_check_out, - connections => $connecting_trains, - api_link => $api_link, + eva => $status->{station_eva}, + results => \@results, + hafas => $use_hafas, + station => $status->{station_name}, + related_stations => $status->{related_stations}, + user_status => $user_status, + can_check_out => $can_check_out, + connections_iris => $connections_iris, + connections_hafas => $connections_hafas, + api_link => $api_link, title => "travelynx: $status->{station_name}", ); } diff --git a/lib/Travelynx/Model/Journeys.pm b/lib/Travelynx/Model/Journeys.pm index 1c975f4..3fa2d96 100755 --- a/lib/Travelynx/Model/Journeys.pm +++ b/lib/Travelynx/Model/Journeys.pm @@ -1726,7 +1726,7 @@ sub get_connection_targets { my $min_count = $opt{min_count} // 3; if ( $opt{destination_name} ) { - return ( $opt{destination_name} ); + return ( { eva => $opt{eva}, name => $opt{destination_name} } ); } my $dest_id = $opt{eva} // $self->get_latest_dest_id(%opt); @@ -1755,7 +1755,6 @@ sub get_connection_targets { = $res->hashes->grep( sub { shift->{count} >= $min_count } ) ->map( sub { shift->{dest} } )->each; @destinations = $self->{stations}->get_by_evas(@destinations); - @destinations = map { $_->{name} } @destinations; return @destinations; } diff --git a/lib/Travelynx/Model/Users.pm b/lib/Travelynx/Model/Users.pm index 4b108d4..4602fa2 100644 --- a/lib/Travelynx/Model/Users.pm +++ b/lib/Travelynx/Model/Users.pm @@ -567,7 +567,6 @@ sub delete { $res{transit} = $db->delete( 'in_transit', { user_id => $uid } ); $res{hooks} = $db->delete( 'webhooks', { user_id => $uid } ); $res{trwl} = $db->delete( 'traewelling', { user_id => $uid } ); - $res{lt} = $db->delete( 'localtransit', { user_id => $uid } ); $res{password} = $db->delete( 'pending_passwords', { user_id => $uid } ); $res{relations} = $db->delete( 'relations', [ { subject_id => $uid }, { object_id => $uid } ] ); @@ -651,34 +650,12 @@ sub use_history { my $uid = $opt{uid}; my $value = $opt{set}; - if ( $opt{destinations} ) { - $db->insert( - 'localtransit', - { - user_id => $uid, - data => - JSON->new->encode( { destinations => $opt{destinations} } ) - }, - { on_conflict => \'(user_id) do update set data = EXCLUDED.data' } - ); - } - if ($value) { $db->update( 'users', { use_history => $value }, { id => $uid } ); } else { - if ( $opt{with_local_transit} ) { - my $res = $db->select( - 'user_transit', - [ 'use_history', 'data' ], - { id => $uid } - )->expand->hash; - return ( $res->{use_history}, $res->{data}{destinations} // [] ); - } - else { - return $db->select( 'users', ['use_history'], { id => $uid } ) - ->hash->{use_history}; - } + return $db->select( 'users', ['use_history'], { id => $uid } ) + ->hash->{use_history}; } } diff --git a/templates/_cancelled_departure.html.ep b/templates/_cancelled_departure.html.ep index 2f7f60b..79492a5 100644 --- a/templates/_cancelled_departure.html.ep +++ b/templates/_cancelled_departure.html.ep @@ -5,7 +5,7 @@ in <%= $journey->{dep_name} %> entfällt. Der Zugausfall auf der Fahrt nach <%= $journey->{arr_name} %> wurde bereits dokumentiert.

- % if (my @connections = @{stash('connections') // []}) { + % if (my @connections = @{stash('connections_iris') // []}) {

Alternative Reisemöglichkeiten:

%= include '_connections', connections => \@connections, checkin_from => $journey->{dep_eva}; % } diff --git a/templates/_checked_in.html.ep b/templates/_checked_in.html.ep index f3ecda4..2300ddd 100644 --- a/templates/_checked_in.html.ep +++ b/templates/_checked_in.html.ep @@ -199,19 +199,17 @@

% } - % if (my @connections = @{stash('connections') // []}) { + % if (@{stash('connections_iris') // [] } or @{stash('connections_hafas') // []}) { Verbindungen % if ($journey->{arrival_countdown} < 0) {

Fahrt auswählen zum Einchecken mit Zielwahl.

% } - %= include '_connections', connections => \@connections, checkin_from => $journey->{arrival_countdown} < 0 ? $journey->{arr_eva} : undef; - % } - % if (my @transit_fyi = @{stash('transit_fyi') // []}) { - Nahverkehr - % if ($journey->{arrival_countdown} < 0) { -

Nur zur Information – kein Checkin möglich.

+ % if (@{stash('connections_iris') // [] }) { + %= include '_connections', connections => stash('connections_iris'), checkin_from => $journey->{arrival_countdown} < 0 ? $journey->{arr_eva} : undef; + % } + % if (@{stash('connections_hafas') // [] }) { + %= include '_connections_hafas', connections => stash('connections_hafas'), checkin_from => $journey->{arrival_countdown} < 0 ? $journey->{arr_eva} : undef; % } - %= include '_transit_fyi', transit_fyi => \@transit_fyi; % } % if (defined $journey->{arrival_countdown} and $journey->{arrival_countdown} <= 0) {

diff --git a/templates/_checked_out.html.ep b/templates/_checked_out.html.ep index 55ced95..98db660 100644 --- a/templates/_checked_out.html.ep +++ b/templates/_checked_out.html.ep @@ -3,10 +3,15 @@ Ausgecheckt

Aus <%= $journey->{train_type} %> <%= $journey->{train_no} %> bis <%= $journey->{arr_name} %>

- % if (my @connections = @{stash('connections') // []}) { + % if (@{stash('connections_iris') // [] } or @{stash('connections_hafas') // []}) { Verbindungen -

Zug auswählen zum Einchecken mit Zielwahl.

- %= include '_connections', connections => \@connections, checkin_from => $journey->{arr_eva}; +

Fahrt auswählen zum Einchecken mit Zielwahl.

+ % if (@{stash('connections_iris') // [] }) { + %= include '_connections', connections => stash('connections_iris'), checkin_from => $journey->{arr_eva}; + % } + % if (@{stash('connections_hafas') // [] }) { + %= include '_connections_hafas', connections => stash('connections_hafas'), checkin_from => $journey->{arr_eva}; + % } % }
diff --git a/templates/_connections.html.ep b/templates/_connections.html.ep index e4f1872..98be766 100644 --- a/templates/_connections.html.ep +++ b/templates/_connections.html.ep @@ -9,7 +9,7 @@ % $link_class = 'action-cancelled-from'; % } % if ($checkin_from) { - + % } % else { @@ -33,10 +33,10 @@ % if ($checkin_from) { - <%= $via %> + <%= $via->{name} %> % } % else { - %= $via + %= $via->{name} % }
% if ($load) { diff --git a/templates/_connections_hafas.html.ep b/templates/_connections_hafas.html.ep new file mode 100644 index 0000000..9322116 --- /dev/null +++ b/templates/_connections_hafas.html.ep @@ -0,0 +1,59 @@ +
+ % for my $res (@{$connections}) { + % my ($train, $via, $via_arr) = @{$res}; + % $via_arr = $via_arr ? $via_arr->strftime('%H:%M') : q{}; + % my $td_class = ''; + % my $link_class = 'action-checkin'; + % if ($train->is_cancelled) { + % $td_class = 'cancelled'; + % $link_class = 'action-cancelled-from'; + % } + % if ($checkin_from) { + + % } + % else { + + % } + + + + + % } +
+ % if ($train->platform) { + % if ($checkin_from) { + Gleis <%= $train->platform %> + % } + % else { + Gleis <%= $train->platform %> + % } +
+ % } + % if ($checkin_from) { + <%= $train->line %> + % } + % else { + %= $train->line + % } +
+ % if ($checkin_from) { + <%= $via->{name} %> + % } + % else { + %= $via->{name} + % } + + % if ($train->is_cancelled) { + %= $train->sched_datetime->strftime('%H:%M') + ⊖ + % } + % else { + %= $train->datetime->strftime('%H:%M') + % } + % if ($via_arr) { + → <%= $via_arr %> + % } + % if ($train->delay) { + %= sprintf('(%+d)', $train->delay) + % } +
diff --git a/templates/_transit_fyi.html.ep b/templates/_transit_fyi.html.ep deleted file mode 100644 index 5951e7d..0000000 --- a/templates/_transit_fyi.html.ep +++ /dev/null @@ -1,22 +0,0 @@ - - % for my $res (@{$transit_fyi}) { - % my ($info, $via, $via_arr) = @{$res}; - % $via_arr = $via_arr ? $via_arr->strftime('%H:%M') : q{}; - - - - - - % } -
- %= $info->{line} - - %= $via - - %= $info->{departure}->strftime('%H:%M') - → - %= $via_arr - % if ($info->{departure_delay}) { - %= sprintf('(%+d)', $info->{departure_delay}) - % } -
diff --git a/templates/account.html.ep b/templates/account.html.ep index fac5817..7f689c2 100644 --- a/templates/account.html.ep +++ b/templates/account.html.ep @@ -67,7 +67,7 @@ Verbindungen edit - % if ($use_history & 0x07) { + % if ($use_history & 0x03) { Vorschläge aktiv % } % else { diff --git a/templates/departures.html.ep b/templates/departures.html.ep index 3a2516f..021e9ca 100644 --- a/templates/departures.html.ep +++ b/templates/departures.html.ep @@ -72,12 +72,17 @@
% } -% elsif (not param('train') and my @connections = @{stash('connections') // []}) { +% elsif (not param('train') and (@{stash('connections_iris') // []} or @{stash('connections_hafas') // []}) ) { % $have_connections = 1;

Häufig genutzte Verbindungen – Fahrt auswählen zum Einchecken mit Zielwahl

- %= include '_connections', connections => \@connections, checkin_from => $eva; + % if (@{stash('connections_iris') // []}) { + %= include '_connections', connections => stash('connections_iris'), checkin_from => $eva; + % } + % if (@{stash('connections_hafas') // []}) { + %= include '_connections_hafas', connections => stash('connections_hafas'), checkin_from => $eva; + % }
% } diff --git a/templates/use_history.html.ep b/templates/use_history.html.ep index e90f17f..9b76e98 100644 --- a/templates/use_history.html.ep +++ b/templates/use_history.html.ep @@ -47,30 +47,6 @@ ohne Umweg über die Abfahrtstafel möglich. -
-
- -
-
-
-
- Zeige beim Reisestatus zusätzlich Anschlussmöglichkeiten an den - Nahverkehr. Diese dienen lediglich zur Information; ein Checkin ist - nicht möglich. Es werden nur Anschlussmöglichkeiten zu Zielen - angezeigt, die im folgenden Feld gelistet sind (ein Ziel pro - Zeile, z.B. „Eichlinghofen H-Bahn, Dortmund“). Falls travelynx in - Zukunft eine Möglichkeit für Checkins in Nahverkehrsmittel erhält, - wird diese Liste ggf. gelöscht. -
-
-
-
- %= text_area 'destinations', id => 'destinations', class => 'materialize-textarea' -
-
-- cgit v1.2.3