diff options
Diffstat (limited to 'lib/Travelynx.pm')
-rwxr-xr-x | lib/Travelynx.pm | 1472 |
1 files changed, 1037 insertions, 435 deletions
diff --git a/lib/Travelynx.pm b/lib/Travelynx.pm index 4749d65..78642ff 100755 --- a/lib/Travelynx.pm +++ b/lib/Travelynx.pm @@ -1,6 +1,7 @@ package Travelynx; # Copyright (C) 2020-2023 Birte Kristina Friesel +# Copyright (C) 2025 networkException <git@nwex.de> # # SPDX-License-Identifier: AGPL-3.0-or-later @@ -19,10 +20,13 @@ use JSON; use List::Util; use List::UtilsBy qw(uniq_by); use List::MoreUtils qw(first_index); -use Travel::Status::DE::DBWagenreihung; +use Travel::Status::DE::DBRIS::Formation; use Travelynx::Helper::DBDB; +use Travelynx::Helper::DBRIS; +use Travelynx::Helper::EFA; use Travelynx::Helper::HAFAS; use Travelynx::Helper::IRIS; +use Travelynx::Helper::MOTIS; use Travelynx::Helper::Sendmail; use Travelynx::Helper::Traewelling; use Travelynx::Model::InTransit; @@ -157,11 +161,12 @@ sub startup { cache_iris_main => sub { my ($self) = @_; - return Cache::File->new( + state $cache = Cache::File->new( cache_root => $self->app->config->{cache}->{schedule}, default_expires => '6 hours', lock_level => Cache::File::LOCK_LOCAL(), ); + return $cache; } ); @@ -169,22 +174,12 @@ sub startup { cache_iris_rt => sub { my ($self) = @_; - return Cache::File->new( + state $cache = Cache::File->new( cache_root => $self->app->config->{cache}->{realtime}, default_expires => '70 seconds', lock_level => Cache::File::LOCK_LOCAL(), ); - } - ); - - $self->attr( - coordinates_by_station => sub { - my $legacy_names = $self->app->renamed_station; - my $location = $self->stations->get_latlon_by_name; - while ( my ( $old_name, $new_name ) = each %{$legacy_names} ) { - $location->{$old_name} = $location->{$new_name}; - } - return $location; + return $cache; } ); @@ -192,15 +187,17 @@ sub startup { # via https://github.com/marudor/bahn.expert/blob/main/src/server/coachSequence/TrainNames.ts $self->attr( ice_name => sub { - my $id_to_name = JSON->new->utf8->decode( - scalar read_file('share/ice_names.json') ); + state $id_to_name = { + Travel::Status::DE::DBRIS::Formation::Group::name_to_designation( + ) + }; return $id_to_name; } ); $self->attr( renamed_station => sub { - my $legacy_to_new = JSON->new->utf8->decode( + state $legacy_to_new = JSON->new->utf8->decode( scalar read_file('share/old_station_names.json') ); return $legacy_to_new; } @@ -226,10 +223,39 @@ sub startup { ); $self->helper( + efa => sub { + my ($self) = @_; + state $efa = Travelynx::Helper::EFA->new( + log => $self->app->log, + main_cache => $self->app->cache_iris_main, + realtime_cache => $self->app->cache_iris_rt, + root_url => $self->base_url_for('/')->to_abs, + user_agent => $self->ua, + version => $self->app->config->{version}, + ); + } + ); + + $self->helper( + dbris => sub { + my ($self) = @_; + state $dbris = Travelynx::Helper::DBRIS->new( + log => $self->app->log, + service_config => $self->app->config->{dbris}, + cache => $self->app->cache_iris_rt, + root_url => $self->base_url_for('/')->to_abs, + user_agent => $self->ua, + version => $self->app->config->{version}, + ); + } + ); + + $self->helper( hafas => sub { my ($self) = @_; state $hafas = Travelynx::Helper::HAFAS->new( log => $self->app->log, + service_config => $self->app->config->{hafas}, main_cache => $self->app->cache_iris_main, realtime_cache => $self->app->cache_iris_rt, root_url => $self->base_url_for('/')->to_abs, @@ -253,6 +279,20 @@ sub startup { ); $self->helper( + motis => sub { + my ($self) = @_; + state $motis = Travelynx::Helper::MOTIS->new( + log => $self->app->log, + cache => $self->app->cache_iris_rt, + user_agent => $self->ua, + root_url => $self->base_url_for('/')->to_abs, + version => $self->app->config->{version}, + time_zone => 'Europe/Berlin', + ); + } + ); + + $self->helper( traewelling => sub { my ($self) = @_; state $trwl = Travelynx::Model::Traewelling->new( pg => $self->pg ); @@ -297,13 +337,12 @@ sub startup { journeys => sub { my ($self) = @_; state $journeys = Travelynx::Model::Journeys->new( - log => $self->app->log, - pg => $self->pg, - in_transit => $self->in_transit, - stats_cache => $self->journey_stats_cache, - renamed_station => $self->app->renamed_station, - latlon_by_station => $self->app->coordinates_by_station, - stations => $self->stations, + log => $self->app->log, + pg => $self->pg, + in_transit => $self->in_transit, + stats_cache => $self->journey_stats_cache, + renamed_station => $self->app->renamed_station, + stations => $self->stations, ); } ); @@ -362,11 +401,12 @@ sub startup { dbdb => sub { my ($self) = @_; state $dbdb = Travelynx::Helper::DBDB->new( - log => $self->app->log, - cache => $self->app->cache_iris_main, - root_url => $self->base_url_for('/')->to_abs, - user_agent => $self->ua, - version => $self->app->config->{version}, + log => $self->app->log, + main_cache => $self->app->cache_iris_main, + realtime_cache => $self->app->cache_iris_rt, + root_url => $self->base_url_for('/')->to_abs, + user_agent => $self->ua, + version => $self->app->config->{version}, ); } ); @@ -408,11 +448,45 @@ sub startup { ); $self->helper( + 'efa_load_icon' => sub { + my ( $self, $occupancy ) = @_; + + my @symbols + = ( + qw(help_outline person_outline people priority_high not_interested) + ); + + if ( $occupancy eq 'MANY_SEATS' ) { + $occupancy = 1; + } + elsif ( $occupancy eq 'FEW_SEATS' ) { + $occupancy = 2; + } + elsif ( $occupancy eq 'STANDING_ONLY' ) { + $occupancy = 3; + } + elsif ( $occupancy eq 'FULL' ) { + $occupancy = 4; + } + + return $symbols[$occupancy] // 'help_outline'; + } + ); + + $self->helper( 'load_icon' => sub { my ( $self, $load ) = @_; my $first = $load->{FIRST} // 0; my $second = $load->{SECOND} // 0; + # DBRIS + if ( $first == 99 ) { + $first = 4; + } + if ( $second == 99 ) { + $second = 4; + } + my @symbols = ( qw(help_outline person_outline people priority_high not_interested) @@ -450,6 +524,7 @@ sub startup { my $station = $opt{station}; my $train_id = $opt{train_id}; + my $ts = $opt{ts}; my $uid = $opt{uid} // $self->current_user->{id}; my $db = $opt{db} // $self->pg->db; my $hafas; @@ -459,9 +534,18 @@ sub startup { return Mojo::Promise->reject('You are already checked in'); } - if ( $train_id =~ m{[|]} ) { + if ( $opt{dbris} ) { + return $self->_checkin_dbris_p(%opt); + } + if ( $opt{efa} ) { + return $self->_checkin_efa_p(%opt); + } + if ( $opt{hafas} ) { return $self->_checkin_hafas_p(%opt); } + if ( $opt{motis} ) { + return $self->_checkin_motis_p(%opt); + } my $promise = Mojo::Promise->new; @@ -493,7 +577,9 @@ sub startup { db => $db, departure_eva => $eva, train => $train, - route => [ $self->iris->route_diff($train) ], + route => [ $self->iris->route_diff($train) ], + backend_id => + $self->stations->get_backend_id( iris => 1 ), ); }; if ($@) { @@ -506,6 +592,17 @@ sub startup { # mustn't be called during a transaction if ( not $opt{in_transaction} ) { $self->add_route_timestamps( $uid, $train, 1 ); + $self->add_wagonorder( + uid => $uid, + train_id => $train->train_id, + is_departure => 1, + eva => $eva, + datetime => $train->sched_departure, + train_type => $train->type, + train_no => $train->train_no + ); + $self->add_stationinfo( $uid, 1, $train->train_id, + $eva ); $self->run_hook( $uid, 'checkin' ); } @@ -525,18 +622,444 @@ sub startup { ); $self->helper( - '_checkin_hafas_p' => sub { + '_checkin_motis_p' => sub { my ( $self, %opt ) = @_; my $station = $opt{station}; my $train_id = $opt{train_id}; + my $ts = $opt{ts}; my $uid = $opt{uid} // $self->current_user->{id}; my $db = $opt{db} // $self->pg->db; my $hafas; my $promise = Mojo::Promise->new; + $self->motis->get_trip_p( + service => $opt{motis}, + trip_id => $train_id, + )->then( + sub { + my ($trip) = @_; + my $found_stopover; + + for my $stopover ( $trip->stopovers ) { + if ( $stopover->stop->id eq $station ) { + $found_stopover = $stopover; + + # Lines may serve the same stop several times. + # Keep looking until the scheduled departure + # matches the one passed while checking in. + if ( $ts + and $stopover->scheduled_departure->epoch + == $ts ) + { + last; + } + } + } + + if ( not $found_stopover ) { + $promise->reject( +"Did not find stopover at '$station' within trip '$train_id'" + ); + return; + } + + for my $stopover ( $trip->stopovers ) { + $self->stations->add_or_update( + stop => $stopover->stop, + db => $db, + motis => $opt{motis}, + ); + } + + $self->stations->add_or_update( + stop => $found_stopover->stop, + db => $db, + motis => $opt{motis}, + ); + + eval { + $self->in_transit->add( + uid => $uid, + db => $db, + journey => $trip, + stopover => $found_stopover, + data => { trip_id => $train_id }, + backend_id => $self->stations->get_backend_id( + motis => $opt{motis} + ), + ); + }; + + if ($@) { + $self->app->log->error( + "Checkin($uid): INSERT failed: $@"); + $promise->reject( 'INSERT failed: ' . $@ ); + return; + } + + my $polyline; + if ( $trip->polyline ) { + my @station_list; + my @coordinate_list; + for my $coordinate ( $trip->polyline ) { + if ( $coordinate->{stop} ) { + if ( not defined $coordinate->{stop}->{eva} ) { + die(); + } + + push( + @coordinate_list, + [ + $coordinate->{lon}, + $coordinate->{lat}, + $coordinate->{stop}->{eva} + ] + ); + + push( @station_list, + $coordinate->{stop}->name ); + } + else { + push( @coordinate_list, + [ $coordinate->{lon}, $coordinate->{lat} ] + ); + } + } + + # equal length → polyline only consists of straight + # lines between stops. that's not helpful. + if ( @station_list == @coordinate_list ) { + $self->log->debug( 'Ignoring polyline for ' + . $trip->route_name + . ' as it only consists of straight lines between stops.' + ); + } + else { + $polyline = { + from_eva => + ( $trip->stopovers )[0]->stop->{eva}, + to_eva => ( $trip->stopovers )[-1]->stop->{eva}, + coords => \@coordinate_list, + }; + } + } + + if ($polyline) { + $self->in_transit->set_polyline( + uid => $uid, + db => $db, + polyline => $polyline, + ); + } + + # mustn't be called during a transaction + if ( not $opt{in_transaction} ) { + $self->run_hook( $uid, 'checkin' ); + } + + $promise->resolve($trip); + } + )->catch( + sub { + my ($err) = @_; + $promise->reject($err); + return; + } + )->wait; + + return $promise; + } + ); + + $self->helper( + '_checkin_dbris_p' => sub { + my ( $self, %opt ) = @_; + + my $station = $opt{station}; + my $train_id = $opt{train_id}; + my $train_suffix = $opt{train_suffix}; + my $ts = $opt{ts}; + my $uid = $opt{uid} // $self->current_user->{id}; + my $db = $opt{db} // $self->pg->db; + my $hafas; + + my $promise = Mojo::Promise->new; + + $self->dbris->get_journey_p( + trip_id => $train_id, + with_polyline => 1 + )->then( + sub { + my ($journey) = @_; + my $found; + for my $stop ( $journey->route ) { + if ( $stop->eva eq $station ) { + $found = $stop; + + # Lines may serve the same stop several times. + # Keep looking until the scheduled departure + # matches the one passed while checking in. + if ( $ts and $stop->sched_dep->epoch == $ts ) { + last; + } + } + } + if ( not $found ) { + $promise->reject( +"Did not find stop '$station' within journey '$train_id'" + ); + return; + } + for my $stop ( $journey->route ) { + $self->stations->add_or_update( + stop => $stop, + db => $db, + dbris => 'bahn.de', + ); + } + eval { + $self->in_transit->add( + uid => $uid, + db => $db, + journey => $journey, + stop => $found, + data => { trip_id => $train_id }, + backend_id => $self->stations->get_backend_id( + dbris => 'bahn.de' + ), + train_suffix => $train_suffix, + ); + }; + if ($@) { + $self->app->log->error( + "Checkin($uid): INSERT failed: $@"); + $promise->reject( 'INSERT failed: ' . $@ ); + return; + } + + my $polyline; + if ( $journey->polyline ) { + my @station_list; + my @coordinate_list; + for my $coord ( $journey->polyline ) { + if ( $coord->{stop} ) { + push( + @coordinate_list, + [ + $coord->{lon}, $coord->{lat}, + $coord->{stop}->eva + ] + ); + push( @station_list, $coord->{stop}->name ); + } + else { + push( @coordinate_list, + [ $coord->{lon}, $coord->{lat} ] ); + } + } + + # equal length → polyline only consists of straight + # lines between stops. that's not helpful. + if ( @station_list == @coordinate_list ) { + $self->log->debug( 'Ignoring polyline for ' + . $journey->train + . ' as it only consists of straight lines between stops.' + ); + } + else { + $polyline = { + from_eva => ( $journey->route )[0]->eva, + to_eva => ( $journey->route )[-1]->eva, + coords => \@coordinate_list, + }; + } + } + + if ($polyline) { + $self->in_transit->set_polyline( + uid => $uid, + db => $db, + polyline => $polyline, + ); + } + + # mustn't be called during a transaction + if ( not $opt{in_transaction} ) { + $self->run_hook( $uid, 'checkin' ); + $self->add_wagonorder( + uid => $uid, + train_id => $train_id, + is_departure => 1, + eva => $found->eva, + datetime => $found->sched_dep, + train_type => $journey->type, + train_no => $journey->train_no, + ); + $self->add_stationinfo( $uid, 1, $train_id, + $found->eva ); + } + + $promise->resolve($journey); + } + )->catch( + sub { + my ($err) = @_; + $promise->reject($err); + return; + } + )->wait; + + return $promise; + } + ); + + $self->helper( + '_checkin_efa_p' => sub { + my ( $self, %opt ) = @_; + my $station = $opt{station}; + my $trip_id = $opt{train_id}; + my $ts = $opt{ts}; + my $uid = $opt{uid} // $self->current_user->{id}; + my $db = $opt{db} // $self->pg->db; + + my $promise = Mojo::Promise->new; + $self->efa->get_journey_p( + service => $opt{efa}, + trip_id => $trip_id + )->then( + sub { + my ($journey) = @_; + + my $found; + for my $stop ( $journey->route ) { + if ( $stop->id_num == $station ) { + $found = $stop; + + # Lines may serve the same stop several times. + # Keep looking until the scheduled departure + # matches the one passed while checking in. + if ( $ts and $stop->sched_dep->epoch == $ts ) { + last; + } + } + } + if ( not $found ) { + $promise->reject( +"Did not find stop '$station' within journey '$trip_id'" + ); + return; + } + + for my $stop ( $journey->route ) { + $self->stations->add_or_update( + stop => $stop, + db => $db, + efa => $opt{efa}, + ); + } + + eval { + $self->in_transit->add( + uid => $uid, + db => $db, + journey => $journey, + stop => $found, + trip_id => $trip_id, + backend_id => $self->stations->get_backend_id( + efa => $opt{efa} + ), + ); + }; + if ($@) { + $self->app->log->error( + "Checkin($uid): INSERT failed: $@"); + $promise->reject( 'INSERT failed: ' . $@ ); + return; + } + + my $polyline; + if ( $journey->polyline ) { + my @station_list; + my @coordinate_list; + for my $coord ( $journey->polyline ) { + if ( $coord->{stop} ) { + push( + @coordinate_list, + [ + $coord->{lon}, $coord->{lat}, + $coord->{stop}->id_num + ] + ); + push( @station_list, + $coord->{stop}->full_name ); + } + else { + push( @coordinate_list, + [ $coord->{lon}, $coord->{lat} ] ); + } + } + + # equal length → polyline only consists of straight + # lines between stops. that's not helpful. + if ( @station_list == @coordinate_list ) { + $self->log->debug( 'Ignoring polyline for ' + . $journey->line + . ' as it only consists of straight lines between stops.' + ); + } + else { + $polyline = { + from_eva => ( $journey->route )[0]->id_num, + to_eva => ( $journey->route )[-1]->id_num, + coords => \@coordinate_list, + }; + } + } + + if ($polyline) { + $self->in_transit->set_polyline( + uid => $uid, + db => $db, + polyline => $polyline, + ); + } + + # mustn't be called during a transaction + if ( not $opt{in_transaction} ) { + $self->run_hook( $uid, 'checkin' ); + } + + $promise->resolve($journey); + + return; + } + )->catch( + sub { + my ($err) = @_; + $promise->reject($err); + return; + } + )->wait; + return $promise; + } + ); + + $self->helper( + '_checkin_hafas_p' => sub { + my ( $self, %opt ) = @_; + + my $station = $opt{station}; + my $train_id = $opt{train_id}; + my $ts = $opt{ts}; + my $uid = $opt{uid} // $self->current_user->{id}; + my $db = $opt{db} // $self->pg->db; + + my $promise = Mojo::Promise->new; + $self->hafas->get_journey_p( + service => $opt{hafas}, trip_id => $train_id, with_polyline => 1 )->then( @@ -548,26 +1071,38 @@ sub startup { or $stop->loc->eva == $station ) { $found = $stop; - last; + + # Lines may serve the same stop several times. + # Keep looking until the scheduled departure + # matches the one passed while checking in. + if ( $ts and $stop->sched_dep->epoch == $ts ) { + last; + } } } if ( not $found ) { $promise->reject( - "Did not find journey $train_id at $station"); +"Did not find stop '$station' within journey '$train_id'" + ); return; } for my $stop ( $journey->route ) { $self->stations->add_or_update( - stop => $stop, - db => $db, + stop => $stop, + db => $db, + hafas => $opt{hafas}, ); } eval { $self->in_transit->add( - uid => $uid, - db => $db, - journey => $journey, - stop => $found, + uid => $uid, + db => $db, + journey => $journey, + stop => $found, + data => { trip_id => $journey->id }, + backend_id => $self->stations->get_backend_id( + hafas => $opt{hafas} + ), ); }; if ($@) { @@ -576,11 +1111,6 @@ sub startup { $promise->reject( 'INSERT failed: ' . $@ ); return; } - $self->in_transit->update_data( - uid => $uid, - db => $db, - data => { trip_id => $journey->id } - ); my $polyline; if ( $journey->polyline ) { @@ -631,6 +1161,19 @@ sub startup { # mustn't be called during a transaction if ( not $opt{in_transaction} ) { $self->run_hook( $uid, 'checkin' ); + if ( $opt{hafas} eq 'DB' and $journey->class <= 16 ) { + $self->add_wagonorder( + uid => $uid, + train_id => $journey->id, + is_departure => 1, + eva => $found->loc->eva, + datetime => $found->sched_dep, + train_type => $journey->type, + train_no => $journey->number + ); + $self->add_stationinfo( $uid, 1, $journey->id, + $found->loc->eva ); + } } $promise->resolve($journey); @@ -749,6 +1292,7 @@ sub startup { my $db = $opt{db} // $self->pg->db; my $user = $self->get_user_status( $uid, $db ); my $train_id = $user->{train_id}; + my $hafas = $opt{hafas}; my $promise = Mojo::Promise->new; @@ -759,8 +1303,7 @@ sub startup { } if ( not $user->{checked_in} and not $user->{cancelled} ) { - return $promise->resolve( 0, - 'You are not checked into any train' ); + return $promise->resolve( 0, 'You are not checked in' ); } if ( $dep_eva and $dep_eva != $user->{dep_eva} ) { @@ -770,8 +1313,13 @@ sub startup { return $promise->resolve( 0, 'race condition' ); } - if ( $train_id =~ m{[|]} ) { - return $self->_checkout_hafas_p(%opt); + if ( $user->{is_dbris} + or $user->{is_efa} + or $user->{is_hafas} + or $user->{is_motis} + or $train_id eq 'manual' ) + { + return $self->_checkout_journey_p(%opt); } my $now = DateTime->now( time_zone => 'Europe/Berlin' ); @@ -893,7 +1441,6 @@ sub startup { uid => $uid, db => $db, train => $train, - route => [ $self->iris->route_diff($train) ] ); $has_arrived @@ -905,7 +1452,7 @@ sub startup { if (@unknown_stations) { $self->app->log->warn( sprintf( -'Route of %s %s (%s -> %s) contains unknown stations: %s', +'IRIS: Route of %s %s (%s -> %s) contains unknown stations: %s', $train->type, $train->train_no, $train->origin, @@ -992,6 +1539,17 @@ sub startup { if ( not $opt{in_transaction} ) { $self->run_hook( $uid, 'update' ); $self->add_route_timestamps( $uid, $train, 0, 1 ); + $self->add_wagonorder( + uid => $uid, + train_id => $train->train_id, + is_arrival => 1, + eva => $new_checkout_station_id, + datetime => $train->sched_departure, + train_type => $train->type, + train_no => $train->train_no + ); + $self->add_stationinfo( $uid, 0, $train->train_id, + $dep_eva, $new_checkout_station_id ); } $promise->resolve( 1, undef ); return; @@ -1010,7 +1568,7 @@ sub startup { ); $self->helper( - '_checkout_hafas_p' => sub { + '_checkout_journey_p' => sub { my ( $self, %opt ) = @_; my $station = $opt{station}; @@ -1036,7 +1594,7 @@ sub startup { my $has_arrived; for my $stop ( @{ $journey->{route_after} } ) { if ( $station eq $stop->[0] or $station eq $stop->[1] ) { - $found = 1; + $found = $stop; $self->in_transit->set_arrival_eva( uid => $uid, db => $db, @@ -1057,6 +1615,13 @@ sub startup { rt_arrival => ( $stop->[2]{rt_arr} || $stop->[2]{sched_arr} ) ); + if ($stop->[2]{platform}) { + $self->in_transit->set_arrival_platform( + uid => $uid, + db => $db, + arrival_platform => $stop->[2]{platform} + ); + } if ( $now > ( $stop->[2]{rt_arr} || $stop->[2]{sched_arr} ) ) { @@ -1065,7 +1630,7 @@ sub startup { last; } } - if ( not $found ) { + if ( not $found and not $force ) { return $promise->resolve( 1, 'station not found in route' ); } @@ -1104,6 +1669,22 @@ sub startup { uid => $uid ); } + elsif ( $found and $found->[2]{isCancelled} ) { + $journey = $self->in_transit->get( + uid => $uid, + db => $db + ); + $journey->{cancelled} = 1; + $self->journeys->add_from_in_transit( + db => $db, + journey => $journey + ); + $self->in_transit->set_cancelled_destination( + uid => $uid, + db => $db, + cancelled_destination => $found->[0], + ); + } if ($tx) { $tx->commit; @@ -1218,6 +1799,123 @@ sub startup { } ); + $self->helper( + 'add_wagonorder' => sub { + my ( $self, %opt ) = @_; + + my $uid = $opt{uid}; + my $train_id = $opt{train_id}; + my $train_type = $opt{train_type}; + my $train_no = $opt{train_no}; + my $eva = $opt{eva}; + my $datetime = $opt{datetime}; + + $uid //= $self->current_user->{id}; + + my $db = $self->pg->db; + + if ( $datetime and $train_no ) { + $self->dbdb->has_wagonorder_p(%opt)->then( + sub { + return $self->dbdb->get_wagonorder_p(%opt); + } + )->then( + sub { + my ($wagonorder) = @_; + + my $data = {}; + my $user_data = {}; + + my $wr; + eval { + $wr + = Travel::Status::DE::DBRIS::Formation->new( + json => $wagonorder ); + }; + + if ( $opt{is_departure} + and $wr + and not exists $wagonorder->{error} ) + { + my $dt + = $opt{datetime}->clone->set_time_zone('UTC'); + $data->{wagonorder_dep} = $wagonorder; + $data->{wagonorder_param} = { + time => $dt->rfc3339 =~ s{(?=Z)}{.000}r, + number => $opt{train_no}, + evaNumber => $opt{eva}, + administrationId => 80, + date => $dt->strftime('%Y-%m-%d'), + category => $opt{train_type}, + }; + $user_data->{wagongroups} = []; + for my $group ( $wr->groups ) { + my @wagons; + for my $wagon ( $group->carriages ) { + push( + @wagons, + { + id => $wagon->uic_id, + number => $wagon->number, + type => $wagon->type, + } + ); + } + push( + @{ $user_data->{wagongroups} }, + { + name => $group->name, + desc => $group->desc_short, + description => $group->description, + designation => $group->designation, + to => $group->destination, + type => $group->train_type, + no => $group->train_no, + wagons => [@wagons], + } + ); + if ( $group->{name} + and $group->{name} eq 'ICE0304' ) + { + $data->{wagonorder_pride} = 1; + } + } + $self->in_transit->update_data( + uid => $uid, + db => $db, + data => $data, + train_id => $train_id, + ); + $self->in_transit->update_user_data( + uid => $uid, + db => $db, + user_data => $user_data, + train_id => $train_id, + ); + } + elsif ( $opt{is_arrival} + and not exists $wagonorder->{error} ) + { + $data->{wagonorder_arr} = $wagonorder; + $self->in_transit->update_data( + uid => $uid, + db => $db, + data => $data, + train_id => $train_id, + ); + } + return; + } + )->catch( + sub { + # no wagonorder? no problem. + return; + } + )->wait; + } + } + ); + # This helper is only ever called from an IRIS context. # HAFAS already has all relevant information. $self->helper( @@ -1241,19 +1939,33 @@ sub startup { return; } - my $route = $in_transit->{route}; + my $route = $in_transit->{route}; + my $train_id = $train->train_id; + + my $tripid_promise; + + if ( $in_transit->{data}{trip_id} ) { + $tripid_promise + = Mojo::Promise->resolve( $in_transit->{data}{trip_id} ); + } + else { + $tripid_promise = $self->hafas->get_tripid_p( train => $train ); + } - $self->hafas->get_tripid_p( train => $train )->then( + $tripid_promise->then( sub { my ($trip_id) = @_; - $self->in_transit->update_data( - uid => $uid, - db => $db, - data => { trip_id => $trip_id } - ); + if ( not $in_transit->{extra_data}{trip_id} ) { + $self->in_transit->update_data( + uid => $uid, + db => $db, + data => { trip_id => $trip_id }, + train_id => $train_id, + ); + } - return $self->hafas->get_route_timestamps_p( + return $self->hafas->get_route_p( train => $train, trip_id => $trip_id, with_polyline => ( @@ -1264,42 +1976,63 @@ sub startup { } )->then( sub { - my ( $route_data, $journey, $polyline ) = @_; + my ( $new_route, $journey, $polyline ) = @_; + my $db_route; - for my $station ( @{$route} ) { - if ( $station->[0] - =~ m{^Betriebsstelle nicht bekannt (\d+)$} ) - { - my $eva = $1; - if ( $route_data->{$eva} ) { - $station->[0] = $route_data->{$eva}{name}; - $station->[1] = $route_data->{$eva}{eva}; - } - } - if ( my $sd = $route_data->{ $station->[0] } ) { - $station->[1] = $sd->{eva}; - if ( $station->[2]{isAdditional} ) { - $sd->{isAdditional} = 1; - } - if ( $station->[2]{isCancelled} ) { - $sd->{isCancelled} = 1; - } + for my $stop ( $journey->route ) { + $self->stations->add_or_update( + stop => $stop, + db => $db, + iris => 1, + ); + } - # keep rt_dep / rt_arr if they are no longer present - my %old; - for my $k (qw(rt_arr rt_dep arr_delay dep_delay)) { - $old{$k} = $station->[2]{$k}; - } - $station->[2] = $sd; - if ( not $station->[2]{rt_arr} ) { - $station->[2]{rt_arr} = $old{rt_arr}; - $station->[2]{arr_delay} = $old{arr_delay}; + for my $i ( 0 .. $#{$new_route} ) { + my $old_name = $route->[$i][0]; + my $old_eva = $route->[$i][1]; + my $old_entry = $route->[$i][2]; + my $new_name = $new_route->[$i]->{name}; + my $new_eva = $new_route->[$i]->{eva}; + my $new_entry = $new_route->[$i]; + + if ( defined $old_name and $old_name eq $new_name ) { + if ( $old_entry->{rt_arr} + and not $new_entry->{rt_arr} ) + { + $new_entry->{rt_arr} = $old_entry->{rt_arr}; + $new_entry->{arr_delay} + = $old_entry->{arr_delay}; } - if ( not $station->[2]{rt_dep} ) { - $station->[2]{rt_dep} = $old{rt_dep}; - $station->[2]{dep_delay} = $old{dep_delay}; + if ( $old_entry->{rt_dep} + and not $new_entry->{rt_dep} ) + { + $new_entry->{rt_dep} = $old_entry->{rt_dep}; + $new_entry->{dep_delay} + = $old_entry->{dep_delay}; } } + + push( + @{$db_route}, + [ + $new_name, + $new_eva, + { + sched_arr => $new_entry->{sched_arr}, + rt_arr => $new_entry->{rt_arr}, + arr_delay => $new_entry->{arr_delay}, + sched_dep => $new_entry->{sched_dep}, + rt_dep => $new_entry->{rt_dep}, + dep_delay => $new_entry->{dep_delay}, + tz_offset => $new_entry->{tz_offset}, + isAdditional => $new_entry->{isAdditional}, + isCancelled => $new_entry->{isCancelled}, + load => $new_entry->{load}, + lat => $new_entry->{lat}, + lon => $new_entry->{lon}, + } + ] + ); } my @messages; @@ -1318,7 +2051,7 @@ sub startup { $self->in_transit->set_route_data( uid => $uid, db => $db, - route => $route, + route => $db_route, delay_messages => [ map { [ $_->[0]->epoch, $_->[1] ] } $train->delay_messages @@ -1328,6 +2061,7 @@ sub startup { $train->qos_messages ], him_messages => \@messages, + train_id => $train_id, ); if ($polyline) { @@ -1336,6 +2070,7 @@ sub startup { db => $db, polyline => $polyline, old_id => $in_transit->{polyline_id}, + train_id => $train_id, ); } @@ -1348,107 +2083,28 @@ sub startup { return; } )->wait; + } + ); - if ( $train->sched_departure ) { - $self->dbdb->has_wagonorder_p( $train->sched_departure, - $train->train_no )->then( - sub { - my ($api) = @_; - return $self->dbdb->get_wagonorder_p( $api, - $train->sched_departure, $train->train_no ); - } - )->then( - sub { - my ($wagonorder) = @_; - - my $data = {}; - my $user_data = {}; + $self->helper( + 'add_stationinfo' => sub { + my ( $self, $uid, $is_departure, $train_id, $dep_eva, $arr_eva ) + = @_; - if ( $is_departure and not exists $wagonorder->{error} ) - { - $data->{wagonorder_dep} = $wagonorder; - $user_data->{wagongroups} = []; - for my $group ( - @{ - $wagonorder->{data}{istformation} - {allFahrzeuggruppe} // [] - } - ) - { - my @wagons; - for - my $wagon ( @{ $group->{allFahrzeug} // [] } ) - { - push( - @wagons, - { - id => $wagon->{fahrzeugnummer}, - number => - $wagon->{wagenordnungsnummer}, - type => $wagon->{fahrzeugtyp}, - } - ); - } - push( - @{ $user_data->{wagongroups} }, - { - name => - $group->{fahrzeuggruppebezeichnung}, - from => - $group->{startbetriebsstellename}, - to => $group->{zielbetriebsstellename}, - no => $group->{verkehrlichezugnummer}, - wagons => [@wagons], - } - ); - if ( $group->{fahrzeuggruppebezeichnung} - and $group->{fahrzeuggruppebezeichnung} eq - 'ICE0304' ) - { - $data->{wagonorder_pride} = 1; - } - } - $self->in_transit->update_data( - uid => $uid, - db => $db, - data => $data - ); - $self->in_transit->update_user_data( - uid => $uid, - db => $db, - user_data => $user_data - ); - } - elsif ( not $is_departure - and not exists $wagonorder->{error} ) - { - $data->{wagonorder_arr} = $wagonorder; - $self->in_transit->update_data( - uid => $uid, - db => $db, - data => $data - ); - } - return; - } - )->catch( - sub { - # no wagonorder? no problem. - return; - } - )->wait; - } + $uid //= $self->current_user->{id}; + my $db = $self->pg->db; if ($is_departure) { - $self->dbdb->get_stationinfo_p( $in_transit->{dep_eva} )->then( + $self->dbdb->get_stationinfo_p($dep_eva)->then( sub { my ($station_info) = @_; my $data = { stationinfo_dep => $station_info }; $self->in_transit->update_data( - uid => $uid, - db => $db, - data => $data + uid => $uid, + db => $db, + data => $data, + train_id => $train_id, ); return; } @@ -1460,16 +2116,17 @@ sub startup { )->wait; } - if ( $in_transit->{arr_eva} and not $is_departure ) { - $self->dbdb->get_stationinfo_p( $in_transit->{arr_eva} )->then( + if ( $arr_eva and not $is_departure ) { + $self->dbdb->get_stationinfo_p($arr_eva)->then( sub { my ($station_info) = @_; my $data = { stationinfo_arr => $station_info }; $self->in_transit->update_data( - uid => $uid, - db => $db, - data => $data + uid => $uid, + db => $db, + data => $data, + train_id => $train_id, ); return; } @@ -1493,6 +2150,17 @@ sub startup { $ret =~ s{[{]tt[}]}{$opt{tt}}g; $ret =~ s{[{]tn[}]}{$opt{tn}}g; $ret =~ s{[{]id[}]}{$opt{id}}g; + $ret =~ s{[{]dbris[}]}{$opt{dbris}}g; + $ret =~ s{[{]efa[}]}{$opt{efa}}g; + $ret =~ s{[{]hafas[}]}{$opt{hafas}}g; + $ret =~ s{[{]motis[}]}{$opt{motis}}g; + + if ( $opt{id} and not $opt{is_iris} ) { + $ret =~ s{[{]id_or_tttn[}]}{$opt{id}}g; + } + else { + $ret =~ s{[{]id_or_tttn[}]}{$opt{tt}$opt{tn}}g; + } return $ret; } ); @@ -1523,14 +2191,14 @@ sub startup { my $wr; eval { $wr - = Travel::Status::DE::DBWagenreihung->new( - from_json => $wagonorder ); + = Travel::Status::DE::DBRIS::Formation->new( + json => $wagonorder ); }; if ( $wr - and $wr->sections + and $wr->sectors and defined $wr->direction ) { - my $section_0 = ( $wr->sections )[0]; + my $section_0 = ( $wr->sectors )[0]; my $direction = $wr->direction; if ( $section_0->name eq 'A' and $direction == 0 ) @@ -1593,6 +2261,7 @@ sub startup { uid => $uid, db => $db, with_data => 1, + with_polyline => 1, with_timestamps => 1, with_visibility => 1, postprocess => 1, @@ -1669,11 +2338,11 @@ sub startup { my $wr; eval { $wr - = Travel::Status::DE::DBWagenreihung->new( - from_json => $in_transit->{data}{wagonorder_dep} ); + = Travel::Status::DE::DBRIS::Formation->new( + json => $in_transit->{data}{wagonorder_dep} ); }; if ( $wr - and $wr->wagons + and $wr->carriages and defined $wr->direction ) { $ret->{wagonorder} = $wr; @@ -1691,7 +2360,8 @@ sub startup { if ( $latest_cancellation and $latest_cancellation->{cancelled} ) { if ( my $station = $self->stations->get_by_eva( - $latest_cancellation->{dep_eva} + $latest_cancellation->{dep_eva}, + backend_id => $latest_cancellation->{backend_id}, ) ) { @@ -1700,7 +2370,8 @@ sub startup { } if ( my $station = $self->stations->get_by_eva( - $latest_cancellation->{arr_eva} + $latest_cancellation->{arr_eva}, + backend_id => $latest_cancellation->{backend_id}, ) ) { @@ -1715,14 +2386,20 @@ sub startup { if ($latest) { my $ts = $latest->{checkout_ts}; my $action_time = epoch_to_dt($ts); - if ( my $station - = $self->stations->get_by_eva( $latest->{dep_eva} ) ) + if ( + my $station = $self->stations->get_by_eva( + $latest->{dep_eva}, backend_id => $latest->{backend_id} + ) + ) { $latest->{dep_ds100} = $station->{ds100}; $latest->{dep_name} = $station->{name}; } - if ( my $station - = $self->stations->get_by_eva( $latest->{arr_eva} ) ) + if ( + my $station = $self->stations->get_by_eva( + $latest->{arr_eva}, backend_id => $latest->{backend_id} + ) + ) { $latest->{arr_ds100} = $station->{ds100}; $latest->{arr_name} = $station->{name}; @@ -1731,6 +2408,12 @@ sub startup { checked_in => 0, cancelled => 0, cancellation => $latest_cancellation, + backend_id => $latest->{backend_id}, + backend_name => $latest->{backend_name}, + is_dbris => $latest->{is_dbris}, + is_iris => $latest->{is_iris}, + is_hafas => $latest->{is_hafas}, + is_motis => $latest->{is_motis}, journey_id => $latest->{journey_id}, timestamp => $action_time, timestamp_delta => $now->epoch - $action_time->epoch, @@ -1742,6 +2425,7 @@ sub startup { real_departure => epoch_to_dt( $latest->{real_dep_ts} ), dep_ds100 => $latest->{dep_ds100}, dep_eva => $latest->{dep_eva}, + dep_external_id => $latest->{dep_external_id}, dep_name => $latest->{dep_name}, dep_lat => $latest->{dep_lat}, dep_lon => $latest->{dep_lon}, @@ -1750,6 +2434,7 @@ sub startup { real_arrival => epoch_to_dt( $latest->{real_arr_ts} ), arr_ds100 => $latest->{arr_ds100}, arr_eva => $latest->{arr_eva}, + arr_external_id => $latest->{arr_external_id}, arr_name => $latest->{arr_name}, arr_lat => $latest->{arr_lat}, arr_lon => $latest->{arr_lon}, @@ -1788,13 +2473,22 @@ sub startup { $status->{checked_in} or $status->{cancelled} ) ? \1 : \0, - comment => $status->{comment}, + comment => $status->{comment}, + backend => { + id => $status->{backend_id}, + type => $status->{is_dbris} ? 'DBRIS' + : $status->{is_hafas} ? 'HAFAS' + : $status->{is_motis} ? 'MOTIS' + : 'IRIS-TTS', + name => $status->{backend_name}, + }, fromStation => { ds100 => $status->{dep_ds100}, name => $status->{dep_name}, uic => $status->{dep_eva}, longitude => $status->{dep_lon}, latitude => $status->{dep_lat}, + platform => $status->{dep_platform}, scheduledTime => $status->{sched_departure} ? $status->{sched_departure}->epoch : undef, @@ -1808,6 +2502,7 @@ sub startup { uic => $status->{arr_eva}, longitude => $status->{arr_lon}, latitude => $status->{arr_lat}, + platform => $status->{arr_platform}, scheduledTime => $status->{sched_arrival} ? $status->{sched_arrival}->epoch : undef, @@ -1896,6 +2591,7 @@ sub startup { $self->log->debug( "... checked in : $traewelling->{dep_name} $traewelling->{dep_eva} -> $traewelling->{arr_name} $traewelling->{arr_eva}" ); + $self->users->mark_seen( uid => $uid ); my $user_status = $self->get_user_status($uid); if ( $user_status->{checked_in} ) { $self->log->debug( @@ -1903,224 +2599,74 @@ sub startup { return $promise->resolve; } - if ( $traewelling->{category} - !~ m{^ (?: national .* | regional .* | suburban ) $ }x ) - { - - my $db = $self->pg->db; - my $tx = $db->begin; - - $self->checkin_p( - station => $traewelling->{dep_eva}, - train_id => $traewelling->{trip_id}, - uid => $uid, - in_transaction => 1, - db => $db - )->then( - sub { - $self->log->debug("... handled origin"); - return $self->checkout_p( - station => $traewelling->{arr_eva}, - train_id => $traewelling->{trip_id}, - uid => $uid, - in_transaction => 1, - db => $db - ); - } - )->then( - sub { - my ( undef, $err ) = @_; - if ($err) { - $self->log->debug("... error: $err"); - return Mojo::Promise->reject($err); - } - $self->log->debug("... handled destination"); - if ( $traewelling->{message} ) { - $self->in_transit->update_user_data( - uid => $uid, - db => $db, - user_data => - { comment => $traewelling->{message} } - ); - } - $self->traewelling->log( - uid => $uid, - db => $db, - message => -"Eingecheckt in $traewelling->{line} nach $traewelling->{arr_name}", - status_id => $traewelling->{status_id}, - ); - $self->traewelling->set_latest_pull_status_id( - uid => $uid, - status_id => $traewelling->{status_id}, - db => $db - ); - - $tx->commit; - $promise->resolve; - return; - } - )->catch( - sub { - my ($err) = @_; - $self->log->debug("... error: $err"); - $self->traewelling->log( - uid => $uid, - message => -"Konnte $traewelling->{line} nach $traewelling->{arr_name} nicht übernehmen: $err", - status_id => $traewelling->{status_id}, - is_error => 1 - ); - $promise->resolve; - return; - } - )->wait; - return $promise; - } - - $self->iris->get_departures_p( - station => $traewelling->{dep_eva}, - lookbehind => 60, - lookahead => 40 + my $db = $self->pg->db; + my $tx = $db->begin; + + $self->_checkin_dbris_p( + station => $traewelling->{dep_eva}, + train_id => $traewelling->{trip_id}, + uid => $uid, + in_transaction => 1, + db => $db )->then( sub { - my ($dep) = @_; - my ( $train_ref, $train_id ); - - if ( $dep->{errstr} ) { - $self->traewelling->log( - uid => $uid, - message => -"Konnte $traewelling->{line} nach $traewelling->{arr_name} nicht übernehmen: $dep->{errstr}", - status_id => $traewelling->{status_id}, - is_error => 1, - ); - $promise->resolve; - return; - } - - for my $train ( @{ $dep->{results} } ) { - if ( $train->line ne $traewelling->{line} ) { - next; - } - if ( not $train->sched_departure - or $train->sched_departure->epoch - != $traewelling->{dep_dt}->epoch ) - { - next; - } - if ( - not - List::Util::first { $_ eq $traewelling->{arr_name} } - $train->route_post - ) - { - next; - } - $train_id = $train->train_id; - $train_ref = $train; - last; - } - - if ( not $train_id ) { - $self->log->debug( - "... train $traewelling->{line} not found"); - $self->traewelling->log( - uid => $uid, - message => -"Konnte $traewelling->{line} nach $traewelling->{arr_name} nicht übernehmen: Zug nicht gefunden", - status_id => $traewelling->{status_id}, - is_error => 1 - ); - return $promise->resolve; - } - - $self->log->debug("... found train: $train_id"); - - my $db = $self->pg->db; - my $tx = $db->begin; - - $self->checkin_p( - station => $traewelling->{dep_eva}, - train_id => $train_id, + $self->log->debug("... handled origin"); + return $self->_checkout_journey_p( + station => $traewelling->{arr_eva}, + train_id => $traewelling->{trip_id}, uid => $uid, in_transaction => 1, db => $db - )->then( - sub { - $self->log->debug("... handled origin"); - return $self->checkout_p( - station => $traewelling->{arr_eva}, - train_id => 0, - uid => $uid, - in_transaction => 1, - db => $db - ); - } - )->then( - sub { - my ( undef, $err ) = @_; - if ($err) { - $self->log->debug("... error: $err"); - return Mojo::Promise->reject($err); - } - $self->log->debug("... handled destination"); - if ( $traewelling->{message} ) { - $self->in_transit->update_user_data( - uid => $uid, - db => $db, - user_data => - { comment => $traewelling->{message} } - ); - } - $self->traewelling->log( - uid => $uid, - db => $db, - message => + ); + } + )->then( + sub { + my ( undef, $err ) = @_; + if ($err) { + $self->log->debug("... error: $err"); + return Mojo::Promise->reject($err); + } + $self->log->debug("... handled destination"); + if ( $traewelling->{message} ) { + $self->in_transit->update_user_data( + uid => $uid, + db => $db, + user_data => { comment => $traewelling->{message} } + ); + } + $self->traewelling->log( + uid => $uid, + db => $db, + message => "Eingecheckt in $traewelling->{line} nach $traewelling->{arr_name}", - status_id => $traewelling->{status_id}, - ); - $self->traewelling->set_latest_pull_status_id( - uid => $uid, - status_id => $traewelling->{status_id}, - db => $db - ); + status_id => $traewelling->{status_id}, + ); - $tx->commit; - $promise->resolve; - return; - } - )->catch( - sub { - my ($err) = @_; - $self->log->debug("... error: $err"); - $self->traewelling->log( - uid => $uid, - message => -"Konnte $traewelling->{line} nach $traewelling->{arr_name} nicht übernehmen: $err", - status_id => $traewelling->{status_id}, - is_error => 1 - ); - $promise->resolve; - return; - } - )->wait; + $self->traewelling->set_latest_pull_status_id( + uid => $uid, + status_id => $traewelling->{status_id}, + db => $db + ); + + $tx->commit; + $promise->resolve; + return; } )->catch( sub { - my ( $err, $dep ) = @_; + my ($err) = @_; + $self->log->debug("... error: $err"); $self->traewelling->log( uid => $uid, message => -"Konnte $traewelling->{line} nach $traewelling->{arr_name} nicht übernehmen: $dep->{errstr}", +"Konnte $traewelling->{line} nach $traewelling->{arr_name} nicht übernehmen: $err", status_id => $traewelling->{status_id}, - is_error => 1, + is_error => 1 ); $promise->resolve; return; } )->wait; - return $promise; } ); @@ -2133,8 +2679,6 @@ sub startup { my $route_type = $opt{route_type} // 'polybee'; my $include_manual = $opt{include_manual} ? 1 : 0; - my $location = $self->app->coordinates_by_station; - my $with_polyline = $route_type eq 'beeline' ? 0 : 1; if ( not @journeys ) { @@ -2150,12 +2694,19 @@ sub startup { my $first_departure = $journeys[-1]->{rt_departure}; my $last_departure = $journeys[0]->{rt_departure}; - my @stations = List::Util::uniq map { $_->{to_name} } @journeys; - push( @stations, - List::Util::uniq map { $_->{from_name} } @journeys ); - @stations = List::Util::uniq @stations; - my @station_coordinates = map { [ $location->{$_}, $_ ] } - grep { exists $location->{$_} } @stations; + my @stations = uniq_by { $_->{name} } map { + { + name => $_->{to_name} // $_->{arr_name}, + latlon => $_->{to_latlon} // $_->{arr_latlon}, + }, + { + name => $_->{from_name} // $_->{dep_name}, + latlon => $_->{from_latlon} // $_->{dep_latlon} + } + } @journeys; + + my @station_coordinates + = map { [ $_->{latlon}, $_->{name} ] } @stations; my @station_pairs; my @polylines; @@ -2175,19 +2726,44 @@ sub startup { for my $journey (@polyline_journeys) { my @polyline = @{ $journey->{polyline} }; - my $from_eva = $journey->{from_eva}; - my $to_eva = $journey->{to_eva}; + my $from_eva = $journey->{from_eva} // $journey->{dep_eva}; + my $to_eva = $journey->{to_eva} // $journey->{arr_eva}; my $from_index = first_index { $_->[2] and $_->[2] == $from_eva } @polyline; my $to_index = first_index { $_->[2] and $_->[2] == $to_eva } @polyline; + # Work around inconsistencies caused by a multiple EVA IDs mapping to the same station name + if ( $from_index == -1 ) { + for my $entry ( @{ $journey->{route} // [] } ) { + if ( $entry->[0] eq $journey->{from_name} ) { + $from_eva = $entry->[1]; + $from_index + = first_index { $_->[2] and $_->[2] == $from_eva } + @polyline; + last; + } + } + } + + if ( $to_index == -1 ) { + for my $entry ( @{ $journey->{route} // [] } ) { + if ( $entry->[0] eq $journey->{to_name} ) { + $to_eva = $entry->[1]; + $to_index + = first_index { $_->[2] and $_->[2] == $to_eva } + @polyline; + last; + } + } + } + if ( $from_index == -1 or $to_index == -1 ) { # Fall back to route - delete $journey->{polyline}; + push( @beeline_journeys, $journey ); next; } @@ -2199,7 +2775,6 @@ sub startup { if ( $seen{$key} ) { next; } - $seen{$key} = 1; # direction does not matter at the moment @@ -2209,6 +2784,9 @@ sub startup { . ( $to_index - $from_index ); $seen{$key} = 1; + if ( $from_index > $to_index ) { + ( $to_index, $from_index ) = ( $from_index, $to_index ); + } @polyline = @polyline[ $from_index .. $to_index ]; my @polyline_coords; for my $coord (@polyline) { @@ -2219,23 +2797,38 @@ sub startup { for my $journey (@beeline_journeys) { - my @route = map { $_->[0] } @{ $journey->{route} }; + my @route = @{ $journey->{route} }; - my $from_index - = first_index { $_ eq $journey->{from_name} } @route; - my $to_index = first_index { $_ eq $journey->{to_name} } @route; + my $from_index = first_index { + ( $_->[1] + and $_->[1] + == ( $journey->{from_eva} // $journey->{dep_eva} ) ) + or $_->[0] eq + ( $journey->{from_name} // $journey->{dep_name} ) + } + @route; + my $to_index = first_index { + ( $_->[1] + and $_->[1] + == ( $journey->{to_eva} // $journey->{arr_eva} ) ) + or $_->[0] eq + ( $journey->{to_name} // $journey->{arr_name} ) + } + @route; if ( $from_index == -1 ) { my $rename = $self->app->renamed_station; $from_index = first_index { - ( $rename->{$_} // $_ ) eq $journey->{from_name} + ( $rename->{ $_->[0] } // $_->[0] ) eq + ( $journey->{from_name} // $journey->{dep_name} ) } @route; } if ( $to_index == -1 ) { my $rename = $self->app->renamed_station; $to_index = first_index { - ( $rename->{$_} // $_ ) eq $journey->{to_name} + ( $rename->{ $_->[0] } // $_->[0] ) eq + ( $journey->{to_name} // $journey->{arr_name} ) } @route; } @@ -2258,7 +2851,8 @@ sub startup { # and entered manually (-> beeline also shown on map, typically # significantly differs from detailed route) -- unless the user # sets include_manual, of course. - if ( $journey->{edited} & 0x0010 + if ( $journey->{edited} + and $journey->{edited} & 0x0010 and @route <= 2 and not $include_manual ) { @@ -2269,7 +2863,7 @@ sub startup { @route = @route[ $from_index .. $to_index ]; - my $key = join( '|', @route ); + my $key = join( '|', map { $_->[0] } @route ); if ( $seen{$key} ) { next; @@ -2278,7 +2872,7 @@ sub startup { $seen{$key} = 1; # direction does not matter at the moment - $seen{ join( '|', reverse @route ) } = 1; + $seen{ join( '|', reverse map { $_->[0] } @route ) } = 1; my $prev_station = shift @route; for my $station (@route) { @@ -2287,14 +2881,17 @@ sub startup { } } - @station_pairs = uniq_by { $_->[0] . '|' . $_->[1] } @station_pairs; - @station_pairs = grep { - exists $location->{ $_->[0] } - and exists $location->{ $_->[1] } - } @station_pairs; @station_pairs - = map { [ $location->{ $_->[0] }, $location->{ $_->[1] } ] } + = uniq_by { $_->[0][0] . '|' . $_->[1][0] } @station_pairs; + @station_pairs + = grep { defined $_->[0][2]{lat} and defined $_->[1][2]{lat} } @station_pairs; + @station_pairs = map { + [ + [ $_->[0][2]{lat}, $_->[0][2]{lon} ], + [ $_->[1][2]{lat}, $_->[1][2]{lon} ] + ] + } @station_pairs; my $ret = { skipped_journeys => \@skipped_journeys, @@ -2351,6 +2948,7 @@ sub startup { $r->get('/changelog')->to('static#changelog'); $r->get('/impressum')->to('static#imprint'); $r->get('/imprint')->to('static#imprint'); + $r->get('/tos')->to('static#tos'); $r->get('/legend')->to('static#legend'); $r->get('/offline.html')->to('static#offline'); $r->get('/api/v1/:user_action/:token')->to('api#get_v1'); @@ -2364,7 +2962,8 @@ sub startup { ->to( 'profile#user_status', format => undef ); $r->get('/ajax/status/#name')->to('profile#status_card'); $r->get('/ajax/status/:name/:ts')->to('profile#status_card'); - $r->get('/p/:name')->to('profile#profile'); + $r->get( '/p/:name' => [ format => [ 'html', 'json' ] ] ) + ->to( 'profile#profile', format => undef ); $r->get( '/p/:name/j/:id' => 'public_journey' ) ->to('profile#journey_details'); $r->get('/.well-known/webfinger')->to('account#webfinger'); @@ -2410,13 +3009,15 @@ sub startup { $authed_r->get('/account/hooks')->to('account#webhook'); $authed_r->get('/account/traewelling')->to('traewelling#settings'); $authed_r->get('/account/insight')->to('account#insight'); - $authed_r->get('/account/services')->to('account#services'); $authed_r->get('/ajax/status_card.html')->to('traveling#status_card'); - $authed_r->get('/cancelled')->to('traveling#cancelled'); + $authed_r->get( '/cancelled' => [ format => [ 'html', 'json' ] ] ) + ->to( 'traveling#cancelled', format => undef ); + $authed_r->get('/checkin/add')->to('traveling#add_intransit_form'); $authed_r->get('/fgr')->to('passengerrights#list_candidates'); $authed_r->get('/account/password')->to('account#password_form'); $authed_r->get('/account/mail')->to('account#change_mail'); $authed_r->get('/account/name')->to('account#change_name'); + $authed_r->get('/account/select_backend')->to('account#backend_form'); $authed_r->get('/export.json')->to('account#json_export'); $authed_r->get('/history.json')->to('traveling#json_history'); $authed_r->get('/history.csv')->to('traveling#csv_history'); @@ -2438,7 +3039,8 @@ sub startup { $authed_r->post('/account/hooks')->to('account#webhook'); $authed_r->post('/account/traewelling')->to('traewelling#settings'); $authed_r->post('/account/insight')->to('account#insight'); - $authed_r->post('/account/services')->to('account#services'); + $authed_r->post('/account/select_backend')->to('account#change_backend'); + $authed_r->post('/checkin/add')->to('traveling#add_intransit_form'); $authed_r->post('/journey/add')->to('traveling#add_journey_form'); $authed_r->post('/journey/comment')->to('traveling#comment_form'); $authed_r->post('/journey/visibility')->to('traveling#visibility_form'); |