summaryrefslogtreecommitdiff
path: root/lib/Travelynx.pm
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Travelynx.pm')
-rwxr-xr-xlib/Travelynx.pm309
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');