summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rwxr-xr-xlib/Travelynx.pm170
-rw-r--r--lib/Travelynx/Command/database.pm69
-rw-r--r--lib/Travelynx/Command/work.pm79
-rw-r--r--lib/Travelynx/Controller/Account.pm43
-rwxr-xr-xlib/Travelynx/Controller/Traveling.pm91
-rw-r--r--lib/Travelynx/Helper/DBRIS.pm1
-rw-r--r--lib/Travelynx/Helper/EFA.pm102
-rw-r--r--lib/Travelynx/Helper/HAFAS.pm14
-rw-r--r--lib/Travelynx/Model/InTransit.pm171
-rwxr-xr-xlib/Travelynx/Model/Journeys.pm5
-rw-r--r--lib/Travelynx/Model/Stations.pm63
-rw-r--r--lib/Travelynx/Model/Users.pm3
12 files changed, 775 insertions, 36 deletions
diff --git a/lib/Travelynx.pm b/lib/Travelynx.pm
index f554d08..3d892ec 100755
--- a/lib/Travelynx.pm
+++ b/lib/Travelynx.pm
@@ -23,6 +23,7 @@ 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::MOTIS;
@@ -160,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;
}
);
@@ -172,11 +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(),
);
+ return $cache;
}
);
@@ -194,7 +197,7 @@ sub startup {
$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;
}
@@ -220,6 +223,20 @@ 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(
@@ -490,15 +507,18 @@ sub startup {
return Mojo::Promise->reject('You are already checked in');
}
- if ( $opt{motis} ) {
- return $self->_checkin_motis_p(%opt);
- }
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;
@@ -869,6 +889,137 @@ sub startup {
);
$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 ) = @_;
@@ -877,7 +1028,6 @@ sub startup {
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;
@@ -1136,7 +1286,11 @@ sub startup {
return $promise->resolve( 0, 'race condition' );
}
- if ( $user->{is_dbris} or $user->{is_hafas} or $user->{is_motis} ) {
+ if ( $user->{is_dbris}
+ or $user->{is_efa}
+ or $user->{is_hafas}
+ or $user->{is_motis} )
+ {
return $self->_checkout_journey_p(%opt);
}
diff --git a/lib/Travelynx/Command/database.pm b/lib/Travelynx/Command/database.pm
index e264c89..1385389 100644
--- a/lib/Travelynx/Command/database.pm
+++ b/lib/Travelynx/Command/database.pm
@@ -10,6 +10,7 @@ use DateTime;
use File::Slurp qw(read_file);
use List::Util qw();
use JSON;
+use Travel::Status::DE::EFA;
use Travel::Status::DE::HAFAS;
use Travel::Status::DE::IRIS::Stations;
use Travel::Status::MOTIS;
@@ -3023,6 +3024,19 @@ qq{select distinct checkout_station_id from in_transit where backend_id = 0;}
}
);
},
+
+ # v62 -> v63
+ # Add EFA backend support
+ sub {
+ my ($db) = @_;
+ $db->query(
+ qq{
+ alter table schema_version add column efa varchar(12);
+ update schema_version set version = 63;
+ update schema_version set efa = '0';
+ }
+ );
+ },
);
sub sync_stations {
@@ -3213,6 +3227,37 @@ sub sync_stations {
}
}
+sub sync_backends_efa {
+ my ($db) = @_;
+ for my $service ( Travel::Status::DE::EFA::get_services() ) {
+ my $present = $db->select(
+ 'backends',
+ 'count(*) as count',
+ {
+ efa => 1,
+ name => $service->{shortname}
+ }
+ )->hash->{count};
+ if ( not $present ) {
+ $db->insert(
+ 'backends',
+ {
+ dbris => 0,
+ efa => 1,
+ hafas => 0,
+ iris => 0,
+ motis => 0,
+ name => $service->{shortname},
+ },
+ { on_conflict => undef }
+ );
+ }
+ }
+
+ $db->update( 'schema_version',
+ { efa => $Travel::Status::DE::EFA::VERSION } );
+}
+
sub sync_backends_hafas {
my ($db) = @_;
for my $service ( Travel::Status::DE::HAFAS::get_services() ) {
@@ -3228,10 +3273,11 @@ sub sync_backends_hafas {
$db->insert(
'backends',
{
- iris => 0,
- hafas => 1,
- efa => 0,
dbris => 0,
+ efa => 0,
+ hafas => 1,
+ iris => 0,
+ motis => 0,
name => $service->{shortname},
},
{ on_conflict => undef }
@@ -3258,10 +3304,10 @@ sub sync_backends_motis {
$db->insert(
'backends',
{
- iris => 0,
- hafas => 0,
- efa => 0,
dbris => 0,
+ efa => 0,
+ hafas => 0,
+ iris => 0,
motis => 1,
name => $service->{shortname},
},
@@ -3361,6 +3407,17 @@ sub migrate_db {
}
}
+ my $efa_version = get_schema_version( $db, 'efa' );
+ say "Found backend table for EFA v${efa_version}";
+ if ( $efa_version eq $Travel::Status::DE::EFA::VERSION ) {
+ say 'Backend table is up-to-date';
+ }
+ else {
+ say
+"Synchronizing with Travel::Status::DE::EFA $Travel::Status::DE::EFA::VERSION";
+ sync_backends_efa($db);
+ }
+
my $hafas_version = get_schema_version( $db, 'hafas' );
say "Found backend table for HAFAS v${hafas_version}";
if ( $hafas_version eq $Travel::Status::DE::HAFAS::VERSION ) {
diff --git a/lib/Travelynx/Command/work.pm b/lib/Travelynx/Command/work.pm
index 2b01cb2..5ea1810 100644
--- a/lib/Travelynx/Command/work.pm
+++ b/lib/Travelynx/Command/work.pm
@@ -185,6 +185,85 @@ sub run {
next;
}
+ if ( $entry->{is_efa} ) {
+ eval {
+ $self->app->efa->get_journey_p(
+ trip_id => $train_id,
+ service => $entry->{backend_name}
+ )->then(
+ sub {
+ my ($journey) = @_;
+
+ my $found_dep;
+ my $found_arr;
+ for my $stop ( $journey->route ) {
+ if ( $stop->id_num == $dep ) {
+ $found_dep = $stop;
+ }
+ if ( $arr and $stop->id_num == $arr ) {
+ $found_arr = $stop;
+ last;
+ }
+ }
+ if ( not $found_dep ) {
+ $self->app->log->debug(
+ "Did not find $dep within journey $train_id");
+ return;
+ }
+
+ if ( $found_dep->rt_dep ) {
+ $self->app->in_transit->update_departure_efa(
+ uid => $uid,
+ journey => $journey,
+ stop => $found_dep,
+ dep_eva => $dep,
+ arr_eva => $arr,
+ trip_id => $train_id,
+ );
+ }
+
+ if ( $found_arr and $found_arr->rt_arr ) {
+ $self->app->in_transit->update_arrival_efa(
+ uid => $uid,
+ journey => $journey,
+ stop => $found_arr,
+ dep_eva => $dep,
+ arr_eva => $arr,
+ trip_id => $train_id,
+ );
+ }
+ }
+ )->catch(
+ sub {
+ my ($err) = @_;
+ $backend_issues += 1;
+ $self->app->log->error(
+"work($uid) @ EFA $entry->{backend_name}: journey: $err"
+ );
+ }
+ )->wait;
+
+ if ( $arr
+ and $entry->{real_arr_ts}
+ and $now->epoch - $entry->{real_arr_ts} > 600 )
+ {
+ $self->app->checkout_p(
+ station => $arr,
+ force => 2,
+ dep_eva => $dep,
+ arr_eva => $arr,
+ uid => $uid
+ )->wait;
+ }
+ };
+ if ($@) {
+ $errors += 1;
+ $self->app->log->error(
+ "work($uid) @ EFA $entry->{backend_name}: $@");
+ }
+ next;
+ }
+
if ( $entry->{is_motis} ) {
eval {
diff --git a/lib/Travelynx/Controller/Account.pm b/lib/Travelynx/Controller/Account.pm
index 033b270..0978c88 100644
--- a/lib/Travelynx/Controller/Account.pm
+++ b/lib/Travelynx/Controller/Account.pm
@@ -1077,6 +1077,49 @@ sub backend_form {
$backend->{homepage} = 'https://www.bahn.de';
$backend->{recommended} = 1;
}
+ elsif ( $backend->{efa} ) {
+ if ( my $s = $self->efa->get_service( $backend->{name} ) ) {
+ $type = 'EFA';
+ $backend->{longname} = $s->{name};
+ $backend->{homepage} = $s->{homepage};
+ $backend->{regions} = [ map { $place_map{$_} // $_ }
+ @{ $s->{coverage}{regions} // [] } ];
+ $backend->{has_area} = $s->{coverage}{area} ? 1 : 0;
+ $backend->{experimental} = 1;
+
+ if (
+ $s->{coverage}{area}
+ and $s->{coverage}{area}{type} eq 'Polygon'
+ and $self->lonlat_in_polygon(
+ $s->{coverage}{area}{coordinates},
+ [ $user_lon, $user_lat ]
+ )
+ )
+ {
+ push( @suggested_backends, $backend );
+ }
+ elsif ( $s->{coverage}{area}
+ and $s->{coverage}{area}{type} eq 'MultiPolygon' )
+ {
+ for my $s_poly (
+ @{ $s->{coverage}{area}{coordinates} // [] } )
+ {
+ if (
+ $self->lonlat_in_polygon(
+ $s_poly, [ $user_lon, $user_lat ]
+ )
+ )
+ {
+ push( @suggested_backends, $backend );
+ last;
+ }
+ }
+ }
+ }
+ else {
+ $type = undef;
+ }
+ }
elsif ( $backend->{hafas} ) {
# These backends lack a journey endpoint or are no longer
diff --git a/lib/Travelynx/Controller/Traveling.pm b/lib/Travelynx/Controller/Traveling.pm
index 0cfccb1..9826211 100755
--- a/lib/Travelynx/Controller/Traveling.pm
+++ b/lib/Travelynx/Controller/Traveling.pm
@@ -562,11 +562,14 @@ sub geolocation {
return;
}
- my ( $dbris_service, $hafas_service, $motis_service );
+ my ( $dbris_service, $efa_service, $hafas_service, $motis_service );
my $backend = $self->stations->get_backend( backend_id => $backend_id );
if ( $backend->{dbris} ) {
$dbris_service = $backend->{name};
}
+ if ( $backend->{efa} ) {
+ $efa_service = $backend->{name};
+ }
elsif ( $backend->{hafas} ) {
$hafas_service = $backend->{name};
}
@@ -617,6 +620,50 @@ sub geolocation {
)->wait;
return;
}
+ elsif ($efa_service) {
+ $self->render_later;
+
+ Travel::Status::DE::EFA->new_p(
+ promise => 'Mojo::Promise',
+ user_agent => Mojo::UserAgent->new,
+ service => $efa_service,
+ coord => {
+ lat => $lat,
+ lon => $lon
+ }
+ )->then(
+ sub {
+ my ($efa) = @_;
+ my @results = map {
+ {
+ name => $_->full_name,
+ eva => $_->id_code,
+ distance => 0,
+ efa => $efa_service,
+ }
+ } $efa->results;
+ if ( @results > 10 ) {
+ @results = @results[ 0 .. 9 ];
+ }
+ $self->render(
+ json => {
+ candidates => [@results],
+ }
+ );
+ }
+ )->catch(
+ sub {
+ my ($err) = @_;
+ $self->render(
+ json => {
+ candidates => [],
+ warning => $err,
+ }
+ );
+ }
+ )->wait;
+ return;
+ }
elsif ($hafas_service) {
$self->render_later;
@@ -726,8 +773,6 @@ sub geolocation {
lon => $_->[0][3],
lat => $_->[0][4],
distance => $_->[1],
- dbris => 0,
- hafas => 0,
}
} Travel::Status::DE::IRIS::Stations::get_station_by_location( $lon,
$lat, 10 );
@@ -796,6 +841,7 @@ sub travel_action {
sub {
return $self->checkin_p(
dbris => $params->{dbris},
+ efa => $params->{efa},
hafas => $params->{hafas},
motis => $params->{motis},
station => $params->{station},
@@ -832,6 +878,9 @@ sub travel_action {
if ( $status->{is_dbris} ) {
$station_link .= '?dbris=' . $status->{backend_name};
}
+ elsif ( $status->{is_efa} ) {
+ $station_link .= '?efa=' . $status->{backend_name};
+ }
elsif ( $status->{is_hafas} ) {
$station_link .= '?hafas=' . $status->{backend_name};
}
@@ -871,6 +920,9 @@ sub travel_action {
if ( $status->{is_dbris} ) {
$station_link .= '?dbris=' . $status->{backend_name};
}
+ elsif ( $status->{is_efa} ) {
+ $station_link .= '?efa=' . $status->{backend_name};
+ }
elsif ( $status->{is_hafas} ) {
$station_link .= '?hafas=' . $status->{backend_name};
}
@@ -929,6 +981,12 @@ sub travel_action {
. '?dbris='
. $status->{backend_name};
}
+ elsif ( $status->{is_efa} ) {
+ $redir
+ = '/s/'
+ . $status->{dep_eva} . '?efa='
+ . $status->{backend_name};
+ }
elsif ( $status->{is_hafas} ) {
$redir
= '/s/'
@@ -959,6 +1017,7 @@ sub travel_action {
$self->render_later;
$self->checkin_p(
dbris => $params->{dbris},
+ efa => $params->{efa},
hafas => $params->{hafas},
motis => $params->{motis},
station => $params->{station},
@@ -1091,6 +1150,8 @@ sub station {
my $dbris_service = $self->param('dbris')
// ( $user->{backend_dbris} ? $user->{backend_name} : undef );
+ my $efa_service = $self->param('efa')
+ // ( $user->{backend_efa} ? $user->{backend_name} : undef );
my $hafas_service = $self->param('hafas')
// ( $user->{backend_hafas} ? $user->{backend_name} : undef );
my $motis_service = $self->param('motis')
@@ -1118,6 +1179,15 @@ sub station {
lookbehind => 30,
);
}
+ elsif ($efa_service) {
+ $promise = $self->efa->get_departures_p(
+ service => $efa_service,
+ name => $station,
+ timestamp => $timestamp,
+ lookbehind => 30,
+ lookahead => 30,
+ );
+ }
elsif ($hafas_service) {
$promise = $self->hafas->get_departures_p(
service => $hafas_service,
@@ -1209,6 +1279,16 @@ sub station {
related_stations => [],
};
}
+ elsif ($efa_service) {
+ @results = map { $_->[0] }
+ sort { $b->[1] <=> $a->[1] }
+ map { [ $_, $_->datetime->epoch ] } $status->results;
+ $status = {
+ station_eva => $status->stop->id_num,
+ station_name => $status->stop->full_name,
+ related_stations => [],
+ };
+ }
elsif ($motis_service) {
@results = map { $_->[0] }
sort { $b->[1] <=> $a->[1] }
@@ -1268,12 +1348,14 @@ sub station {
eva => $user_status->{cancellation}{dep_eva},
destination_name =>
$user_status->{cancellation}{arr_name},
+ efa => $efa_service,
hafas => $hafas_service,
);
}
else {
$connections_p = $self->get_connecting_trains_p(
eva => $status->{station_eva},
+ efa => $efa_service,
hafas => $hafas_service
);
}
@@ -1287,6 +1369,7 @@ sub station {
'departures',
user => $user,
dbris => $dbris_service,
+ efa => $efa_service,
hafas => $hafas_service,
motis => $motis_service,
eva => $status->{station_eva},
@@ -1308,6 +1391,7 @@ sub station {
'departures',
user => $user,
dbris => $dbris_service,
+ efa => $efa_service,
hafas => $hafas_service,
motis => $motis_service,
eva => $status->{station_eva},
@@ -1328,6 +1412,7 @@ sub station {
'departures',
user => $user,
dbris => $dbris_service,
+ efa => $efa_service,
hafas => $hafas_service,
motis => $motis_service,
eva => $status->{station_eva},
diff --git a/lib/Travelynx/Helper/DBRIS.pm b/lib/Travelynx/Helper/DBRIS.pm
index 0a46758..9ddaa5f 100644
--- a/lib/Travelynx/Helper/DBRIS.pm
+++ b/lib/Travelynx/Helper/DBRIS.pm
@@ -94,7 +94,6 @@ sub get_journey_p {
my ( $self, %opt ) = @_;
my $promise = Mojo::Promise->new;
- my $now = DateTime->now( time_zone => 'Europe/Berlin' );
my $agent = $self->{user_agent};
my $proxy;
diff --git a/lib/Travelynx/Helper/EFA.pm b/lib/Travelynx/Helper/EFA.pm
new file mode 100644
index 0000000..ba11764
--- /dev/null
+++ b/lib/Travelynx/Helper/EFA.pm
@@ -0,0 +1,102 @@
+package Travelynx::Helper::EFA;
+
+# Copyright (C) 2024 Birte Kristina Friesel
+#
+# SPDX-License-Identifier: AGPL-3.0-or-later
+
+use strict;
+use warnings;
+use 5.020;
+
+use Travel::Status::DE::EFA;
+
+sub new {
+ my ( $class, %opt ) = @_;
+
+ my $version = $opt{version};
+
+ $opt{header}
+ = { 'User-Agent' =>
+"travelynx/${version} on $opt{root_url} +https://finalrewind.org/projects/travelynx"
+ };
+
+ return bless( \%opt, $class );
+}
+
+sub get_service {
+ my ( $self, $service ) = @_;
+
+ return Travel::Status::DE::EFA::get_service($service);
+}
+
+sub get_departures_p {
+ my ( $self, %opt ) = @_;
+
+ my $when = (
+ $opt{timestamp}
+ ? $opt{timestamp}->clone
+ : DateTime->now( time_zone => 'Europe/Berlin' )
+ )->subtract( minutes => $opt{lookbehind} );
+ return Travel::Status::DE::EFA->new_p(
+ service => $opt{service},
+ name => $opt{name},
+ datetime => $when,
+ full_routes => 1,
+ cache => $self->{realtime_cache},
+ promise => 'Mojo::Promise',
+ user_agent => $self->{user_agent}->request_timeout(5),
+ );
+}
+
+sub get_journey_p {
+ my ( $self, %opt ) = @_;
+
+ my $promise = Mojo::Promise->new;
+ my $agent = $self->{user_agent};
+ my $stopseq;
+
+ if ( $opt{trip_id} =~ m{ ^ ([^@]*) @ ([^@]*) [(] ([^)]*) [)] (.*) $ }x ) {
+ $stopseq = {
+ stateless => $1,
+ stop_id => $2,
+ date => $3,
+ key => $4
+ };
+ }
+ else {
+ return $promise->reject("Invalid trip_id: $opt{trip_id}");
+ }
+
+ Travel::Status::DE::EFA->new_p(
+ service => $opt{service},
+ stopseq => $stopseq,
+ cache => $self->{realtime_cache},
+ promise => 'Mojo::Promise',
+ user_agent => $agent->request_timeout(10),
+ )->then(
+ sub {
+ my ($efa) = @_;
+ my $journey = $efa->result;
+
+ if ($journey) {
+ $self->{log}->debug("get_journey_p($opt{trip_id}): success");
+ $promise->resolve($journey);
+ return;
+ }
+ $self->{log}->debug("get_journey_p($opt{trip_id}): no journey");
+ $promise->reject('no journey');
+ return;
+ }
+ )->catch(
+ sub {
+ my ($err) = @_;
+ $self->{log}->debug("get_journey_p($opt{trip_id}): error $err");
+ $promise->reject($err);
+ return;
+ }
+ )->wait;
+
+ return $promise;
+}
+
+1;
diff --git a/lib/Travelynx/Helper/HAFAS.pm b/lib/Travelynx/Helper/HAFAS.pm
index ebf44d2..c35dfdb 100644
--- a/lib/Travelynx/Helper/HAFAS.pm
+++ b/lib/Travelynx/Helper/HAFAS.pm
@@ -35,6 +35,20 @@ sub new {
return bless( \%opt, $class );
}
+sub class_to_product {
+ my ( $self, $hafas ) = @_;
+
+ my $bits = $hafas->get_active_service->{productbits};
+ my $ret;
+
+ for my $i ( 0 .. $#{$bits} ) {
+ $ret->{ 2**$i }
+ = ref( $bits->[$i] ) eq 'ARRAY' ? $bits->[$i][0] : $bits->[$i];
+ }
+
+ return $ret;
+}
+
sub get_service {
my ( $self, $service ) = @_;
diff --git a/lib/Travelynx/Model/InTransit.pm b/lib/Travelynx/Model/InTransit.pm
index cc943b3..470b45d 100644
--- a/lib/Travelynx/Model/InTransit.pm
+++ b/lib/Travelynx/Model/InTransit.pm
@@ -110,8 +110,6 @@ sub add {
my $now = DateTime->now( time_zone => 'Europe/Berlin' );
if ($train) {
-
- # IRIS
$db->insert(
'in_transit',
{
@@ -142,9 +140,60 @@ sub add {
}
);
}
- elsif ( $journey and $stop and $journey->can('product') ) {
-
- # HAFAS
+ elsif ( $journey
+ and $stop
+ and ref($journey) eq 'Travel::Status::DE::EFA::Trip' )
+ {
+ my @route;
+ for my $j_stop ( $journey->route ) {
+ push(
+ @route,
+ [
+ $j_stop->full_name,
+ $j_stop->id_num,
+ {
+ sched_arr => _epoch( $j_stop->sched_arr ),
+ sched_dep => _epoch( $j_stop->sched_dep ),
+ rt_arr => _epoch( $j_stop->rt_arr ),
+ rt_dep => _epoch( $j_stop->rt_dep ),
+ arr_delay => $j_stop->arr_delay,
+ dep_delay => $j_stop->dep_delay,
+ efa_load => $j_stop->occupancy,
+ lat => $j_stop->latlon->[0],
+ lon => $j_stop->latlon->[1],
+ }
+ ]
+ );
+ }
+ $db->insert(
+ 'in_transit',
+ {
+ user_id => $uid,
+ cancelled => 0, # TODO
+ checkin_station_id => $stop->id_num,
+ checkin_time => $now,
+ dep_platform => $stop->platform,
+ train_type => $journey->type // q{},
+ train_line => $journey->line,
+ train_no => $journey->number // q{},
+ train_id => $opt{trip_id},
+ sched_departure => $stop->sched_dep,
+ real_departure => $stop->rt_dep // $stop->sched_dep,
+ route => $json->encode( \@route ),
+ data => JSON->new->encode(
+ {
+ rt => $stop->rt_dep ? 1 : 0,
+ %{ $data // {} }
+ }
+ ),
+ backend_id => $backend_id,
+ }
+ );
+ }
+ elsif ( $journey
+ and $stop
+ and ref($journey) eq 'Travel::Status::DE::HAFAS::Journey' )
+ {
my @route;
my $product = $journey->product_at( $stop->loc->eva )
// $journey->product;
@@ -198,9 +247,10 @@ sub add {
}
);
}
- elsif ( $journey and $stop ) {
-
- # DBRIS
+ elsif ( $journey
+ and $stop
+ and ref($journey) eq 'Travel::Status::DE::DBRIS::Journey' )
+ {
my $number = $journey->train_no // $journey->number // $train_suffix;
my $line;
@@ -284,9 +334,10 @@ sub add {
}
);
}
- elsif ( $journey and $stopover ) {
-
- # MOTIS
+ elsif ( $journey
+ and $stopover
+ and ref($journey) eq 'Travel::Status::MOTIS::Trip' )
+ {
my @route;
for my $journey_stopover ( $journey->stopovers ) {
push(
@@ -340,7 +391,7 @@ sub add {
);
}
else {
- die('neither train nor journey specified');
+ die('invalid arguments / argument types passed to InTransit->add');
}
}
@@ -944,6 +995,41 @@ sub update_departure_dbris {
);
}
+sub update_departure_efa {
+ my ( $self, %opt ) = @_;
+ my $uid = $opt{uid};
+ my $db = $opt{db} // $self->{pg}->db;
+ my $dep_eva = $opt{dep_eva};
+ my $arr_eva = $opt{arr_eva};
+ my $journey = $opt{journey};
+ my $stop = $opt{stop};
+ my $json = JSON->new;
+
+ my $res_h = $db->select( 'in_transit', ['data'], { user_id => $uid } )
+ ->expand->hash;
+ my $ephemeral_data = $res_h ? $res_h->{data} : {};
+ if ( $stop->rt_dep ) {
+ $ephemeral_data->{rt} = 1;
+ }
+
+ # selecting on user_id and train_no avoids a race condition if a user checks
+ # into a new train while we are fetching data for their previous journey. In
+ # this case, the new train would receive data from the previous journey.
+ $db->update(
+ 'in_transit',
+ {
+ data => $json->encode($ephemeral_data),
+ real_departure => $stop->rt_dep,
+ },
+ {
+ user_id => $uid,
+ train_id => $opt{trip_id},
+ checkin_station_id => $dep_eva,
+ checkout_station_id => $arr_eva,
+ }
+ );
+}
+
sub update_departure_motis {
my ( $self, %opt ) = @_;
my $uid = $opt{uid};
@@ -1137,6 +1223,67 @@ sub update_arrival_dbris {
);
}
+sub update_arrival_efa {
+ my ( $self, %opt ) = @_;
+ my $uid = $opt{uid};
+ my $db = $opt{db} // $self->{pg}->db;
+ my $dep_eva = $opt{dep_eva};
+ my $arr_eva = $opt{arr_eva};
+ my $journey = $opt{journey};
+ my $stop = $opt{stop};
+ my $json = JSON->new;
+
+ my $res_h
+ = $db->select( 'in_transit', [ 'data', 'route' ], { user_id => $uid } )
+ ->expand->hash;
+ my $ephemeral_data = $res_h ? $res_h->{data} : {};
+ my $old_route = $res_h ? $res_h->{route} : [];
+
+ if ( $stop->rt_arr ) {
+ $ephemeral_data->{rt} = 1;
+ }
+
+ my @route;
+ for my $j_stop ( $journey->route ) {
+ push(
+ @route,
+ [
+ $j_stop->full_name,
+ $j_stop->id_num,
+ {
+ sched_arr => _epoch( $j_stop->sched_arr ),
+ sched_dep => _epoch( $j_stop->sched_dep ),
+ rt_arr => _epoch( $j_stop->rt_arr ),
+ rt_dep => _epoch( $j_stop->rt_dep ),
+ arr_delay => $j_stop->arr_delay,
+ dep_delay => $j_stop->dep_delay,
+ efa_load => $j_stop->occupancy,
+ lat => $j_stop->latlon->[0],
+ lon => $j_stop->latlon->[1],
+ }
+ ]
+ );
+ }
+
+ # selecting on user_id and train_no avoids a race condition if a user checks
+ # into a new train while we are fetching data for their previous journey. In
+ # this case, the new train would receive data from the previous journey.
+ $db->update(
+ 'in_transit',
+ {
+ data => $json->encode($ephemeral_data),
+ real_arrival => $stop->rt_arr,
+ route => $json->encode( [@route] ),
+ },
+ {
+ user_id => $uid,
+ train_id => $opt{trip_id},
+ checkin_station_id => $dep_eva,
+ checkout_station_id => $arr_eva,
+ }
+ );
+}
+
sub update_arrival_motis {
my ( $self, %opt ) = @_;
my $uid = $opt{uid};
diff --git a/lib/Travelynx/Model/Journeys.pm b/lib/Travelynx/Model/Journeys.pm
index 8efbab2..1662787 100755
--- a/lib/Travelynx/Model/Journeys.pm
+++ b/lib/Travelynx/Model/Journeys.pm
@@ -1735,6 +1735,8 @@ sub compute_stats {
sub get_stats {
my ( $self, %opt ) = @_;
+ $self->{log}->debug("get_stats");
+
if ( $opt{cancelled} ) {
$self->{log}
->warn('get_journey_stats called with illegal option cancelled => 1');
@@ -1761,9 +1763,12 @@ sub get_stats {
)
)
{
+ $self->{log}->debug("got cached journey stats for $year/$month");
return $stats;
}
+ $self->{log}->debug("computing journey stats for $year/$month");
+
my $interval_start = DateTime->new(
time_zone => 'Europe/Berlin',
year => 2000,
diff --git a/lib/Travelynx/Model/Stations.pm b/lib/Travelynx/Model/Stations.pm
index 174b3b4..bf35d1a 100644
--- a/lib/Travelynx/Model/Stations.pm
+++ b/lib/Travelynx/Model/Stations.pm
@@ -23,12 +23,15 @@ sub get_backend_id {
# special case
return 0;
}
- if ( $opt{hafas} and $self->{backend_id}{hafas}{ $opt{hafas} } ) {
- return $self->{backend_id}{hafas}{ $opt{hafas} };
- }
if ( $opt{dbris} and $self->{backend_id}{dbris}{ $opt{dbris} } ) {
return $self->{backend_id}{dbris}{ $opt{dbris} };
}
+ if ( $opt{efa} and $self->{backend_id}{efa}{ $opt{efa} } ) {
+ return $self->{backend_id}{efa}{ $opt{efa} };
+ }
+ if ( $opt{hafas} and $self->{backend_id}{hafas}{ $opt{hafas} } ) {
+ return $self->{backend_id}{hafas}{ $opt{hafas} };
+ }
if ( $opt{motis} and $self->{backend_id}{motis}{ $opt{motis} } ) {
return $self->{backend_id}{motis}{ $opt{motis} };
}
@@ -47,6 +50,17 @@ sub get_backend_id {
)->hash->{id};
$self->{backend_id}{dbris}{ $opt{dbris} } = $backend_id;
}
+ elsif ( $opt{efa} ) {
+ $backend_id = $db->select(
+ 'backends',
+ ['id'],
+ {
+ efa => 1,
+ name => $opt{efa}
+ }
+ )->hash->{id};
+ $self->{backend_id}{efa}{ $opt{efa} } = $backend_id;
+ }
elsif ( $opt{hafas} ) {
$backend_id = $db->select(
'backends',
@@ -100,7 +114,7 @@ sub get_backends {
$opt{db} //= $self->{pg}->db;
my $res = $opt{db}->select( 'backends',
- [ 'id', 'name', 'iris', 'hafas', 'dbris', 'motis' ] );
+ [ 'id', 'name', 'dbris', 'efa', 'hafas', 'iris', 'motis' ] );
my @ret;
while ( my $row = $res->hash ) {
@@ -109,9 +123,10 @@ sub get_backends {
{
id => $row->{id},
name => $row->{name},
- iris => $row->{iris},
dbris => $row->{dbris},
+ efa => $row->{efa},
hafas => $row->{hafas},
+ iris => $row->{iris},
motis => $row->{motis},
}
);
@@ -166,6 +181,44 @@ sub add_or_update {
return;
}
+ if ( $opt{efa} ) {
+ if (
+ my $s = $self->get_by_eva(
+ $stop->id_num,
+ db => $opt{db},
+ backend_id => $opt{backend_id}
+ )
+ )
+ {
+ $opt{db}->update(
+ 'stations',
+ {
+ name => $stop->full_name,
+ lat => $stop->latlon->[0],
+ lon => $stop->latlon->[1],
+ archived => 0
+ },
+ {
+ eva => $stop->id_num,
+ source => $opt{backend_id}
+ }
+ );
+ return;
+ }
+ $opt{db}->insert(
+ 'stations',
+ {
+ eva => $stop->id_num,
+ name => $stop->full_name,
+ lat => $stop->latlon->[0],
+ lon => $stop->latlon->[1],
+ source => $opt{backend_id},
+ archived => 0
+ }
+ );
+ return;
+ }
+
if ( $opt{motis} ) {
if (
my $s = $self->get_by_external_id(
diff --git a/lib/Travelynx/Model/Users.pm b/lib/Travelynx/Model/Users.pm
index 1c3692e..a552633 100644
--- a/lib/Travelynx/Model/Users.pm
+++ b/lib/Travelynx/Model/Users.pm
@@ -418,7 +418,7 @@ sub get {
. 'extract(epoch from registered_at) as registered_at_ts, '
. 'extract(epoch from last_seen) as last_seen_ts, '
. 'extract(epoch from deletion_requested) as deletion_requested_ts, '
- . 'backend_id, backend_name, hafas, dbris, motis',
+ . 'backend_id, backend_name, dbris, efa, hafas, motis',
{ id => $uid }
)->hash;
if ($user) {
@@ -458,6 +458,7 @@ sub get {
backend_id => $user->{backend_id},
backend_name => $user->{backend_name},
backend_dbris => $user->{dbris},
+ backend_efa => $user->{efa},
backend_hafas => $user->{hafas},
backend_motis => $user->{motis},
};