diff options
Diffstat (limited to 'lib/Travelynx.pm')
| -rwxr-xr-x | lib/Travelynx.pm | 309 |
1 files changed, 220 insertions, 89 deletions
diff --git a/lib/Travelynx.pm b/lib/Travelynx.pm index b36c633..a0661bf 100755 --- a/lib/Travelynx.pm +++ b/lib/Travelynx.pm @@ -21,11 +21,11 @@ use List::Util; use List::UtilsBy qw(uniq_by); use List::MoreUtils qw(first_index); 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::Locales; use Travelynx::Helper::MOTIS; use Travelynx::Helper::Sendmail; use Travelynx::Helper::Traewelling; @@ -68,6 +68,8 @@ sub startup { $self->types->type( csv => 'text/csv; charset=utf-8' ); $self->types->type( json => 'application/json; charset=utf-8' ); + $self->types->type( gpx => 'application/gpx+xml; charset=utf-8' ); + $self->types->type( xml => 'text/xml; charset=utf-8' ); $self->plugin('Config'); @@ -157,6 +159,14 @@ sub startup { } ); + $self->hook( + 'before_render' => sub { + my ($self) = @_; + + $self->stash( loc_handle => $self->loc_handle ); + } + ); + $self->attr( cache_iris_main => sub { my ($self) = @_; @@ -242,7 +252,8 @@ sub startup { state $dbris = Travelynx::Helper::DBRIS->new( log => $self->app->log, service_config => $self->app->config->{dbris}, - cache => $self->app->cache_iris_rt, + realtime_cache => $self->app->cache_iris_rt, + main_cache => $self->app->cache_iris_main, root_url => $self->base_url_for('/')->to_abs, user_agent => $self->ua, version => $self->app->config->{version}, @@ -287,6 +298,7 @@ sub startup { user_agent => $self->ua, root_url => $self->base_url_for('/')->to_abs, version => $self->app->config->{version}, + time_zone => 'Europe/Berlin', ); } ); @@ -397,16 +409,59 @@ sub startup { ); $self->helper( - dbdb => sub { + loc_handle => sub { my ($self) = @_; - state $dbdb = Travelynx::Helper::DBDB->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}, - ); + + my @languages; + if ( $self->is_user_authenticated + and @{ $self->current_user->{languages} } ) + { + @languages = @{ $self->current_user->{languages} }; + } + elsif ( my $languages = $self->req->headers->accept_language ) { + for my $lang ( split( qr{ \s* , \s* }x, $languages ) ) { + if ( $lang =~ m{ ^ de }x ) { + push( @languages, 'de-DE' ); + } + elsif ( $lang =~ m{ ^ en }x ) { + push( @languages, 'en-GB' ); + } + elsif ( $lang =~ m{ ^ fr }x ) { + push( @languages, 'fr-FR' ); + } + elsif ( $lang =~ m{ ^ hu }x ) { + push( @languages, 'hu-HU' ); + } + elsif ( $lang =~ m{ ^ pl }x ) { + push( @languages, 'pl-PL' ); + } + } + } + + # en-GB and de-DE serve as fall-back languages, both in case + # we do not have the handle we need (here) and in case a string + # has only been translated to some languages (below). + + my $handle + = Travelynx::Helper::Locales->get_handle( @languages, 'en-GB', + 'de-DE' ); + my $first_fallback + = Travelynx::Helper::Locales->get_handle('en-GB'); + my $second_fallback + = Travelynx::Helper::Locales->get_handle('de-DE'); + + $handle->fail_with( + sub { $first_fallback->maketext( @_[ 1 .. $#_ ] ) } ); + $first_fallback->fail_with( + sub { $second_fallback->maketext( @_[ 1 .. $#_ ] ) } ); + return $handle; + } + ); + + $self->helper( + 'L' => sub { + my ( $self, @args ) = @_; + $self->stash('loc_handle')->maketext(@args); } ); @@ -1160,7 +1215,7 @@ 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 ) { + if ( $opt{hafas} eq 'ÖBB' and $journey->class <= 16 ) { $self->add_wagonorder( uid => $uid, train_id => $journey->id, @@ -1315,7 +1370,8 @@ sub startup { if ( $user->{is_dbris} or $user->{is_efa} or $user->{is_hafas} - or $user->{is_motis} ) + or $user->{is_motis} + or $train_id eq 'manual' ) { return $self->_checkout_journey_p(%opt); } @@ -1446,7 +1502,9 @@ sub startup { if ($has_arrived) { my @unknown_stations = $self->stations->grep_unknown( - $train->route ); + backend_id => $user->{backend_id}, + names => [ $train->route ] + ); if (@unknown_stations) { $self->app->log->warn( sprintf( @@ -1613,6 +1671,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} ) ) { @@ -1806,38 +1871,25 @@ sub startup { 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( + $self->dbris->get_wagonorder_p(%opt)->then( sub { - my ($wagonorder) = @_; + my ($status) = @_; + my $wr = $status->result; 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} ) + and $wr ) { my $dt = $opt{datetime}->clone->set_time_zone('UTC'); - $data->{wagonorder_dep} = $wagonorder; + $data->{wagonorder_dep} = $status->{raw_json}; $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}, + tt => $opt{train_type}, + tn => $opt{train_no}, + dt => $dt->epoch, + eva => $opt{eva} }; $user_data->{wagongroups} = []; for my $group ( $wr->groups ) { @@ -1884,10 +1936,8 @@ sub startup { train_id => $train_id, ); } - elsif ( $opt{is_arrival} - and not exists $wagonorder->{error} ) - { - $data->{wagonorder_arr} = $wagonorder; + elsif ( $opt{is_arrival} ) { + $data->{wagonorder_arr} = $status->{raw_json}; $self->in_transit->update_data( uid => $uid, db => $db, @@ -1899,6 +1949,10 @@ sub startup { } )->catch( sub { + my ($err) = @_; + $self->log->debug( + "add_wagonorder: promise rejected with ${err}"); + # no wagonorder? no problem. return; } @@ -2086,47 +2140,36 @@ sub startup { my $db = $self->pg->db; if ($is_departure) { - $self->dbdb->get_stationinfo_p($dep_eva)->then( - sub { - my ($station_info) = @_; - my $data = { stationinfo_dep => $station_info }; + if ( my $si + = $self->stations->get_bahn_stationinfo( eva => $dep_eva ) ) + { + my $data = { stationinfo_dep => $si }; - $self->in_transit->update_data( - uid => $uid, - db => $db, - data => $data, - train_id => $train_id, - ); - return; - } - )->catch( - sub { - # no stationinfo? no problem. - return; - } - )->wait; + $self->in_transit->update_data( + uid => $uid, + db => $db, + data => $data, + train_id => $train_id, + ); + return; + } } 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 }; + if ( my $si + = $self->stations->get_bahn_stationinfo( eva => $arr_eva ) ) + { - $self->in_transit->update_data( - uid => $uid, - db => $db, - data => $data, - train_id => $train_id, - ); - return; - } - )->catch( - sub { - # no stationinfo? no problem. - return; - } - )->wait; + my $data = { stationinfo_arr => $si }; + + $self->in_transit->update_data( + uid => $uid, + db => $db, + data => $data, + train_id => $train_id, + ); + return; + } } } ); @@ -2402,6 +2445,7 @@ sub startup { backend_id => $latest->{backend_id}, backend_name => $latest->{backend_name}, is_dbris => $latest->{is_dbris}, + is_efa => $latest->{is_efa}, is_iris => $latest->{is_iris}, is_hafas => $latest->{is_hafas}, is_motis => $latest->{is_motis}, @@ -2468,6 +2512,7 @@ sub startup { backend => { id => $status->{backend_id}, type => $status->{is_dbris} ? 'DBRIS' + : $status->{is_efa} ? 'EFA' : $status->{is_hafas} ? 'HAFAS' : $status->{is_motis} ? 'MOTIS' : 'IRIS-TTS', @@ -2695,14 +2740,46 @@ sub startup { latlon => $_->{from_latlon} // $_->{dep_latlon} } } @journeys; + my @extra_stations; + + if ( $opt{show_all_stops} ) { + for my $journey (@journeys) { + my @j_stops = map { + { + name => $_->[2], + latlon => [ $_->[1], $_->[0] ] + } + } grep { defined $_->[2] } + @{ $journey->{polyline} // [] }; + @extra_stations + = uniq_by { $_->{name} } ( @extra_stations, @j_stops ); + } + } my @station_coordinates = map { [ $_->{latlon}, $_->{name} ] } @stations; + my @extra_station_coordinates + = map { [ $_->{latlon}, $_->{name} ] } @extra_stations; + + my @now_coordinates; + if ( $opt{with_now_markers} ) { + @now_coordinates = map { + [ + $_->{now_latlon}, + $_->{train_type} . ' ' + . ( $_->{train_line} // $_->{train_no} ) + ] + } @journeys; + } my @station_pairs; my @polylines; my %seen; + # not part of the travelled route, but trip route before/after the journey. + # Only used if show_full_route is set. + my @extra_polylines; + my @skipped_journeys; my @polyline_journeys = grep { $_->{polyline} } @journeys; my @beeline_journeys = grep { not $_->{polyline} } @journeys; @@ -2720,10 +2797,14 @@ sub startup { 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; + # poly_dep_index, poly_arr_index are only available for + # journeys that were processed by get_travel_distance + # beforehand. However, they are much less error-prone than this + # first_index / last_index kludge when it comes to ring lines. + my $from_index = $journey->{poly_dep_index} + // first_index { $_->[2] and $_->[2] == $from_eva } @polyline; + my $to_index = $journey->{poly_arr_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 ) { @@ -2778,12 +2859,28 @@ sub startup { if ( $from_index > $to_index ) { ( $to_index, $from_index ) = ( $from_index, $to_index ); } + if ( $opt{show_full_route} ) { + my @pre_polyline = @polyline[ 0 .. $from_index ]; + my @post_polyline = @polyline[ $to_index .. $#polyline ]; + my @pre_polyline_coords; + for my $coord (@pre_polyline) { + push( @pre_polyline_coords, + [ $coord->[1], $coord->[0] ] ); + } + my @post_polyline_coords; + for my $coord (@post_polyline) { + push( @post_polyline_coords, + [ $coord->[1], $coord->[0] ] ); + } + push( @extra_polylines, + ( \@pre_polyline_coords, \@post_polyline_coords ) ); + } @polyline = @polyline[ $from_index .. $to_index ]; my @polyline_coords; for my $coord (@polyline) { push( @polyline_coords, [ $coord->[1], $coord->[0] ] ); } - push( @polylines, [@polyline_coords] ); + push( @polylines, \@polyline_coords ); } for my $journey (@beeline_journeys) { @@ -2891,7 +2988,7 @@ sub startup { { polylines => $json->encode( \@station_pairs ), color => '#673ab7', - opacity => @polylines + opacity => scalar @polylines ? $with_polyline ? 0.4 : 0.6 @@ -2901,8 +2998,14 @@ sub startup { polylines => $json->encode( \@polylines ), color => '#673ab7', opacity => 0.8, - } + }, + { + polylines => $json->encode( \@extra_polylines ), + color => '#665577', + opacity => 0.6, + }, ], + markers => \@now_coordinates, }; if (@station_coordinates) { @@ -2916,6 +3019,13 @@ sub startup { = [ [ $min_lat, $min_lon ], [ $max_lat, $max_lon ] ]; } + if (@extra_station_coordinates) { + $ret->{station_coordinates} = [ + uniq_by { $_->[1] } + ( @station_coordinates, @extra_station_coordinates ) + ]; + } + return $ret; } ); @@ -2983,10 +3093,17 @@ sub startup { if ( $self->is_user_authenticated ) { return 1; } - $self->render( - 'login', - redirect_to => $self->req->url, - from => 'auth_required' + $self->respond_to( + json => { + json => { error => 'authentication required' }, + status => 401 + }, + any => { + template => 'login', + status => 401, + redirect_to => $self->req->url, + from => 'auth_required' + } ); return undef; } @@ -3000,9 +3117,11 @@ 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/language')->to('account#change_language'); $authed_r->get('/ajax/status_card.html')->to('traveling#status_card'); $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'); @@ -3017,10 +3136,19 @@ sub startup { $authed_r->get('/history/:year')->to('traveling#yearly_history'); $authed_r->get('/history/:year/review')->to('traveling#year_in_review'); $authed_r->get('/history/:year/:month')->to('traveling#monthly_history'); - $authed_r->get('/journey/add')->to('traveling#add_journey_form'); + $authed_r->get('/journey/add') + ->to('traveling#add_journey_form') + ->name('add_journey'); $authed_r->get('/journey/comment')->to('traveling#comment_form'); $authed_r->get('/journey/visibility')->to('traveling#visibility_form'); - $authed_r->get('/journey/:id')->to('traveling#journey_details'); + $authed_r->get( '/journey/:id' => [ format => [ 'html', 'json' ] ] ) + ->to( 'traveling#journey_details', format => undef ) + ->name('journey'); + $authed_r->get( '/polyline/:id' => [ format => [ 'gpx', 'json' ] ] )->to( + 'traveling#journey_details', + format => undef, + polyline_export => 1 + )->name('polyline_download'); $authed_r->get('/s/*station')->to('traveling#station'); $authed_r->get('/confirm_mail/:token')->to('account#confirm_mail'); $authed_r->post('/account/privacy')->to('account#privacy'); @@ -3029,8 +3157,11 @@ 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/language')->to('account#change_language'); $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('/polyline/set')->to('traveling#set_polyline'); $authed_r->post('/journey/comment')->to('traveling#comment_form'); $authed_r->post('/journey/visibility')->to('traveling#visibility_form'); $authed_r->post('/journey/edit')->to('traveling#edit_journey'); |
