Multi-backend support
Squashed commit of the following: commit 92518024ba295456358618c0e8180bd8e996fdf1 Author: Birte Kristina Friesel <birte.friesel@uos.de> Date: Fri Jul 26 18:39:46 2024 +0200 add_or_update station: remove superfluos 'new backend id := old backend id' commit df21c20c6e4c86454f8a9ac69121404415217f2a Author: Birte Kristina Friesel <birte.friesel@uos.de> Date: Fri Jul 26 18:35:51 2024 +0200 revert connection targets min_count to 3 commit be335cef07d0b42874f5fc1de4a1d13396e8e807 Author: Birte Kristina Friesel <birte.friesel@uos.de> Date: Fri Jul 26 18:20:05 2024 +0200 mention backend selection in API documentation commit 9f41828fb4f18fd707e0087def3032e8d4c8d7d8 Author: Birte Kristina Friesel <derf@finalrewind.org> Date: Thu Jul 25 20:19:23 2024 +0200 use_history: not all backends provide route data in departure monitor commit 09714b4d89684b8331d0e96f564a4c7432318f70 Author: Birte Kristina Friesel <derf@finalrewind.org> Date: Thu Jul 25 20:11:44 2024 +0200 disambiguation: pass correct hafas parameter commit 8cdf1120fc32155dc6525be64601b7c10a9c7f52 Author: Birte Kristina Friesel <derf@finalrewind.org> Date: Thu Jul 25 20:11:28 2024 +0200 _checked_in: hide Zuglauf link for non-db checkins commit 7455653f541198e0e0a6d11aed421487ffdb6285 Author: Birte Kristina Friesel <derf@finalrewind.org> Date: Thu Jul 25 20:01:47 2024 +0200 debug output commit b9cda07f85601a58ea32dbdacdd5399f302db52b Author: Birte Kristina Friesel <derf@finalrewind.org> Date: Thu Jul 25 19:09:07 2024 +0200 fix remaining get_connection_targets / get_connecting_trains_p invocations commit 2759d7258c37c7498905cfe19f6b4c4f6d16bd21 Author: Birte Kristina Friesel <derf@finalrewind.org> Date: Wed Jul 24 20:50:12 2024 +0200 support non-DB HAFAS backends (WiP)
This commit is contained in:
parent
7811520a30
commit
47f76da4f8
33 changed files with 935 additions and 382 deletions
|
@ -448,7 +448,7 @@ sub startup {
|
|||
return Mojo::Promise->reject('You are already checked in');
|
||||
}
|
||||
|
||||
if ( $train_id =~ m{[|]} ) {
|
||||
if ( $opt{hafas} ) {
|
||||
return $self->_checkin_hafas_p(%opt);
|
||||
}
|
||||
|
||||
|
@ -482,7 +482,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 ($@) {
|
||||
|
@ -530,6 +532,7 @@ sub startup {
|
|||
my $promise = Mojo::Promise->new;
|
||||
|
||||
$self->hafas->get_journey_p(
|
||||
service => $opt{hafas},
|
||||
trip_id => $train_id,
|
||||
with_polyline => 1
|
||||
)->then(
|
||||
|
@ -551,17 +554,21 @@ sub startup {
|
|||
}
|
||||
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,
|
||||
data => { trip_id => $journey->id }
|
||||
uid => $uid,
|
||||
db => $db,
|
||||
journey => $journey,
|
||||
stop => $found,
|
||||
data => { trip_id => $journey->id },
|
||||
backend_id => $self->stations->get_backend_id(
|
||||
hafas => $opt{hafas}
|
||||
),
|
||||
);
|
||||
};
|
||||
if ($@) {
|
||||
|
@ -620,8 +627,8 @@ sub startup {
|
|||
# mustn't be called during a transaction
|
||||
if ( not $opt{in_transaction} ) {
|
||||
$self->run_hook( $uid, 'checkin' );
|
||||
if ( $journey->class <= 16 ) {
|
||||
$self->app->add_wagonorder( $uid, 1, $journey->id,
|
||||
if ( $opt{hafas} eq 'DB' and $journey->class <= 16 ) {
|
||||
$self->add_wagonorder( $uid, 1, $journey->id,
|
||||
$found->sched_dep, $journey->number );
|
||||
$self->add_stationinfo( $uid, 1, $journey->id,
|
||||
$found->loc->eva );
|
||||
|
@ -744,6 +751,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;
|
||||
|
||||
|
@ -765,7 +773,7 @@ sub startup {
|
|||
return $promise->resolve( 0, 'race condition' );
|
||||
}
|
||||
|
||||
if ( $train_id =~ m{[|]} ) {
|
||||
if ( $user->{is_hafas} ) {
|
||||
return $self->_checkout_hafas_p(%opt);
|
||||
}
|
||||
|
||||
|
@ -1736,7 +1744,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},
|
||||
)
|
||||
)
|
||||
{
|
||||
|
@ -1745,7 +1754,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},
|
||||
)
|
||||
)
|
||||
{
|
||||
|
@ -1760,14 +1770,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};
|
||||
|
@ -1776,6 +1792,10 @@ sub startup {
|
|||
checked_in => 0,
|
||||
cancelled => 0,
|
||||
cancellation => $latest_cancellation,
|
||||
backend_id => $latest->{backend_id},
|
||||
backend_name => $latest->{backend_name},
|
||||
is_iris => $latest->{is_iris},
|
||||
is_hafas => $latest->{is_hafas},
|
||||
journey_id => $latest->{journey_id},
|
||||
timestamp => $action_time,
|
||||
timestamp_delta => $now->epoch - $action_time->epoch,
|
||||
|
@ -1833,7 +1853,12 @@ sub startup {
|
|||
$status->{checked_in}
|
||||
or $status->{cancelled}
|
||||
) ? \1 : \0,
|
||||
comment => $status->{comment},
|
||||
comment => $status->{comment},
|
||||
backend => {
|
||||
id => $status->{backend_id},
|
||||
type => $status->{is_hafas} ? 'HAFAS' : 'IRIS-TTS',
|
||||
name => $status->{backend_name},
|
||||
},
|
||||
fromStation => {
|
||||
ds100 => $status->{dep_ds100},
|
||||
name => $status->{dep_name},
|
||||
|
@ -1992,6 +2017,7 @@ sub startup {
|
|||
"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},
|
||||
|
@ -2324,6 +2350,7 @@ sub startup {
|
|||
$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');
|
||||
|
@ -2345,6 +2372,7 @@ 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/select_backend')->to('account#change_backend');
|
||||
$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');
|
||||
|
|
|
@ -8,6 +8,7 @@ use Mojo::Base 'Mojolicious::Command';
|
|||
use DateTime;
|
||||
use File::Slurp qw(read_file);
|
||||
use JSON;
|
||||
use Travel::Status::DE::HAFAS;
|
||||
use Travel::Status::DE::IRIS::Stations;
|
||||
|
||||
has description => 'Initialize or upgrade database layout';
|
||||
|
@ -1918,7 +1919,7 @@ my @migrations = (
|
|||
|
||||
# v49 -> v50
|
||||
# travelynx 2.0 introduced proper HAFAS support, so there is no need for
|
||||
# the 'FYI, here is some hAFAS data' kludge anymore.
|
||||
# the 'FYI, here is some HAFAS data' kludge anymore.
|
||||
sub {
|
||||
my ($db) = @_;
|
||||
$db->query(
|
||||
|
@ -2310,6 +2311,235 @@ my @migrations = (
|
|||
);
|
||||
},
|
||||
|
||||
# v54 -> v55
|
||||
# do not share stations between backends
|
||||
sub {
|
||||
my ($db) = @_;
|
||||
$db->query(
|
||||
qq{
|
||||
alter table schema_version add column hafas varchar(12);
|
||||
alter table users drop column external_services;
|
||||
alter table users add column backend_id smallint references backends (id) default 1;
|
||||
alter table stations drop constraint stations_pkey;
|
||||
alter table stations add unique (eva, source);
|
||||
create index eva_by_source on stations (eva, source);
|
||||
create index eva on stations (eva);
|
||||
alter table related_stations drop constraint related_stations_eva_meta_key;
|
||||
drop index rel_eva;
|
||||
alter table related_stations add column backend_id smallint;
|
||||
update related_stations set backend_id = 1;
|
||||
alter table related_stations alter column backend_id set not null;
|
||||
alter table related_stations add constraint backend_fk foreign key (backend_id) references backends (id);
|
||||
alter table related_stations add unique (eva, meta, backend_id);
|
||||
create index related_stations_eva_backend_key on related_stations (eva, backend_id);
|
||||
}
|
||||
);
|
||||
|
||||
# up until now, IRIS and DB HAFAS shared stations, with IRIS taking
|
||||
# preference. As of v2.7, this is no longer the case. However, old DB
|
||||
# HAFAS journeys may still reference IRIS-specific stations. So, we
|
||||
# make all IRIS stations available as DB HAFAS stations as well.
|
||||
my $total
|
||||
= $db->select( 'stations', 'count(*) as count', { source => 0 } )
|
||||
->hash->{count};
|
||||
my $count = 0;
|
||||
|
||||
# Caveat: If this is a fresh installation, there are no IRIS stations
|
||||
# in the database yet. So we have to populate it first.
|
||||
if ( not $total ) {
|
||||
say
|
||||
'Preparing to untangle IRIS / HAFAS stations, this may take a while ...';
|
||||
$total = scalar Travel::Status::DE::IRIS::Stations::get_stations();
|
||||
for my $s ( Travel::Status::DE::IRIS::Stations::get_stations() ) {
|
||||
my ( $ds100, $name, $eva, $lon, $lat ) = @{$s};
|
||||
if ( $ENV{__TRAVELYNX_TEST_MINI_IRIS}
|
||||
and ( $eva < 8000000 or $eva > 8000100 ) )
|
||||
{
|
||||
next;
|
||||
}
|
||||
$db->insert(
|
||||
'stations',
|
||||
{
|
||||
eva => $eva,
|
||||
ds100 => $ds100,
|
||||
name => $name,
|
||||
lat => $lat,
|
||||
lon => $lon,
|
||||
source => 0,
|
||||
archived => 0
|
||||
},
|
||||
);
|
||||
if ( $count++ % 1000 == 0 ) {
|
||||
printf( " %2.0f%% complete\n", $count * 100 / $total );
|
||||
}
|
||||
}
|
||||
$count = 0;
|
||||
}
|
||||
|
||||
say 'Untangling IRIS / HAFAS stations, this may take a while ...';
|
||||
my $res = $db->query(
|
||||
qq{
|
||||
select eva, ds100, name, lat, lon, archived
|
||||
from stations
|
||||
where source = 0;
|
||||
}
|
||||
);
|
||||
while ( my $row = $res->hash ) {
|
||||
$db->insert(
|
||||
'stations',
|
||||
{
|
||||
eva => $row->{eva},
|
||||
ds100 => $row->{ds100},
|
||||
name => $row->{name},
|
||||
lat => $row->{lat},
|
||||
lon => $row->{lon},
|
||||
archived => $row->{archived},
|
||||
source => 1,
|
||||
}
|
||||
);
|
||||
if ( $count++ % 1000 == 0 ) {
|
||||
printf( " %2.0f%% complete\n", $count * 100 / $total );
|
||||
}
|
||||
}
|
||||
$db->query(
|
||||
qq{
|
||||
alter table in_transit add constraint in_transit_checkin_eva_fk
|
||||
foreign key (checkin_station_id, backend_id)
|
||||
references stations (eva, source);
|
||||
alter table in_transit add constraint in_transit_checkout_eva_fk
|
||||
foreign key (checkout_station_id, backend_id)
|
||||
references stations (eva, source);
|
||||
alter table journeys add constraint journeys_checkin_eva_fk
|
||||
foreign key (checkin_station_id, backend_id)
|
||||
references stations (eva, source);
|
||||
alter table journeys add constraint journeys_checkout_eva_fk
|
||||
foreign key (checkout_station_id, backend_id)
|
||||
references stations (eva, source);
|
||||
drop view in_transit_str;
|
||||
drop view journeys_str;
|
||||
drop view follows_in_transit;
|
||||
create view in_transit_str as select
|
||||
user_id,
|
||||
backend.iris as is_iris, backend.hafas as is_hafas,
|
||||
backend.efa as is_efa, backend.ris as is_ris,
|
||||
backend.name as backend_name, in_transit.backend_id as backend_id,
|
||||
train_type, train_line, train_no, train_id,
|
||||
extract(epoch from checkin_time) as checkin_ts,
|
||||
extract(epoch from sched_departure) as sched_dep_ts,
|
||||
extract(epoch from real_departure) as real_dep_ts,
|
||||
checkin_station_id as dep_eva,
|
||||
dep_station.ds100 as dep_ds100,
|
||||
dep_station.name as dep_name,
|
||||
dep_station.lat as dep_lat,
|
||||
dep_station.lon as dep_lon,
|
||||
extract(epoch from checkout_time) as checkout_ts,
|
||||
extract(epoch from sched_arrival) as sched_arr_ts,
|
||||
extract(epoch from real_arrival) as real_arr_ts,
|
||||
checkout_station_id as arr_eva,
|
||||
arr_station.ds100 as arr_ds100,
|
||||
arr_station.name as arr_name,
|
||||
arr_station.lat as arr_lat,
|
||||
arr_station.lon as arr_lon,
|
||||
polyline_id,
|
||||
polylines.polyline as polyline,
|
||||
visibility,
|
||||
coalesce(visibility, users.public_level & 127) as effective_visibility,
|
||||
cancelled, route, messages, user_data,
|
||||
dep_platform, arr_platform, data
|
||||
from in_transit
|
||||
left join polylines on polylines.id = polyline_id
|
||||
left join users on users.id = user_id
|
||||
left join stations as dep_station on checkin_station_id = dep_station.eva and in_transit.backend_id = dep_station.source
|
||||
left join stations as arr_station on checkout_station_id = arr_station.eva and in_transit.backend_id = arr_station.source
|
||||
left join backends as backend on in_transit.backend_id = backend.id
|
||||
;
|
||||
create view journeys_str as select
|
||||
journeys.id as journey_id, user_id,
|
||||
backend.iris as is_iris, backend.hafas as is_hafas,
|
||||
backend.efa as is_efa, backend.ris as is_ris,
|
||||
backend.name as backend_name, journeys.backend_id as backend_id,
|
||||
train_type, train_line, train_no, train_id,
|
||||
extract(epoch from checkin_time) as checkin_ts,
|
||||
extract(epoch from sched_departure) as sched_dep_ts,
|
||||
extract(epoch from real_departure) as real_dep_ts,
|
||||
checkin_station_id as dep_eva,
|
||||
dep_station.ds100 as dep_ds100,
|
||||
dep_station.name as dep_name,
|
||||
dep_station.lat as dep_lat,
|
||||
dep_station.lon as dep_lon,
|
||||
extract(epoch from checkout_time) as checkout_ts,
|
||||
extract(epoch from sched_arrival) as sched_arr_ts,
|
||||
extract(epoch from real_arrival) as real_arr_ts,
|
||||
checkout_station_id as arr_eva,
|
||||
arr_station.ds100 as arr_ds100,
|
||||
arr_station.name as arr_name,
|
||||
arr_station.lat as arr_lat,
|
||||
arr_station.lon as arr_lon,
|
||||
polylines.polyline as polyline,
|
||||
visibility,
|
||||
coalesce(visibility, users.public_level & 127) as effective_visibility,
|
||||
cancelled, edited, route, messages, user_data,
|
||||
dep_platform, arr_platform
|
||||
from journeys
|
||||
left join polylines on polylines.id = polyline_id
|
||||
left join users on users.id = user_id
|
||||
left join stations as dep_station on checkin_station_id = dep_station.eva and journeys.backend_id = dep_station.source
|
||||
left join stations as arr_station on checkout_station_id = arr_station.eva and journeys.backend_id = arr_station.source
|
||||
left join backends as backend on journeys.backend_id = backend.id
|
||||
;
|
||||
create view follows_in_transit as select
|
||||
r1.subject_id as follower_id, user_id as followee_id,
|
||||
users.name as followee_name,
|
||||
train_type, train_line, train_no, train_id,
|
||||
in_transit.backend_id as backend_id,
|
||||
extract(epoch from checkin_time) as checkin_ts,
|
||||
extract(epoch from sched_departure) as sched_dep_ts,
|
||||
extract(epoch from real_departure) as real_dep_ts,
|
||||
checkin_station_id as dep_eva,
|
||||
dep_station.ds100 as dep_ds100,
|
||||
dep_station.name as dep_name,
|
||||
dep_station.lat as dep_lat,
|
||||
dep_station.lon as dep_lon,
|
||||
extract(epoch from checkout_time) as checkout_ts,
|
||||
extract(epoch from sched_arrival) as sched_arr_ts,
|
||||
extract(epoch from real_arrival) as real_arr_ts,
|
||||
checkout_station_id as arr_eva,
|
||||
arr_station.ds100 as arr_ds100,
|
||||
arr_station.name as arr_name,
|
||||
arr_station.lat as arr_lat,
|
||||
arr_station.lon as arr_lon,
|
||||
polyline_id,
|
||||
polylines.polyline as polyline,
|
||||
visibility,
|
||||
coalesce(visibility, users.public_level & 127) as effective_visibility,
|
||||
cancelled, route, messages, user_data,
|
||||
dep_platform, arr_platform, data
|
||||
from in_transit
|
||||
left join polylines on polylines.id = polyline_id
|
||||
left join users on users.id = user_id
|
||||
left join relations as r1 on r1.predicate = 1 and r1.object_id = user_id
|
||||
left join stations as dep_station on checkin_station_id = dep_station.eva and in_transit.backend_id = dep_station.source
|
||||
left join stations as arr_station on checkout_station_id = arr_station.eva and in_transit.backend_id = arr_station.source
|
||||
order by checkin_time desc
|
||||
;
|
||||
create view users_with_backend as select
|
||||
users.id as id, users.name as name, status, public_level,
|
||||
email, password, registered_at, last_seen,
|
||||
deletion_requested, deletion_notified, use_history,
|
||||
accept_follows, notifications, profile, backend_id, iris,
|
||||
hafas, efa, ris, backend.name as backend_name
|
||||
from users
|
||||
left join backends as backend on users.backend_id = backend.id
|
||||
;
|
||||
update schema_version set version = 55;
|
||||
update schema_version set hafas = '0';
|
||||
}
|
||||
);
|
||||
say
|
||||
'This travelynx instance now has support for non-DB HAFAS backends.';
|
||||
say
|
||||
'If the migration fails due to a deadlock, re-run it after stopping all background workers';
|
||||
},
|
||||
);
|
||||
|
||||
sub sync_stations {
|
||||
|
@ -2341,7 +2571,7 @@ sub sync_stations {
|
|||
},
|
||||
{
|
||||
on_conflict => \
|
||||
'(eva) do update set archived = false, source = 0, ds100 = EXCLUDED.ds100, name=EXCLUDED.name, lat=EXCLUDED.lat, lon=EXCLUDED.lon'
|
||||
'(eva, source) do update set archived = false, source = 0, ds100 = EXCLUDED.ds100, name=EXCLUDED.name, lat=EXCLUDED.lat, lon=EXCLUDED.lon'
|
||||
}
|
||||
);
|
||||
if ( $count++ % 1000 == 0 ) {
|
||||
|
@ -2500,6 +2730,26 @@ sub sync_stations {
|
|||
}
|
||||
}
|
||||
|
||||
sub sync_backends {
|
||||
my ($db) = @_;
|
||||
for my $service ( Travel::Status::DE::HAFAS::get_services()) {
|
||||
$db->insert(
|
||||
'backends',
|
||||
{
|
||||
iris => 0,
|
||||
hafas => 1,
|
||||
efa => 0,
|
||||
ris => 0,
|
||||
name => $service->{shortname},
|
||||
},
|
||||
{ on_conflict => undef }
|
||||
);
|
||||
}
|
||||
|
||||
$db->update( 'schema_version',
|
||||
{ hafas => $Travel::Status::DE::HAFAS::VERSION } );
|
||||
}
|
||||
|
||||
sub setup_db {
|
||||
my ($db) = @_;
|
||||
my $tx = $db->begin;
|
||||
|
@ -2566,9 +2816,9 @@ sub migrate_db {
|
|||
}
|
||||
|
||||
my $iris_version = get_schema_version( $db, 'iris' );
|
||||
say "Found IRIS station database v${iris_version}";
|
||||
say "Found IRIS station table v${iris_version}";
|
||||
if ( $iris_version eq $Travel::Status::DE::IRIS::Stations::VERSION ) {
|
||||
say 'Station database is up-to-date';
|
||||
say 'Station table is up-to-date';
|
||||
}
|
||||
else {
|
||||
eval {
|
||||
|
@ -2587,6 +2837,18 @@ sub migrate_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 ) {
|
||||
say 'Backend table is up-to-date';
|
||||
}
|
||||
else {
|
||||
say
|
||||
"Synchronizing with Travel::Status::DE::HAFAS $Travel::Status::DE::HAFAS::VERSION";
|
||||
sync_backends($db);
|
||||
}
|
||||
|
||||
|
||||
$db->update( 'schema_version',
|
||||
{ travelynx => $self->app->config->{version} } );
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
package Travelynx::Command::dumpconfig;
|
||||
|
||||
# Copyright (C) 2020-2023 Birte Kristina Friesel
|
||||
#
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
|
|
@ -47,9 +47,12 @@ sub run {
|
|||
my $arr = $entry->{arr_eva};
|
||||
my $train_id = $entry->{train_id};
|
||||
|
||||
if ( $train_id =~ m{[|]} ) {
|
||||
if ( $entry->{is_hafas} ) {
|
||||
|
||||
$self->app->hafas->get_journey_p( trip_id => $train_id )->then(
|
||||
$self->app->hafas->get_journey_p(
|
||||
trip_id => $train_id,
|
||||
service => $entry->{backend_name}
|
||||
)->then(
|
||||
sub {
|
||||
my ($journey) = @_;
|
||||
|
||||
|
@ -135,6 +138,9 @@ sub run {
|
|||
next;
|
||||
}
|
||||
|
||||
# TODO irgendwo ist hier ne race condition wo ein neuer checkin (in HAFAS) mit IRIS-Daten überschrieben wird.
|
||||
# Die ganzen updates brauchen wirklich mal sanity checks mit train id ...
|
||||
|
||||
# Note: IRIS data is not always updated in real-time. Both departure and
|
||||
# arrival delays may take several minutes to appear, especially in case
|
||||
# of large-scale disturbances. We work around this by continuing to
|
||||
|
|
|
@ -999,6 +999,52 @@ sub password_form {
|
|||
$self->render('change_password');
|
||||
}
|
||||
|
||||
sub backend_form {
|
||||
my ($self) = @_;
|
||||
my $user = $self->current_user;
|
||||
|
||||
my @backends = $self->stations->get_backends;
|
||||
|
||||
for my $backend (@backends) {
|
||||
my $type = 'UNKNOWN';
|
||||
if ( $backend->{iris} ) {
|
||||
$type = 'IRIS-TTS';
|
||||
$backend->{name} = 'DB';
|
||||
}
|
||||
elsif ( $backend->{hafas} ) {
|
||||
$type = 'HAFAS';
|
||||
$backend->{longname}
|
||||
= $self->hafas->get_service( $backend->{name} )->{name};
|
||||
}
|
||||
$backend->{type} = $type;
|
||||
}
|
||||
|
||||
$self->render(
|
||||
'select_backend',
|
||||
backends => \@backends,
|
||||
user => $user,
|
||||
redirect_to => $self->req->param('redirect_to') // '/',
|
||||
);
|
||||
}
|
||||
|
||||
sub change_backend {
|
||||
my ($self) = @_;
|
||||
|
||||
my $backend_id = $self->req->param('backend');
|
||||
my $redir = $self->req->param('redirect_to') // '/';
|
||||
|
||||
if ( $backend_id !~ m{ ^ \d+ $ }x ) {
|
||||
$self->redirect_to($redir);
|
||||
}
|
||||
|
||||
$self->users->set_backend(
|
||||
uid => $self->current_user->{id},
|
||||
backend_id => $backend_id,
|
||||
);
|
||||
|
||||
$self->redirect_to($redir);
|
||||
}
|
||||
|
||||
sub change_password {
|
||||
my ($self) = @_;
|
||||
my $old_password = $self->req->param('oldpw');
|
||||
|
|
|
@ -117,6 +117,7 @@ sub travel_v1 {
|
|||
deprecated => \0,
|
||||
error => 'Malformed JSON',
|
||||
},
|
||||
status => 400,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
@ -130,6 +131,7 @@ sub travel_v1 {
|
|||
deprecated => \0,
|
||||
error => 'Malformed token',
|
||||
},
|
||||
status => 400,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
@ -143,6 +145,7 @@ sub travel_v1 {
|
|||
deprecated => \0,
|
||||
error => 'Malformed token',
|
||||
},
|
||||
status => 400,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
@ -155,6 +158,7 @@ sub travel_v1 {
|
|||
deprecated => \0,
|
||||
error => 'Invalid token',
|
||||
},
|
||||
status => 400,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
@ -169,6 +173,7 @@ sub travel_v1 {
|
|||
error => 'Missing or invalid action',
|
||||
status => $self->get_user_status_json_v1( uid => $uid )
|
||||
},
|
||||
status => 400,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
@ -177,7 +182,8 @@ sub travel_v1 {
|
|||
my $from_station = sanitize( q{}, $payload->{fromStation} );
|
||||
my $to_station = sanitize( q{}, $payload->{toStation} );
|
||||
my $train_id;
|
||||
my $hafas = exists $payload->{train}{journeyID} ? 1 : 0;
|
||||
my $hafas = sanitize(undef, $payload->{hafas});
|
||||
$hafas //= exists $payload->{train}{journeyID} ? 'DB' : undef;
|
||||
|
||||
if (
|
||||
not(
|
||||
|
@ -195,11 +201,12 @@ sub travel_v1 {
|
|||
error => 'Missing fromStation or train data',
|
||||
status => $self->get_user_status_json_v1( uid => $uid )
|
||||
},
|
||||
status => 400,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if ( not $hafas and not $self->stations->search($from_station) ) {
|
||||
if ( not $hafas and not $self->stations->search($from_station, iris => 1) ) {
|
||||
$self->render(
|
||||
json => {
|
||||
success => \0,
|
||||
|
@ -207,13 +214,14 @@ sub travel_v1 {
|
|||
error => 'Unknown fromStation',
|
||||
status => $self->get_user_status_json_v1( uid => $uid )
|
||||
},
|
||||
status => 400,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if ( $to_station
|
||||
and not $hafas
|
||||
and not $self->stations->search($to_station) )
|
||||
and not $self->stations->search($to_station, iris => 1) )
|
||||
{
|
||||
$self->render(
|
||||
json => {
|
||||
|
@ -222,6 +230,7 @@ sub travel_v1 {
|
|||
error => 'Unknown toStation',
|
||||
status => $self->get_user_status_json_v1( uid => $uid )
|
||||
},
|
||||
status => 400,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
@ -273,7 +282,8 @@ sub travel_v1 {
|
|||
return $self->checkin_p(
|
||||
station => $from_station,
|
||||
train_id => $train_id,
|
||||
uid => $uid
|
||||
uid => $uid,
|
||||
hafas => $hafas,
|
||||
);
|
||||
}
|
||||
)->then(
|
||||
|
@ -654,10 +664,13 @@ sub autocomplete {
|
|||
|
||||
$self->res->headers->cache_control('max-age=86400, immutable');
|
||||
|
||||
my $backend_id = $self->param('backend_id') // 1;
|
||||
|
||||
my $output
|
||||
= "document.addEventListener('DOMContentLoaded',function(){M.Autocomplete.init(document.querySelectorAll('.autocomplete'),{\n";
|
||||
$output .= 'minLength:3,limit:50,data:';
|
||||
$output .= encode_json( $self->stations->get_for_autocomplete );
|
||||
$output
|
||||
.= encode_json( $self->stations->get_for_autocomplete( backend_id => $backend_id ) );
|
||||
$output .= "\n});});\n";
|
||||
|
||||
$self->render(
|
||||
|
|
|
@ -24,10 +24,15 @@ sub has_str_in_list {
|
|||
return;
|
||||
}
|
||||
|
||||
# when called with "eva" provided: look up connections from eva, either
|
||||
# for provided backend_id / hafas or (if not provided) for user backend id.
|
||||
# When calld without "eva": look up connections from current/latest arrival
|
||||
# eva, using the checkin's backend id.
|
||||
sub get_connecting_trains_p {
|
||||
my ( $self, %opt ) = @_;
|
||||
|
||||
my $uid = $opt{uid} //= $self->current_user->{id};
|
||||
my $user = $self->current_user;
|
||||
my $uid = $opt{uid} //= $user->{id};
|
||||
my $use_history = $self->users->use_history( uid => $uid );
|
||||
|
||||
my ( $eva, $exclude_via, $exclude_train_id, $exclude_before );
|
||||
|
@ -43,10 +48,20 @@ sub get_connecting_trains_p {
|
|||
elsif ( $opt{destination_name} ) {
|
||||
$eva = $opt{eva};
|
||||
}
|
||||
if ( not defined $opt{backend_id} ) {
|
||||
if ( $opt{hafas} ) {
|
||||
$opt{backend_id}
|
||||
= $self->stations->get_backend_id( hafas => $opt{hafas} );
|
||||
}
|
||||
else {
|
||||
$opt{backend_id} = $user->{backend_id};
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ( $use_history & 0x02 ) {
|
||||
my $status = $self->get_user_status;
|
||||
$opt{backend_id} = $status->{backend_id};
|
||||
$eva = $status->{arr_eva};
|
||||
$exclude_via = $status->{dep_name};
|
||||
$exclude_train_id = $status->{train_id};
|
||||
|
@ -65,10 +80,12 @@ sub get_connecting_trains_p {
|
|||
return $promise->reject;
|
||||
}
|
||||
|
||||
my ( $dest_ids, $destinations )
|
||||
= $self->journeys->get_connection_targets(%opt);
|
||||
$self->log->debug(
|
||||
"get_connecting_trains_p(backend_id => $opt{backend_id}, eva => $eva)");
|
||||
|
||||
my @destinations = uniq_by { $_->{name} } @{$destinations};
|
||||
my @destinations = $self->journeys->get_connection_targets(%opt);
|
||||
|
||||
@destinations = uniq_by { $_->{name} } @destinations;
|
||||
|
||||
if ($exclude_via) {
|
||||
@destinations = grep { $_->{name} ne $exclude_via } @destinations;
|
||||
|
@ -78,11 +95,8 @@ sub get_connecting_trains_p {
|
|||
return $promise->reject;
|
||||
}
|
||||
|
||||
my $iris_eva = $eva;
|
||||
if ( $eva < 8000000 ) {
|
||||
$iris_eva = ( List::Util::first { $_ >= 8000000 } @{$dest_ids} )
|
||||
// $eva;
|
||||
}
|
||||
$self->log->debug( 'get_connection_targets returned '
|
||||
. join( q{, }, map { $_->{name} } @destinations ) );
|
||||
|
||||
my $can_check_in = not $arr_epoch || ( $arr_countdown // 1 ) < 0;
|
||||
my $lookahead
|
||||
|
@ -91,11 +105,9 @@ sub get_connecting_trains_p {
|
|||
my $iris_promise = Mojo::Promise->new;
|
||||
my %via_count = map { $_->{name} => 0 } @destinations;
|
||||
|
||||
if ( $iris_eva >= 8000000
|
||||
and List::Util::any { $_->{eva} >= 8000000 } @destinations )
|
||||
{
|
||||
if ( $opt{backend_id} == 0 ) {
|
||||
$self->iris->get_departures_p(
|
||||
station => $iris_eva,
|
||||
station => $eva,
|
||||
lookbehind => 10,
|
||||
lookahead => $lookahead,
|
||||
with_related => 1
|
||||
|
@ -103,7 +115,7 @@ sub get_connecting_trains_p {
|
|||
sub {
|
||||
my ($stationboard) = @_;
|
||||
if ( $stationboard->{errstr} ) {
|
||||
$iris_promise->resolve( [] );
|
||||
$promise->resolve( [], [] );
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -237,105 +249,30 @@ sub get_connecting_trains_p {
|
|||
}
|
||||
}
|
||||
|
||||
$iris_promise->resolve( [ @results, @cancellations ] );
|
||||
$promise->resolve( [ @results, @cancellations ], [] );
|
||||
return;
|
||||
}
|
||||
)->catch(
|
||||
sub {
|
||||
$iris_promise->resolve( [] );
|
||||
$promise->resolve( [], [] );
|
||||
return;
|
||||
}
|
||||
)->wait;
|
||||
}
|
||||
else {
|
||||
$iris_promise->resolve( [] );
|
||||
}
|
||||
|
||||
my $hafas_promise = Mojo::Promise->new;
|
||||
$self->hafas->get_departures_p(
|
||||
eva => $eva,
|
||||
lookbehind => 10,
|
||||
lookahead => $lookahead
|
||||
)->then(
|
||||
sub {
|
||||
my ($status) = @_;
|
||||
$hafas_promise->resolve( [ $status->results ] );
|
||||
return;
|
||||
}
|
||||
)->catch(
|
||||
sub {
|
||||
# HAFAS data is optional.
|
||||
# Errors are logged by get_json_p and can be silently ignored here.
|
||||
$hafas_promise->resolve( [] );
|
||||
return;
|
||||
}
|
||||
)->wait;
|
||||
|
||||
Mojo::Promise->all( $iris_promise, $hafas_promise )->then(
|
||||
sub {
|
||||
my ( $iris, $hafas ) = @_;
|
||||
my @iris_trains = @{ $iris->[0] };
|
||||
my @all_hafas_trains = @{ $hafas->[0] };
|
||||
my @hafas_trains;
|
||||
|
||||
# We've already got a list of connecting trains; this function
|
||||
# only adds further information to them. We ignore errors, as
|
||||
# partial data is better than no data.
|
||||
eval {
|
||||
for my $iris_train (@iris_trains) {
|
||||
if ( $iris_train->[0]->departure_is_cancelled ) {
|
||||
for my $hafas_train (@all_hafas_trains) {
|
||||
if ( $hafas_train->number
|
||||
and $hafas_train->number
|
||||
== $iris_train->[0]->train_no )
|
||||
{
|
||||
$hafas_train->{iris_seen} = 1;
|
||||
next;
|
||||
}
|
||||
}
|
||||
next;
|
||||
}
|
||||
for my $hafas_train (@all_hafas_trains) {
|
||||
if ( $hafas_train->number
|
||||
and $hafas_train->number
|
||||
== $iris_train->[0]->train_no )
|
||||
{
|
||||
$hafas_train->{iris_seen} = 1;
|
||||
if ( $hafas_train->load
|
||||
and $hafas_train->load->{SECOND} )
|
||||
{
|
||||
$iris_train->[3] = $hafas_train->load;
|
||||
}
|
||||
for my $stop ( $hafas_train->route ) {
|
||||
if ( $stop->loc->name
|
||||
and $stop->loc->name eq
|
||||
$iris_train->[1]->{name}
|
||||
and $stop->arr )
|
||||
{
|
||||
$iris_train->[2] = $stop->arr;
|
||||
if ( $iris_train->[0]->departure_delay
|
||||
and not $stop->arr_delay )
|
||||
{
|
||||
$iris_train->[2]
|
||||
->add( minutes => $iris_train->[0]
|
||||
->departure_delay );
|
||||
}
|
||||
last;
|
||||
}
|
||||
}
|
||||
last;
|
||||
}
|
||||
}
|
||||
}
|
||||
my $hafas_service
|
||||
= $self->stations->get_hafas_name( backend_id => $opt{backend_id} );
|
||||
$self->hafas->get_departures_p(
|
||||
service => $hafas_service,
|
||||
eva => $eva,
|
||||
lookbehind => 10,
|
||||
lookahead => $lookahead
|
||||
)->then(
|
||||
sub {
|
||||
my ($status) = @_;
|
||||
my @hafas_trains;
|
||||
my @all_hafas_trains = $status->results;
|
||||
for my $hafas_train (@all_hafas_trains) {
|
||||
if ( $hafas_train->{iris_seen} ) {
|
||||
next;
|
||||
}
|
||||
if ( $hafas_train->station_eva >= 8000000 ) {
|
||||
|
||||
# better safe than sorry, for now
|
||||
next;
|
||||
}
|
||||
for my $stop ( $hafas_train->route ) {
|
||||
for my $dest (@destinations) {
|
||||
if ( $stop->loc->name
|
||||
|
@ -353,30 +290,30 @@ sub get_connecting_trains_p {
|
|||
}
|
||||
if ( $departure->epoch >= $exclude_before ) {
|
||||
$via_count{ $dest->{name} }++;
|
||||
push( @hafas_trains,
|
||||
[ $hafas_train, $dest, $arrival ] );
|
||||
push(
|
||||
@hafas_trains,
|
||||
[
|
||||
$hafas_train, $dest,
|
||||
$arrival, $hafas_service
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
if ($@) {
|
||||
$self->app->log->error(
|
||||
"get_connecting_trains_p($uid): IRIS/HAFAS merge failed: $@"
|
||||
);
|
||||
$promise->resolve( [], \@hafas_trains );
|
||||
return;
|
||||
}
|
||||
|
||||
$promise->resolve( \@iris_trains, \@hafas_trains );
|
||||
return;
|
||||
}
|
||||
)->catch(
|
||||
sub {
|
||||
my ($err) = @_;
|
||||
$promise->reject($err);
|
||||
return;
|
||||
}
|
||||
)->wait;
|
||||
)->catch(
|
||||
sub {
|
||||
my ($err) = @_;
|
||||
$self->log->debug("get_connection_trains: hafas: $err");
|
||||
$promise->resolve( [], [] );
|
||||
return;
|
||||
}
|
||||
)->wait;
|
||||
}
|
||||
|
||||
return $promise;
|
||||
}
|
||||
|
@ -394,7 +331,8 @@ sub compute_effective_visibility {
|
|||
sub homepage {
|
||||
my ($self) = @_;
|
||||
if ( $self->is_user_authenticated ) {
|
||||
my $uid = $self->current_user->{id};
|
||||
my $user = $self->current_user;
|
||||
my $uid = $user->{id};
|
||||
my $status = $self->get_user_status;
|
||||
my @timeline = $self->in_transit->get_timeline(
|
||||
uid => $uid,
|
||||
|
@ -405,7 +343,7 @@ sub homepage {
|
|||
if ( $status->{checked_in} ) {
|
||||
my $journey_visibility
|
||||
= $self->compute_effective_visibility(
|
||||
$self->current_user->{default_visibility_str},
|
||||
$user->{default_visibility_str},
|
||||
$status->{visibility_str} );
|
||||
if ( defined $status->{arrival_countdown}
|
||||
and $status->{arrival_countdown} < ( 40 * 60 ) )
|
||||
|
@ -416,6 +354,7 @@ sub homepage {
|
|||
my ( $connections_iris, $connections_hafas ) = @_;
|
||||
$self->render(
|
||||
'landingpage',
|
||||
user => $user,
|
||||
user_status => $status,
|
||||
journey_visibility => $journey_visibility,
|
||||
connections_iris => $connections_iris,
|
||||
|
@ -427,6 +366,7 @@ sub homepage {
|
|||
sub {
|
||||
$self->render(
|
||||
'landingpage',
|
||||
user => $user,
|
||||
user_status => $status,
|
||||
journey_visibility => $journey_visibility,
|
||||
);
|
||||
|
@ -438,6 +378,7 @@ sub homepage {
|
|||
else {
|
||||
$self->render(
|
||||
'landingpage',
|
||||
user => $user,
|
||||
user_status => $status,
|
||||
journey_visibility => $journey_visibility,
|
||||
);
|
||||
|
@ -451,10 +392,12 @@ sub homepage {
|
|||
}
|
||||
$self->render(
|
||||
'landingpage',
|
||||
user => $user,
|
||||
user_status => $status,
|
||||
recent_targets => \@recent_targets,
|
||||
with_autocomplete => 1,
|
||||
with_geolocation => 1
|
||||
with_geolocation => 1,
|
||||
backend_id => $user->{backend_id},
|
||||
);
|
||||
$self->users->mark_seen( uid => $uid );
|
||||
}
|
||||
|
@ -515,6 +458,7 @@ sub status_card {
|
|||
elsif ( $status->{cancellation} ) {
|
||||
$self->render_later;
|
||||
$self->get_connecting_trains_p(
|
||||
backend_id => $status->{backend_id},
|
||||
eva => $status->{cancellation}{dep_eva},
|
||||
destination_name => $status->{cancellation}{arr_name}
|
||||
)->then(
|
||||
|
@ -563,14 +507,63 @@ sub status_card {
|
|||
sub geolocation {
|
||||
my ($self) = @_;
|
||||
|
||||
my $lon = $self->param('lon');
|
||||
my $lat = $self->param('lat');
|
||||
my $lon = $self->param('lon');
|
||||
my $lat = $self->param('lat');
|
||||
my $backend_id = $self->param('backend') // 0;
|
||||
|
||||
if ( not $lon or not $lat ) {
|
||||
if ( not $lon or not $lat or $backend_id !~ m{ ^ \d+ $ }x ) {
|
||||
$self->render( json => { error => 'Invalid lon/lat received' } );
|
||||
return;
|
||||
}
|
||||
$self->render_later;
|
||||
|
||||
my $hafas_service
|
||||
= $self->stations->get_hafas_name( backend_id => $backend_id );
|
||||
|
||||
if ($hafas_service) {
|
||||
$self->render_later;
|
||||
|
||||
Travel::Status::DE::HAFAS->new_p(
|
||||
promise => 'Mojo::Promise',
|
||||
user_agent => $self->ua,
|
||||
service => $hafas_service,
|
||||
geoSearch => {
|
||||
lat => $lat,
|
||||
lon => $lon
|
||||
}
|
||||
)->then(
|
||||
sub {
|
||||
my ($hafas) = @_;
|
||||
my @hafas = map {
|
||||
{
|
||||
name => $_->name,
|
||||
eva => $_->eva,
|
||||
distance => $_->distance_m / 1000,
|
||||
hafas => $hafas_service
|
||||
}
|
||||
} $hafas->results;
|
||||
if ( @hafas > 10 ) {
|
||||
@hafas = @hafas[ 0 .. 9 ];
|
||||
}
|
||||
$self->render(
|
||||
json => {
|
||||
candidates => [@hafas],
|
||||
}
|
||||
);
|
||||
}
|
||||
)->catch(
|
||||
sub {
|
||||
my ($err) = @_;
|
||||
$self->render(
|
||||
json => {
|
||||
candidates => [],
|
||||
warning => $err,
|
||||
}
|
||||
);
|
||||
}
|
||||
)->wait;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
my @iris = map {
|
||||
{
|
||||
|
@ -580,7 +573,6 @@ sub geolocation {
|
|||
lon => $_->[0][3],
|
||||
lat => $_->[0][4],
|
||||
distance => $_->[1],
|
||||
hafas => 0,
|
||||
}
|
||||
} Travel::Status::DE::IRIS::Stations::get_station_by_location( $lon,
|
||||
$lat, 10 );
|
||||
|
@ -588,48 +580,12 @@ sub geolocation {
|
|||
if ( @iris > 5 ) {
|
||||
@iris = @iris[ 0 .. 4 ];
|
||||
}
|
||||
$self->render(
|
||||
json => {
|
||||
candidates => [@iris],
|
||||
}
|
||||
);
|
||||
|
||||
Travel::Status::DE::HAFAS->new_p(
|
||||
promise => 'Mojo::Promise',
|
||||
user_agent => $self->ua,
|
||||
geoSearch => {
|
||||
lat => $lat,
|
||||
lon => $lon
|
||||
}
|
||||
)->then(
|
||||
sub {
|
||||
my ($hafas) = @_;
|
||||
my @hafas = map {
|
||||
{
|
||||
name => $_->name,
|
||||
eva => $_->eva,
|
||||
distance => $_->distance_m / 1000,
|
||||
hafas => 'DB'
|
||||
}
|
||||
} $hafas->results;
|
||||
if ( @hafas > 10 ) {
|
||||
@hafas = @hafas[ 0 .. 9 ];
|
||||
}
|
||||
my @results = map { $_->[0] }
|
||||
sort { $a->[1] <=> $b->[1] }
|
||||
map { [ $_, $_->{distance} ] } ( @iris, @hafas );
|
||||
$self->render(
|
||||
json => {
|
||||
candidates => [@results],
|
||||
}
|
||||
);
|
||||
}
|
||||
)->catch(
|
||||
sub {
|
||||
my ($err) = @_;
|
||||
$self->render(
|
||||
json => {
|
||||
candidates => [@iris],
|
||||
warning => $err,
|
||||
}
|
||||
);
|
||||
}
|
||||
)->wait;
|
||||
}
|
||||
|
||||
sub travel_action {
|
||||
|
@ -684,6 +640,7 @@ sub travel_action {
|
|||
$promise->then(
|
||||
sub {
|
||||
return $self->checkin_p(
|
||||
hafas => $params->{hafas},
|
||||
station => $params->{station},
|
||||
train_id => $params->{train}
|
||||
);
|
||||
|
@ -713,8 +670,8 @@ sub travel_action {
|
|||
my ( $still_checked_in, undef ) = @_;
|
||||
if ( my $destination = $params->{dest} ) {
|
||||
my $station_link = '/s/' . $destination;
|
||||
if ( $status->{train_id} =~ m{[|]} ) {
|
||||
$station_link .= '?hafas=DB';
|
||||
if ( $status->{is_hafas} ) {
|
||||
$station_link .= '?hafas=' . $status->{backend_name};
|
||||
}
|
||||
$self->render(
|
||||
json => {
|
||||
|
@ -749,8 +706,8 @@ sub travel_action {
|
|||
sub {
|
||||
my ( $still_checked_in, $error ) = @_;
|
||||
my $station_link = '/s/' . $params->{station};
|
||||
if ( $status->{train_id} =~ m{[|]} ) {
|
||||
$station_link .= '?hafas=DB';
|
||||
if ( $status->{is_hafas} ) {
|
||||
$station_link .= '?hafas=' . $status->{backend_name};
|
||||
}
|
||||
|
||||
if ($error) {
|
||||
|
@ -800,8 +757,12 @@ sub travel_action {
|
|||
else {
|
||||
my $redir = '/';
|
||||
if ( $status->{checked_in} or $status->{cancelled} ) {
|
||||
if ( $status->{train_id} =~ m{[|]} ) {
|
||||
$redir = '/s/' . $status->{dep_eva} . '?hafas=DB';
|
||||
if ( $status->{is_hafas} ) {
|
||||
$redir
|
||||
= '/s/'
|
||||
. $status->{dep_eva}
|
||||
. '?hafas='
|
||||
. $status->{backend_name};
|
||||
}
|
||||
else {
|
||||
$redir = '/s/' . $status->{dep_ds100};
|
||||
|
@ -818,6 +779,7 @@ sub travel_action {
|
|||
elsif ( $params->{action} eq 'cancelled_from' ) {
|
||||
$self->render_later;
|
||||
$self->checkin_p(
|
||||
hafas => $params->{hafas},
|
||||
station => $params->{station},
|
||||
train_id => $params->{train}
|
||||
)->then(
|
||||
|
@ -920,7 +882,8 @@ sub station {
|
|||
my $train = $self->param('train');
|
||||
my $trip_id = $self->param('trip_id');
|
||||
my $timestamp = $self->param('timestamp');
|
||||
my $uid = $self->current_user->{id};
|
||||
my $user = $self->current_user;
|
||||
my $uid = $user->{id};
|
||||
|
||||
my @timeline = $self->in_transit->get_timeline(
|
||||
uid => $uid,
|
||||
|
@ -928,7 +891,6 @@ sub station {
|
|||
);
|
||||
my %checkin_by_train;
|
||||
for my $checkin (@timeline) {
|
||||
say $checkin->{train_id};
|
||||
push( @{ $checkin_by_train{ $checkin->{train_id} } }, $checkin );
|
||||
}
|
||||
$self->stash( checkin_by_train => \%checkin_by_train );
|
||||
|
@ -945,10 +907,12 @@ sub station {
|
|||
$timestamp = DateTime->now( time_zone => 'Europe/Berlin' );
|
||||
}
|
||||
|
||||
my $use_hafas = $self->param('hafas');
|
||||
my $hafas_service = $self->param('hafas')
|
||||
// ( $user->{backend_hafas} ? $user->{backend_name} : undef );
|
||||
my $promise;
|
||||
if ($use_hafas) {
|
||||
if ($hafas_service) {
|
||||
$promise = $self->hafas->get_departures_p(
|
||||
service => $hafas_service,
|
||||
eva => $station,
|
||||
timestamp => $timestamp,
|
||||
lookbehind => 30,
|
||||
|
@ -966,27 +930,21 @@ sub station {
|
|||
$promise->then(
|
||||
sub {
|
||||
my ($status) = @_;
|
||||
my $api_link;
|
||||
my @results;
|
||||
|
||||
my $now = $self->now->epoch;
|
||||
my $now_within_range
|
||||
= abs( $timestamp->epoch - $now ) < 1800 ? 1 : 0;
|
||||
|
||||
if ($use_hafas) {
|
||||
|
||||
my $iris_eva = List::Util::min grep { $_ >= 1000000 }
|
||||
@{ $status->station->{evas} // [] };
|
||||
if ($iris_eva) {
|
||||
$api_link = '/s/' . $iris_eva;
|
||||
}
|
||||
if ($hafas_service) {
|
||||
|
||||
@results = map { $_->[0] }
|
||||
sort { $b->[1] <=> $a->[1] }
|
||||
map { [ $_, $_->datetime->epoch ] } $status->results;
|
||||
$self->stations->add_meta(
|
||||
eva => $status->station->{eva},
|
||||
meta => $status->station->{evas} // []
|
||||
eva => $status->station->{eva},
|
||||
meta => $status->station->{evas} // [],
|
||||
hafas => $hafas_service,
|
||||
);
|
||||
$status = {
|
||||
station_eva => $status->station->{eva},
|
||||
|
@ -999,8 +957,6 @@ sub station {
|
|||
}
|
||||
else {
|
||||
|
||||
$api_link = '/s/' . $status->{station_eva} . '?hafas=DB';
|
||||
|
||||
# You can't check into a train which terminates here
|
||||
@results = grep { $_->departure } @{ $status->{results} };
|
||||
|
||||
|
@ -1029,10 +985,10 @@ sub station {
|
|||
}
|
||||
|
||||
my $connections_p;
|
||||
if ( $trip_id and $use_hafas ) {
|
||||
if ( $trip_id and $hafas_service ) {
|
||||
@results = grep { $_->id eq $trip_id } @results;
|
||||
}
|
||||
elsif ( $train and not $use_hafas ) {
|
||||
elsif ( $train and not $hafas_service ) {
|
||||
@results
|
||||
= grep { $_->type . ' ' . $_->train_no eq $train } @results;
|
||||
}
|
||||
|
@ -1044,12 +1000,15 @@ sub station {
|
|||
$connections_p = $self->get_connecting_trains_p(
|
||||
eva => $user_status->{cancellation}{dep_eva},
|
||||
destination_name =>
|
||||
$user_status->{cancellation}{arr_name}
|
||||
$user_status->{cancellation}{arr_name},
|
||||
hafas => $hafas_service,
|
||||
);
|
||||
}
|
||||
else {
|
||||
$connections_p = $self->get_connecting_trains_p(
|
||||
eva => $status->{station_eva} );
|
||||
eva => $status->{station_eva},
|
||||
hafas => $hafas_service
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1059,18 +1018,18 @@ sub station {
|
|||
my ( $connections_iris, $connections_hafas ) = @_;
|
||||
$self->render(
|
||||
'departures',
|
||||
user => $user,
|
||||
hafas => $hafas_service,
|
||||
eva => $status->{station_eva},
|
||||
datetime => $timestamp,
|
||||
now_in_range => $now_within_range,
|
||||
results => \@results,
|
||||
hafas => $use_hafas,
|
||||
station => $status->{station_name},
|
||||
related_stations => $status->{related_stations},
|
||||
user_status => $user_status,
|
||||
can_check_out => $can_check_out,
|
||||
connections_iris => $connections_iris,
|
||||
connections_hafas => $connections_hafas,
|
||||
api_link => $api_link,
|
||||
title => "travelynx: $status->{station_name}",
|
||||
);
|
||||
}
|
||||
|
@ -1078,16 +1037,16 @@ sub station {
|
|||
sub {
|
||||
$self->render(
|
||||
'departures',
|
||||
user => $user,
|
||||
hafas => $hafas_service,
|
||||
eva => $status->{station_eva},
|
||||
datetime => $timestamp,
|
||||
now_in_range => $now_within_range,
|
||||
results => \@results,
|
||||
hafas => $use_hafas,
|
||||
station => $status->{station_name},
|
||||
related_stations => $status->{related_stations},
|
||||
user_status => $user_status,
|
||||
can_check_out => $can_check_out,
|
||||
api_link => $api_link,
|
||||
title => "travelynx: $status->{station_name}",
|
||||
);
|
||||
}
|
||||
|
@ -1096,16 +1055,16 @@ sub station {
|
|||
else {
|
||||
$self->render(
|
||||
'departures',
|
||||
user => $user,
|
||||
hafas => $hafas_service,
|
||||
eva => $status->{station_eva},
|
||||
datetime => $timestamp,
|
||||
now_in_range => $now_within_range,
|
||||
results => \@results,
|
||||
hafas => $use_hafas,
|
||||
station => $status->{station_name},
|
||||
related_stations => $status->{related_stations},
|
||||
user_status => $user_status,
|
||||
can_check_out => $can_check_out,
|
||||
api_link => $api_link,
|
||||
title => "travelynx: $status->{station_name}",
|
||||
);
|
||||
}
|
||||
|
@ -1120,15 +1079,22 @@ sub station {
|
|||
status => 300,
|
||||
);
|
||||
}
|
||||
elsif ( $use_hafas and $status and $status->errcode eq 'LOCATION' )
|
||||
elsif ( $hafas_service
|
||||
and $status
|
||||
and $status->errcode eq 'LOCATION' )
|
||||
{
|
||||
$self->hafas->search_location_p( query => $station )->then(
|
||||
$self->hafas->search_location_p(
|
||||
service => $hafas_service,
|
||||
query => $station
|
||||
)->then(
|
||||
sub {
|
||||
my ($hafas2) = @_;
|
||||
my @suggestions = $hafas2->results;
|
||||
if ( @suggestions == 1 ) {
|
||||
$self->redirect_to(
|
||||
'/s/' . $suggestions[0]->eva . '?hafas=DB' );
|
||||
$self->redirect_to( '/s/'
|
||||
. $suggestions[0]->eva
|
||||
. '?hafas='
|
||||
. $hafas_service );
|
||||
}
|
||||
else {
|
||||
$self->render(
|
||||
|
@ -1169,17 +1135,7 @@ sub redirect_to_station {
|
|||
my ($self) = @_;
|
||||
my $station = $self->param('station');
|
||||
|
||||
if ( my $s = $self->app->stations->search($station) ) {
|
||||
if ( $s->{source} == 1 ) {
|
||||
$self->redirect_to("/s/${station}?hafas=DB");
|
||||
}
|
||||
else {
|
||||
$self->redirect_to("/s/${station}");
|
||||
}
|
||||
}
|
||||
else {
|
||||
$self->redirect_to("/s/${station}?hafas=DB");
|
||||
}
|
||||
$self->redirect_to("/s/${station}");
|
||||
}
|
||||
|
||||
sub cancelled {
|
||||
|
|
|
@ -97,6 +97,7 @@ sub get_departures_p {
|
|||
: DateTime->now( time_zone => 'Europe/Berlin' )
|
||||
)->subtract( minutes => $opt{lookbehind} );
|
||||
return Travel::Status::DE::HAFAS->new_p(
|
||||
service => $opt{service},
|
||||
station => $opt{eva},
|
||||
datetime => $when,
|
||||
lookahead => $opt{lookahead} + $opt{lookbehind},
|
||||
|
@ -111,6 +112,7 @@ sub search_location_p {
|
|||
my ( $self, %opt ) = @_;
|
||||
|
||||
return Travel::Status::DE::HAFAS->new_p(
|
||||
service => $opt{service},
|
||||
locationSearch => $opt{query},
|
||||
cache => $self->{realtime_cache},
|
||||
promise => 'Mojo::Promise',
|
||||
|
@ -128,6 +130,7 @@ sub get_tripid_p {
|
|||
$train_desc =~ s{^- }{};
|
||||
|
||||
Travel::Status::DE::HAFAS->new_p(
|
||||
service => $opt{service},
|
||||
journeyMatch => $train_desc,
|
||||
datetime => $train->start,
|
||||
cache => $self->{realtime_cache},
|
||||
|
@ -175,6 +178,7 @@ sub get_journey_p {
|
|||
my $now = DateTime->now( time_zone => 'Europe/Berlin' );
|
||||
|
||||
Travel::Status::DE::HAFAS->new_p(
|
||||
service => $opt{service},
|
||||
journey => {
|
||||
id => $opt{trip_id},
|
||||
},
|
||||
|
@ -212,6 +216,7 @@ sub get_route_p {
|
|||
my $now = DateTime->now( time_zone => 'Europe/Berlin' );
|
||||
|
||||
Travel::Status::DE::HAFAS->new_p(
|
||||
service => $opt{service},
|
||||
journey => {
|
||||
id => $opt{trip_id},
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ use warnings;
|
|||
|
||||
use 5.020;
|
||||
|
||||
use Encode qw(encode);
|
||||
use Encode qw(encode);
|
||||
use Email::Sender::Simple qw(try_to_sendmail);
|
||||
use MIME::Entity;
|
||||
|
||||
|
|
|
@ -116,6 +116,7 @@ sub get_status_p {
|
|||
|
||||
my $category = $status->{train}{category};
|
||||
my $linename = $status->{train}{lineName};
|
||||
my $train_no = $status->{train}{journeyNumber};
|
||||
my $trip_id = $status->{train}{hafasId};
|
||||
my ( $train_type, $train_line ) = split( qr{ }, $linename );
|
||||
$promise->resolve(
|
||||
|
@ -133,6 +134,7 @@ sub get_status_p {
|
|||
arr_ds100 => $arr_ds100,
|
||||
arr_name => $arr_name,
|
||||
trip_id => $trip_id,
|
||||
train_no => $train_no,
|
||||
train_type => $train_type,
|
||||
line => $linename,
|
||||
line_no => $train_line,
|
||||
|
|
|
@ -93,6 +93,7 @@ sub add {
|
|||
|
||||
my $uid = $opt{uid};
|
||||
my $db = $opt{db} // $self->{pg}->db;
|
||||
my $backend_id = $opt{backend_id};
|
||||
my $train = $opt{train};
|
||||
my $journey = $opt{journey};
|
||||
my $stop = $opt{stop};
|
||||
|
@ -103,8 +104,6 @@ sub add {
|
|||
my $json = JSON->new;
|
||||
|
||||
if ($train) {
|
||||
my $backend_id
|
||||
= $db->select( 'backends', ['id'], { iris => 1 } )->hash->{id};
|
||||
$db->insert(
|
||||
'in_transit',
|
||||
{
|
||||
|
@ -136,14 +135,6 @@ sub add {
|
|||
);
|
||||
}
|
||||
elsif ( $journey and $stop ) {
|
||||
my $backend_id = $db->select(
|
||||
'backends',
|
||||
['id'],
|
||||
{
|
||||
hafas => 1,
|
||||
name => 'DB'
|
||||
}
|
||||
)->hash->{id};
|
||||
my @route;
|
||||
my $product = $journey->product_at( $stop->loc->eva )
|
||||
// $journey->product;
|
||||
|
@ -440,17 +431,20 @@ sub get_all_active {
|
|||
->hashes->each;
|
||||
}
|
||||
|
||||
sub get_checkout_station_id {
|
||||
sub get_checkout_ids {
|
||||
my ( $self, %opt ) = @_;
|
||||
|
||||
my $uid = $opt{uid};
|
||||
my $db = $opt{db} // $self->{pg}->db;
|
||||
|
||||
my $status = $db->select( 'in_transit', ['checkout_station_id'],
|
||||
{ user_id => $uid } )->hash;
|
||||
my $status = $db->select(
|
||||
'in_transit',
|
||||
[ 'checkout_station_id', 'backend_id' ],
|
||||
{ user_id => $uid }
|
||||
)->hash;
|
||||
|
||||
if ($status) {
|
||||
return $status->{checkout_station_id};
|
||||
return $status->{checkout_station_id}, $status->{backend_id};
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -819,7 +813,6 @@ sub update_arrival_hafas {
|
|||
my $stop = $opt{stop};
|
||||
my $json = JSON->new;
|
||||
|
||||
# TODO use old rt data if available
|
||||
my @route;
|
||||
for my $j_stop ( $journey->route ) {
|
||||
push(
|
||||
|
|
|
@ -545,7 +545,7 @@ sub get {
|
|||
|
||||
my @select
|
||||
= (
|
||||
qw(journey_id is_iris is_hafas backend_name train_type train_line train_no checkin_ts sched_dep_ts real_dep_ts dep_eva dep_ds100 dep_name dep_lat dep_lon checkout_ts sched_arr_ts real_arr_ts arr_eva arr_ds100 arr_name arr_lat arr_lon cancelled edited route messages user_data visibility effective_visibility)
|
||||
qw(journey_id is_iris is_hafas backend_name backend_id train_type train_line train_no checkin_ts sched_dep_ts real_dep_ts dep_eva dep_ds100 dep_name dep_lat dep_lon checkout_ts sched_arr_ts real_arr_ts arr_eva arr_ds100 arr_name arr_lat arr_lon cancelled edited route messages user_data visibility effective_visibility)
|
||||
);
|
||||
my %where = (
|
||||
user_id => $uid,
|
||||
|
@ -606,6 +606,7 @@ sub get {
|
|||
is_iris => $entry->{is_iris},
|
||||
is_hafas => $entry->{is_hafas},
|
||||
backend_name => $entry->{backend_name},
|
||||
backend_id => $entry->{backend_id},
|
||||
type => $entry->{train_type},
|
||||
line => $entry->{train_line},
|
||||
no => $entry->{train_no},
|
||||
|
@ -665,7 +666,10 @@ sub get {
|
|||
my $rename = $self->{renamed_station};
|
||||
for my $stop ( @{ $ref->{route} } ) {
|
||||
if ( $stop->[0] =~ m{^Betriebsstelle nicht bekannt (\d+)$} ) {
|
||||
if ( my $s = $self->{stations}->get_by_eva($1) ) {
|
||||
if ( my $s
|
||||
= $self->{stations}
|
||||
->get_by_eva( $1, backend_id => $ref->{backend_id} ) )
|
||||
{
|
||||
$stop->[0] = $s->{name};
|
||||
}
|
||||
}
|
||||
|
@ -800,14 +804,14 @@ sub get_oldest_ts {
|
|||
return undef;
|
||||
}
|
||||
|
||||
sub get_latest_checkout_station_id {
|
||||
sub get_latest_checkout_ids {
|
||||
my ( $self, %opt ) = @_;
|
||||
my $uid = $opt{uid};
|
||||
my $db = $opt{db} // $self->{pg}->db;
|
||||
|
||||
my $res_h = $db->select(
|
||||
'journeys',
|
||||
['checkout_station_id'],
|
||||
[ 'checkout_station_id', 'backend_id', ],
|
||||
{
|
||||
user_id => $uid,
|
||||
cancelled => 0
|
||||
|
@ -822,7 +826,7 @@ sub get_latest_checkout_station_id {
|
|||
return;
|
||||
}
|
||||
|
||||
return $res_h->{checkout_station_id};
|
||||
return $res_h->{checkout_station_id}, $res_h->{backend_id};
|
||||
}
|
||||
|
||||
sub get_latest_checkout_stations {
|
||||
|
@ -833,7 +837,10 @@ sub get_latest_checkout_stations {
|
|||
|
||||
my $res = $db->select(
|
||||
'journeys_str',
|
||||
[ 'arr_name', 'arr_eva', 'train_id' ],
|
||||
[
|
||||
'arr_name', 'arr_eva', 'train_id', 'backend_id',
|
||||
'backend_name', 'is_hafas'
|
||||
],
|
||||
{
|
||||
user_id => $uid,
|
||||
cancelled => 0
|
||||
|
@ -854,9 +861,10 @@ sub get_latest_checkout_stations {
|
|||
push(
|
||||
@ret,
|
||||
{
|
||||
name => $row->{arr_name},
|
||||
eva => $row->{arr_eva},
|
||||
hafas => ( $row->{train_id} =~ m{[|]} ? 'DB' : 0 ),
|
||||
name => $row->{arr_name},
|
||||
eva => $row->{arr_eva},
|
||||
hafas => $row->{is_hafas} ? $row->{backend_name} : 0,
|
||||
backend_id => $row->{backend_id},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -1726,28 +1734,29 @@ sub get_stats {
|
|||
return $stats;
|
||||
}
|
||||
|
||||
sub get_latest_dest_id {
|
||||
sub get_latest_dest_ids {
|
||||
my ( $self, %opt ) = @_;
|
||||
|
||||
my $uid = $opt{uid};
|
||||
my $db = $opt{db} // $self->{pg}->db;
|
||||
|
||||
if (
|
||||
my $id = $self->{in_transit}->get_checkout_station_id(
|
||||
my ( $id, $backend_id ) = $self->{in_transit}->get_checkout_ids(
|
||||
uid => $uid,
|
||||
db => $db
|
||||
)
|
||||
)
|
||||
{
|
||||
return $id;
|
||||
return ( $id, $backend_id );
|
||||
}
|
||||
|
||||
return $self->get_latest_checkout_station_id(
|
||||
return $self->get_latest_checkout_ids(
|
||||
uid => $uid,
|
||||
db => $db
|
||||
);
|
||||
}
|
||||
|
||||
# Returns a listref of {eva, name} hashrefs for the specified backend.
|
||||
sub get_connection_targets {
|
||||
my ( $self, %opt ) = @_;
|
||||
|
||||
|
@ -1756,21 +1765,29 @@ sub get_connection_targets {
|
|||
// DateTime->now( time_zone => 'Europe/Berlin' )->subtract( months => 4 );
|
||||
my $db = $opt{db} //= $self->{pg}->db;
|
||||
my $min_count = $opt{min_count} // 3;
|
||||
my $dest_id = $opt{eva};
|
||||
|
||||
if ( $opt{destination_name} ) {
|
||||
return (
|
||||
[],
|
||||
[ { eva => $opt{eva}, name => $opt{destination_name} } ]
|
||||
);
|
||||
return [ { eva => $opt{eva}, name => $opt{destination_name} } ];
|
||||
}
|
||||
|
||||
my $dest_id = $opt{eva} // $self->get_latest_dest_id(%opt);
|
||||
my $backend_id = $opt{backend_id};
|
||||
|
||||
if ( not $dest_id ) {
|
||||
return ( [], [] );
|
||||
( $dest_id, $backend_id ) = $self->get_latest_dest_ids(%opt);
|
||||
}
|
||||
|
||||
my $dest_ids = [ $dest_id, $self->{stations}->get_meta( eva => $dest_id ) ];
|
||||
if ( not $dest_id ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
my $dest_ids = [
|
||||
$dest_id,
|
||||
$self->{stations}->get_meta(
|
||||
eva => $dest_id,
|
||||
backend_id => $backend_id,
|
||||
)
|
||||
];
|
||||
|
||||
my $res = $db->select(
|
||||
'journeys',
|
||||
|
@ -1778,7 +1795,8 @@ sub get_connection_targets {
|
|||
{
|
||||
user_id => $uid,
|
||||
checkin_station_id => $dest_ids,
|
||||
real_departure => { '>', $threshold }
|
||||
real_departure => { '>', $threshold },
|
||||
backend_id => $opt{backend_id},
|
||||
},
|
||||
{
|
||||
group_by => ['checkout_station_id'],
|
||||
|
@ -1788,8 +1806,11 @@ sub get_connection_targets {
|
|||
my @destinations
|
||||
= $res->hashes->grep( sub { shift->{count} >= $min_count } )
|
||||
->map( sub { shift->{dest} } )->each;
|
||||
@destinations = $self->{stations}->get_by_evas(@destinations);
|
||||
return ( $dest_ids, \@destinations );
|
||||
@destinations = $self->{stations}->get_by_evas(
|
||||
backend_id => $opt{backend_id},
|
||||
evas => [@destinations]
|
||||
);
|
||||
return @destinations;
|
||||
}
|
||||
|
||||
sub update_visibility {
|
||||
|
|
|
@ -14,38 +14,125 @@ sub new {
|
|||
return bless( \%opt, $class );
|
||||
}
|
||||
|
||||
sub get_backend_id {
|
||||
my ( $self, %opt ) = @_;
|
||||
|
||||
if ( $opt{iris} ) {
|
||||
|
||||
# special case
|
||||
return 0;
|
||||
}
|
||||
if ( $opt{hafas} and $self->{backend_id}{hafas}{ $opt{hafas} } ) {
|
||||
return $self->{backend_id}{hafas}{ $opt{hafas} };
|
||||
}
|
||||
|
||||
my $db = $opt{db} // $self->{pg}->db;
|
||||
my $backend_id = 0;
|
||||
|
||||
if ( $opt{hafas} ) {
|
||||
$backend_id = $db->select(
|
||||
'backends',
|
||||
['id'],
|
||||
{
|
||||
hafas => 1,
|
||||
name => $opt{hafas}
|
||||
}
|
||||
)->hash->{id};
|
||||
$self->{backend_id}{hafas}{ $opt{hafas} } = $backend_id;
|
||||
}
|
||||
|
||||
return $backend_id;
|
||||
}
|
||||
|
||||
sub get_hafas_name {
|
||||
my ( $self, %opt ) = @_;
|
||||
|
||||
if ( exists $self->{hafas_name}{ $opt{backend_id} } ) {
|
||||
return $self->{hafas_name}{ $opt{backend_id} };
|
||||
}
|
||||
|
||||
my $db = $opt{db} // $self->{pg}->db;
|
||||
my $hafas_name;
|
||||
my $ret = $db->select(
|
||||
'backends',
|
||||
['name'],
|
||||
{
|
||||
hafas => 1,
|
||||
id => $opt{backend_id},
|
||||
}
|
||||
)->hash;
|
||||
|
||||
if ($ret) {
|
||||
$hafas_name = $ret->{name};
|
||||
}
|
||||
|
||||
$self->{hafas_name}{ $opt{backend_id} } = $hafas_name;
|
||||
|
||||
return $hafas_name;
|
||||
}
|
||||
|
||||
sub get_backends {
|
||||
my ( $self, %opt ) = @_;
|
||||
|
||||
$opt{db} //= $self->{pg}->db;
|
||||
|
||||
my $res = $opt{db}->select( 'backends', [ 'id', 'name', 'iris', 'hafas' ] );
|
||||
my @ret;
|
||||
|
||||
while ( my $row = $res->hash ) {
|
||||
push(
|
||||
@ret,
|
||||
{
|
||||
id => $row->{id},
|
||||
name => $row->{name},
|
||||
iris => $row->{iris},
|
||||
hafas => $row->{hafas},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return @ret;
|
||||
}
|
||||
|
||||
sub add_or_update {
|
||||
my ( $self, %opt ) = @_;
|
||||
my $stop = $opt{stop};
|
||||
my $loc = $stop->loc;
|
||||
my $source = 1;
|
||||
my $db = $opt{db} // $self->{pg}->db;
|
||||
my $stop = $opt{stop};
|
||||
my $loc = $stop->loc;
|
||||
$opt{db} //= $self->{pg}->db;
|
||||
|
||||
if ( my $s = $self->get_by_eva( $loc->eva, db => $db ) ) {
|
||||
if ( $source == 1 and $s->{source} == 0 and not $s->{archived} ) {
|
||||
return;
|
||||
}
|
||||
$db->update(
|
||||
$opt{backend_id} //= $self->get_backend_id(%opt);
|
||||
|
||||
if (
|
||||
my $s = $self->get_by_eva(
|
||||
$loc->eva,
|
||||
db => $opt{db},
|
||||
backend_id => $opt{backend_id}
|
||||
)
|
||||
)
|
||||
{
|
||||
$opt{db}->update(
|
||||
'stations',
|
||||
{
|
||||
name => $loc->name,
|
||||
lat => $loc->lat,
|
||||
lon => $loc->lon,
|
||||
source => $source,
|
||||
archived => 0
|
||||
},
|
||||
{ eva => $loc->eva }
|
||||
{
|
||||
eva => $loc->eva,
|
||||
source => $opt{backend_id}
|
||||
}
|
||||
);
|
||||
return;
|
||||
}
|
||||
$db->insert(
|
||||
$opt{db}->insert(
|
||||
'stations',
|
||||
{
|
||||
eva => $loc->eva,
|
||||
name => $loc->name,
|
||||
lat => $loc->lat,
|
||||
lon => $loc->lon,
|
||||
source => $source,
|
||||
source => $opt{backend_id},
|
||||
archived => 0
|
||||
}
|
||||
);
|
||||
|
@ -53,17 +140,20 @@ sub add_or_update {
|
|||
|
||||
sub add_meta {
|
||||
my ( $self, %opt ) = @_;
|
||||
my $db = $opt{db} // $self->{pg}->db;
|
||||
my $eva = $opt{eva};
|
||||
my @meta = @{ $opt{meta} };
|
||||
|
||||
$opt{db} //= $self->{pg}->db;
|
||||
$opt{backend_id} //= $self->get_backend_id(%opt);
|
||||
|
||||
for my $meta (@meta) {
|
||||
if ( $meta != $eva ) {
|
||||
$db->insert(
|
||||
$opt{db}->insert(
|
||||
'related_stations',
|
||||
{
|
||||
eva => $eva,
|
||||
meta => $meta
|
||||
eva => $eva,
|
||||
meta => $meta,
|
||||
backend_id => $opt{backend_id},
|
||||
},
|
||||
{ on_conflict => undef }
|
||||
);
|
||||
|
@ -82,7 +172,16 @@ sub get_meta {
|
|||
my $db = $opt{db} // $self->{pg}->db;
|
||||
my $eva = $opt{eva};
|
||||
|
||||
my $res = $db->select( 'related_stations', ['meta'], { eva => $eva } );
|
||||
$opt{backend_id} //= $self->get_backend_id( %opt, db => $db );
|
||||
|
||||
my $res = $db->select(
|
||||
'related_stations',
|
||||
['meta'],
|
||||
{
|
||||
eva => $eva,
|
||||
backend_id => $opt{backend_id}
|
||||
}
|
||||
);
|
||||
my @ret;
|
||||
|
||||
while ( my $row = $res->hash ) {
|
||||
|
@ -93,9 +192,12 @@ sub get_meta {
|
|||
}
|
||||
|
||||
sub get_for_autocomplete {
|
||||
my ($self) = @_;
|
||||
my ( $self, %opt ) = @_;
|
||||
|
||||
my $res = $self->{pg}->db->select( 'stations', ['name'] );
|
||||
$opt{backend_id} //= $self->get_backend_id(%opt);
|
||||
|
||||
my $res = $self->{pg}
|
||||
->db->select( 'stations', ['name'], { source => $opt{backend_id} } );
|
||||
my %ret;
|
||||
|
||||
while ( my $row = $res->hash ) {
|
||||
|
@ -113,18 +215,34 @@ sub get_by_eva {
|
|||
return;
|
||||
}
|
||||
|
||||
my $db = $opt{db} // $self->{pg}->db;
|
||||
$opt{db} //= $self->{pg}->db;
|
||||
$opt{backend_id} //= $self->get_backend_id(%opt);
|
||||
|
||||
return $db->select( 'stations', '*', { eva => $eva } )->hash;
|
||||
return $opt{db}->select(
|
||||
'stations',
|
||||
'*',
|
||||
{
|
||||
eva => $eva,
|
||||
source => $opt{backend_id}
|
||||
}
|
||||
)->hash;
|
||||
}
|
||||
|
||||
# Fast
|
||||
sub get_by_evas {
|
||||
my ( $self, @evas ) = @_;
|
||||
my ( $self, %opt ) = @_;
|
||||
|
||||
my @ret
|
||||
= $self->{pg}->db->select( 'stations', '*', { eva => { '=', \@evas } } )
|
||||
->hashes->each;
|
||||
$opt{db} //= $self->{pg}->db;
|
||||
$opt{backend_id} //= $self->get_backend_id(%opt);
|
||||
|
||||
my @ret = $self->{pg}->db->select(
|
||||
'stations',
|
||||
'*',
|
||||
{
|
||||
eva => { '=', $opt{evas} },
|
||||
source => $opt{backend_id}
|
||||
}
|
||||
)->hashes->each;
|
||||
return @ret;
|
||||
}
|
||||
|
||||
|
@ -132,10 +250,18 @@ sub get_by_evas {
|
|||
sub get_by_name {
|
||||
my ( $self, $name, %opt ) = @_;
|
||||
|
||||
my $db = $opt{db} // $self->{pg}->db;
|
||||
$opt{db} //= $self->{pg}->db;
|
||||
$opt{backend_id} //= $self->get_backend_id(%opt);
|
||||
|
||||
return $db->select( 'stations', '*', { name => $name }, { limit => 1 } )
|
||||
->hash;
|
||||
return $opt{db}->select(
|
||||
'stations',
|
||||
'*',
|
||||
{
|
||||
name => $name,
|
||||
source => $opt{backend_id}
|
||||
},
|
||||
{ limit => 1 }
|
||||
)->hash;
|
||||
}
|
||||
|
||||
# Slow
|
||||
|
@ -152,16 +278,27 @@ sub get_by_names {
|
|||
sub get_by_ds100 {
|
||||
my ( $self, $ds100, %opt ) = @_;
|
||||
|
||||
my $db = $opt{db} // $self->{pg}->db;
|
||||
$opt{db} //= $self->{pg}->db;
|
||||
$opt{backend_id} //= $self->get_backend_id(%opt);
|
||||
|
||||
return $db->select( 'stations', '*', { ds100 => $ds100 }, { limit => 1 } )
|
||||
->hash;
|
||||
return $opt{db}->select(
|
||||
'stations',
|
||||
'*',
|
||||
{
|
||||
ds100 => $ds100,
|
||||
source => $opt{backend_id}
|
||||
},
|
||||
{ limit => 1 }
|
||||
)->hash;
|
||||
}
|
||||
|
||||
# Can be slow
|
||||
sub search {
|
||||
my ( $self, $identifier, %opt ) = @_;
|
||||
|
||||
$opt{db} //= $self->{pg}->db;
|
||||
$opt{backend_id} //= $self->get_backend_id(%opt);
|
||||
|
||||
if ( $identifier =~ m{ ^ \d+ $ }x ) {
|
||||
return $self->get_by_eva( $identifier, %opt )
|
||||
// $self->get_by_ds100( $identifier, %opt )
|
||||
|
|
|
@ -224,6 +224,7 @@ sub get_pushable_accounts {
|
|||
join in_transit_str as i on t.user_id = i.user_id
|
||||
where t.push_sync = True
|
||||
and i.arr_eva is not null
|
||||
and i.backend_id <= 1
|
||||
and i.cancelled = False
|
||||
}
|
||||
);
|
||||
|
|
|
@ -205,6 +205,13 @@ sub get_privacy_by {
|
|||
return;
|
||||
}
|
||||
|
||||
sub set_backend {
|
||||
my ( $self, %opt ) = @_;
|
||||
$opt{db} //= $self->{pg}->db;
|
||||
|
||||
$opt{db}->update('users', {backend_id => $opt{backend_id}}, {id => $opt{uid}});
|
||||
}
|
||||
|
||||
sub set_privacy {
|
||||
my ( $self, %opt ) = @_;
|
||||
my $db = $opt{db} // $self->{pg}->db;
|
||||
|
@ -401,12 +408,13 @@ sub get {
|
|||
my $uid = $opt{uid};
|
||||
|
||||
my $user = $db->select(
|
||||
'users',
|
||||
'users_with_backend',
|
||||
'id, name, status, public_level, email, '
|
||||
. 'accept_follows, notifications, '
|
||||
. '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',
|
||||
. 'extract(epoch from deletion_requested) as deletion_requested_ts, '
|
||||
. 'backend_id, backend_name, hafas',
|
||||
{ id => $uid }
|
||||
)->hash;
|
||||
if ($user) {
|
||||
|
@ -443,6 +451,9 @@ sub get {
|
|||
time_zone => 'Europe/Berlin'
|
||||
)
|
||||
: undef,
|
||||
backend_id => $user->{backend_id},
|
||||
backend_name => $user->{backend_name},
|
||||
backend_hafas => $user->{hafas},
|
||||
};
|
||||
}
|
||||
return undef;
|
||||
|
|
|
@ -62,7 +62,8 @@ $(document).ready(function() {
|
|||
};
|
||||
|
||||
const processLocation = function(loc) {
|
||||
$.post('/geolocation', {lon: loc.coords.longitude, lat: loc.coords.latitude}, processResult);
|
||||
const backend = $('div.geolocation > button').data('backend');
|
||||
$.post('/geolocation', {lon: loc.coords.longitude, lat: loc.coords.latitude, backend: backend}, processResult);
|
||||
};
|
||||
|
||||
const processError = function(error) {
|
||||
|
|
|
@ -191,6 +191,7 @@ function tvly_reg_handlers() {
|
|||
var link = $(this);
|
||||
var req = {
|
||||
action: 'checkin',
|
||||
hafas: link.data('hafas'),
|
||||
station: link.data('station'),
|
||||
train: link.data('train'),
|
||||
dest: link.data('dest'),
|
||||
|
@ -202,6 +203,7 @@ function tvly_reg_handlers() {
|
|||
var link = $(this);
|
||||
var req = {
|
||||
action: 'checkout',
|
||||
hafas: link.data('hafas'),
|
||||
station: link.data('station'),
|
||||
force: link.data('force'),
|
||||
};
|
||||
|
@ -232,6 +234,7 @@ function tvly_reg_handlers() {
|
|||
var link = $(this);
|
||||
var req = {
|
||||
action: 'cancelled_from',
|
||||
hafas: link.data('hafas'),
|
||||
station: link.data('station'),
|
||||
ts: link.data('ts'),
|
||||
train: link.data('train'),
|
||||
|
@ -242,6 +245,7 @@ function tvly_reg_handlers() {
|
|||
var link = $(this);
|
||||
var req = {
|
||||
action: 'cancelled_to',
|
||||
hafas: link.data('hafas'),
|
||||
station: link.data('station'),
|
||||
force: true,
|
||||
};
|
||||
|
|
|
@ -341,6 +341,7 @@ $t->app->in_transit->add(
|
|||
departure_eva => 8000001,
|
||||
train => $train_dep,
|
||||
route => [],
|
||||
backend_id => $t->app->stations->get_backend_id( iris => 1 ),
|
||||
);
|
||||
$t->app->in_transit->set_arrival_eva(
|
||||
uid => $uid1,
|
||||
|
|
|
@ -303,6 +303,7 @@ $t->app->in_transit->add(
|
|||
departure_eva => 8000001,
|
||||
train => $train_dep,
|
||||
route => [],
|
||||
backend_id => $t->app->stations->get_backend_id( iris => 1 ),
|
||||
);
|
||||
$t->app->in_transit->set_arrival_eva(
|
||||
uid => $uid1,
|
||||
|
|
|
@ -266,6 +266,7 @@ $t->app->in_transit->add(
|
|||
departure_eva => 8000001,
|
||||
train => $train_dep,
|
||||
route => [],
|
||||
backend_id => $t->app->stations->get_backend_id( iris => 1 ),
|
||||
);
|
||||
$t->app->in_transit->set_arrival_eva(
|
||||
uid => $uid1,
|
||||
|
|
|
@ -343,9 +343,14 @@
|
|||
% else {
|
||||
% $url = $url . $journey->{train_type} . ' ' . $journey->{train_no} . '/' . $journey->{sched_departure}->epoch . '000?station=' . $journey->{dep_eva};
|
||||
% }
|
||||
<a style="margin-right: 0;" href="<%= $url %>"><i class="material-icons left" aria-hidden="true">timeline</i> Zuglauf</a>
|
||||
% if ($journey->{backend_id} <= 1) {
|
||||
<a style="margin-right: 0;" href="<%= $url %>"><i class="material-icons left" aria-hidden="true">timeline</i> Zuglauf</a>
|
||||
% }
|
||||
% else {
|
||||
|
||||
% }
|
||||
% if ($journey->{extra_data}{trip_id}) {
|
||||
<a class="right" style="margin-right: 0;" href="https://dbf.finalrewind.org/map/<%= $journey->{extra_data}{trip_id} =~ s{#}{%23}gr %>/<%= $journey->{train_line} || 0 %>?from=<%= $journey->{dep_name} %>&to=<%= $journey->{arr_name} %>&dark=<%= (session('theme') and session('theme') eq 'dark') ? 1 : 0 %>"><i class="material-icons left" aria-hidden="true">map</i> Karte</a>
|
||||
<a class="right" style="margin-right: 0;" href="https://dbf.finalrewind.org/map/<%= $journey->{extra_data}{trip_id} =~ s{#}{%23}gr %>/<%= $journey->{train_line} || 0 %>?hafas=<%= $journey->{backend_name} // 'DB' %>&from=<%= $journey->{dep_name} %>&to=<%= $journey->{arr_name} %>&dark=<%= (session('theme') and session('theme') eq 'dark') ? 1 : 0 %>"><i class="material-icons left" aria-hidden="true">map</i> Karte</a>
|
||||
% }
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<span class="card-title">Ausgecheckt</span>
|
||||
<p>Aus
|
||||
%= include '_format_train', journey => $journey
|
||||
bis <a href="/s/<%= $journey->{arr_eva} %>?hafas=<%= $journey->{train_id} =~ m{[|]} ? 1 : 0 %>"><%= $journey->{arr_name} %></a></p>
|
||||
bis <a href="/s/<%= $journey->{arr_eva} %>?hafas=<%= $journey->{is_hafas} ? $journey->{backend_name} : q{} %>"><%= $journey->{arr_name} %></a></p>
|
||||
% if (@{stash('connections_iris') // [] } or @{stash('connections_hafas') // []}) {
|
||||
<span class="card-title" style="margin-top: 2ex;">Verbindungen</span>
|
||||
<p>Fahrt auswählen zum Einchecken mit Zielwahl.</p>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<ul class="collection departures connections">
|
||||
% for my $res (@{$connections}) {
|
||||
% my ($train, $via, $via_arr) = @{$res};
|
||||
% my ($train, $via, $via_arr, $hafas_service) = @{$res};
|
||||
% $via_arr = $via_arr ? $via_arr->strftime('%H:%M') : q{};
|
||||
% my $row_class = '';
|
||||
% my $link_class = 'action-checkin';
|
||||
|
@ -10,6 +10,7 @@
|
|||
% }
|
||||
% if ($checkin_from) {
|
||||
<li class="collection-item <%= $row_class %> <%= $link_class %>"
|
||||
data-hafas="<%= $hafas_service %>"
|
||||
data-station="<%= $train->station_eva %>"
|
||||
data-train="<%= $train->id %>"
|
||||
data-ts="<%= ($train->sched_datetime // $train->datetime)->epoch %>"
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
</li>
|
||||
% }
|
||||
<li class="collection-item <%= $link_class %> <%= $row_class %>"
|
||||
data-hafas="<%= $hafas %>"
|
||||
data-station="<%= $result->station_eva %>"
|
||||
data-train="<%= $result->id %>"
|
||||
data-ts="<%= ($result->sched_datetime // $result->datetime)->epoch %>"
|
||||
|
|
|
@ -30,6 +30,11 @@
|
|||
"actionTime" : 1234567, (UNIX-Timestamp des letzten Checkin/Checkout)<br/>
|
||||
"checkedIn" : true / false,<br/>
|
||||
"comment": "Kommentar",<br/>
|
||||
"backend": {<br/>
|
||||
"id": 1,<br/>
|
||||
"name": "DB",<br/>
|
||||
"type": "HAFAS",<br/>
|
||||
},<br/>
|
||||
"fromStation" : { (letzter Checkin)<br/>
|
||||
"name" : "Essen Hbf",<br/>
|
||||
"ds100" : "EE", (ggf. null)<br/>
|
||||
|
@ -122,6 +127,7 @@
|
|||
{<br/>
|
||||
"token" : "<%= $uid %>-<%= $token->{travel} // 'TOKEN' %>",<br/>
|
||||
"action" : "checkin",<br/>
|
||||
"hafas" : "DB", (HAFAS-Instanz – Default: Deutsche Bahn)<br/>
|
||||
"train" : {<br/>
|
||||
"journeyID" : "1|1426396|4|80|19082023",<br/>
|
||||
}<br/>
|
||||
|
|
|
@ -1,26 +1,27 @@
|
|||
<div class="row">
|
||||
<div class="col s12">
|
||||
<h2>
|
||||
<i class="material-icons " aria-hidden="true"><%= param('hafas') ? 'directions' : 'train' %></i>
|
||||
<div class="col s8">
|
||||
<strong style="font-size: 120%;">
|
||||
<%= $station %>
|
||||
</h2>
|
||||
</strong>
|
||||
% for my $related_station (sort { $a->{name} cmp $b->{name} } @{$related_stations}) {
|
||||
+ <%= $related_station->{name} %> <br/>
|
||||
% }
|
||||
</div>
|
||||
</div>
|
||||
% if ($api_link) {
|
||||
<div class="row">
|
||||
<div class="col s12 center-align">
|
||||
% if (param('hafas')) {
|
||||
<a href="<%= $api_link %>" class="btn-small"><i class="material-icons left" aria-hidden="true">train</i>zum Schienenverkehr</a>
|
||||
% }
|
||||
% else {
|
||||
<a href="<%= $api_link %>" class="btn-small"><i class="material-icons left" aria-hidden="true">directions</i>zum Nahverkehr</a>
|
||||
% }
|
||||
<div class="col s4 center-align">
|
||||
% my $self_link = url_for('sstation', station => param('station'));
|
||||
% if (param('hafas')) {
|
||||
<span class="btn-small disabled"><i class="material-icons left" aria-hidden="true">directions</i> <%= param('hafas') %> (HAFAS)</span>
|
||||
% }
|
||||
% else {
|
||||
% if ($user->{backend_id}) {
|
||||
<a href="/account/select_backend?redirect_to=<%= $self_link %>" class="btn-small"><i class="material-icons left" aria-hidden="true">directions</i><%= $user->{backend_name} %> (<%= $user->{backend_hafas} ? 'HAFAS' : q{} %>)</a>
|
||||
% }
|
||||
% else {
|
||||
<a href="/account/select_backend?redirect_to=<%= $self_link %>" class="btn-small"><i class="material-icons left" aria-hidden="true">train</i>DB (IRIS-TTS)</a>
|
||||
% }
|
||||
% }
|
||||
</div>
|
||||
</div>
|
||||
% }
|
||||
|
||||
% my $have_connections = 0;
|
||||
% if ($user_status->{checked_in}) {
|
||||
|
@ -40,10 +41,10 @@
|
|||
</div>
|
||||
<div class="card-action">
|
||||
% if ($can_check_out) {
|
||||
<a class="action-undo" data-id="in_transit" data-checkints="<%= $user_status->{timestamp}->epoch %>" style="margin-right: 0;">
|
||||
<a class="action-undo" data-hafas="<%= param('hafas') // q{} %>" data-id="in_transit" data-checkints="<%= $user_status->{timestamp}->epoch %>" style="margin-right: 0;">
|
||||
<i class="material-icons left" aria-hidden="true">undo</i> Rückgängig
|
||||
</a>
|
||||
<a class="action-checkout right" data-station="<%= $eva %>" data-force="1">
|
||||
<a class="action-checkout right" data-hafas="<%= param('hafas') // q{} %>" data-station="<%= $eva %>" data-force="1">
|
||||
Hier auschecken
|
||||
</a>
|
||||
% }
|
||||
|
@ -51,7 +52,7 @@
|
|||
<a class="action-undo" data-id="in_transit" data-checkints="<%= $user_status->{timestamp}->epoch %>" style="margin-right: 0;">
|
||||
<i class="material-icons left" aria-hidden="true">undo</i> Rückgängig
|
||||
</a>
|
||||
<a class="action-checkout right" data-station="<%= $eva %>" data-force="1">
|
||||
<a class="action-checkout right" data-hafas="<%= param('hafas') // q{} %>" data-station="<%= $eva %>" data-force="1">
|
||||
<i class="material-icons left" aria-hidden="true">gps_off</i>
|
||||
Hier auschecken
|
||||
</a>
|
||||
|
@ -139,7 +140,7 @@
|
|||
</p>
|
||||
% if (not $user_status->{checked_in} or ($can_check_out and $user_status->{arr_eva} and $user_status->{arrival_countdown} <= 0)) {
|
||||
% if ($hafas) {
|
||||
%= include '_departures_hafas', results => $results;
|
||||
%= include '_departures_hafas', results => $results, hafas => $hafas;
|
||||
% }
|
||||
% else {
|
||||
%= include '_departures_iris', results => $results;
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
<div class="col s12">
|
||||
<ul class="suggestions">
|
||||
% for my $suggestion (@{$suggestions // []}) {
|
||||
<li><a href="<%= url_for('station' => $suggestion->{eva}) . (param('hafas') ? '?hafas=DB' : q{}) %>"><%= $suggestion->{name} %></a></li>
|
||||
<li><a href="<%= url_for('station' => $suggestion->{eva}) . (param('hafas') ? '?hafas=' . param('hafas') : q{}) %>"><%= $suggestion->{name} %></a></li>
|
||||
% }
|
||||
</ul>
|
||||
</div>
|
||||
|
|
|
@ -20,8 +20,15 @@
|
|||
Timestamp:
|
||||
%= DateTime->now(time_zone => 'Europe/Berlin')->strftime("%d/%b/%Y:%H:%M:%S %z")
|
||||
<br/><br/>
|
||||
Message:
|
||||
%= ref($exception) ? (split(qr{\n}, $exception->message))[0] : $exception
|
||||
% if (ref($exception)) {
|
||||
Trace:<br/>
|
||||
% for my $line (split(qr{\n}, $exception->message)) {
|
||||
<%= $line %><br/>
|
||||
% }
|
||||
% }
|
||||
% else {
|
||||
Message: <%= $exception %>
|
||||
% }
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
% if (is_user_authenticated()) {
|
||||
% my $status = stash('user_status');
|
||||
% my $user = stash('user');
|
||||
% if (stash('error')) {
|
||||
<div class="row">
|
||||
<div class="col s12">
|
||||
|
@ -51,32 +52,40 @@
|
|||
% if ( @{stash('timeline') // [] } ) {
|
||||
%= include '_timeline_link', timeline => stash('timeline')
|
||||
% }
|
||||
<div class="card">
|
||||
<div class="card-content">
|
||||
<span class="card-title">Hallo, <%= current_user->{name} %>!</span>
|
||||
<p>Du bist gerade nicht eingecheckt.</p>
|
||||
<div class="geolocation" data-recent="<%= join('|', map { $_->{eva} . ';' . $_->{name} . ';' . $_->{hafas} } @{stash('recent_targets') // []} ) %>">
|
||||
<button class="btn waves-effect waves-light btn-flat">Stationen in der Umgebung abfragen</button>
|
||||
</div>
|
||||
%= form_for 'list_departures' => begin
|
||||
%= form_for 'list_departures' => begin
|
||||
<div class="card">
|
||||
<div class="card-content">
|
||||
<span class="card-title">Hallo, <%= $user->{name} %>!</span>
|
||||
<p>Du bist gerade nicht eingecheckt.</p>
|
||||
<p>
|
||||
<div class="geolocation" data-recent="<%= join('|', map { $_->{eva} . ';' . $_->{name} . ';' . $_->{hafas} } @{stash('recent_targets') // []} ) %>" data-backend=<%= $user->{backend_id} %>">
|
||||
<button class="btn waves-effect waves-light btn-flat">Stationen in der Umgebung abfragen</button>
|
||||
</div>
|
||||
<div class="input-field">
|
||||
%= text_field 'station', id => 'station', class => 'autocomplete contrast-color-text', autocomplete => 'off', required => undef
|
||||
<label for="station">Manuelle Eingabe</label>
|
||||
</div>
|
||||
<div class="center-align">
|
||||
<button class="btn waves-effect waves-light btn-flat" type="submit" name="action" value="departures">
|
||||
<i class="material-icons left" aria-hidden="true">send</i>
|
||||
Abfahrten
|
||||
</button>
|
||||
</div>
|
||||
%= end
|
||||
</p>
|
||||
</div>
|
||||
<div class="card-action">
|
||||
% if ($user->{backend_id}) {
|
||||
<a href="/account/select_backend?redirect_to=/" class="btn btn-flat"><i class="material-icons left" aria-hidden="true">directions</i><%= $user->{backend_name} %> (<%= $user->{backend_hafas} ? 'HAFAS' : q{} %>)</a>
|
||||
% }
|
||||
% else {
|
||||
<a href="/account/select_backend?redirect_to=/" class="btn btn-flat"><i class="material-icons left" aria-hidden="true">train</i>DB (IRIS-TTS)</a>
|
||||
% }
|
||||
<button class="btn right waves-effect waves-light btn-flat" type="submit" name="action" value="departures">
|
||||
<i class="material-icons left" aria-hidden="true">send</i>
|
||||
Abfahrten
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
%= end
|
||||
% }
|
||||
</div>
|
||||
</div>
|
||||
<h2 style="margin-left: 0.75rem;">Letzte Fahrten</h2>
|
||||
%= include '_history_trains', date_format => '%d.%m.%Y', journeys => [journeys->get(uid => current_user->{id}, limit => 5, with_datetime => 1)];
|
||||
%= include '_history_trains', date_format => '%d.%m.%Y', journeys => [journeys->get(uid => $user->{id}, limit => 5, with_datetime => 1)];
|
||||
% }
|
||||
% else {
|
||||
<div class="row">
|
||||
|
|
|
@ -62,7 +62,7 @@
|
|||
%= javascript "/static/${av}/js/geolocation${min}.js"
|
||||
% }
|
||||
% if (stash('with_autocomplete')) {
|
||||
%= javascript "/dyn/${av}/autocomplete.js", defer => undef
|
||||
%= javascript "/dyn/${av}/autocomplete.js?backend_id=" . (stash('backend_id') // 1), defer => undef
|
||||
% }
|
||||
% if (stash('with_map')) {
|
||||
%= javascript "/static/${av}/leaflet/leaflet.js"
|
||||
|
|
34
templates/select_backend.html.ep
Normal file
34
templates/select_backend.html.ep
Normal file
|
@ -0,0 +1,34 @@
|
|||
<div class="row">
|
||||
<div class="col s12">
|
||||
<h2>Backend auswählen</h2>
|
||||
<p style="text-align: justify;">
|
||||
Das ausgewählte Backend bestimmt die Datenquelle für Fahrten in travelynx.
|
||||
<strong>DB (HAFAS)</strong> ist eine gute Wahl für Nah-, Regional- und Fernverkehr in Deutschland und (teilweise) Nachbarländern.
|
||||
<strong>IRIS-TTS</strong> unterstützt ausschließlich Schienenverkehr; im Gegensatz zum HAFAS sind hier detaillierte Verspätungsgründe verfügbar.
|
||||
Die restlichen Backends können sich für Fahrten außerhalb Deutschlands sowie in einzelnen innerdeutschen Verkehrsverbünden lohnen.
|
||||
Fahrten im Ausland fehlen im DB-HAFAS oft; Fahrten im Inland können mit spezifischen Backends genauere Daten haben – z.B. liefert das AVV-HAFAS im Gegensatz zum DB-HAFAS Kartendaten für dortige Buslinien.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
%= form_for '/account/select_backend' => (method => 'POST') => begin
|
||||
% if (stash('redirect_to')) {
|
||||
%= hidden_field 'redirect_to' => stash('redirect_to')
|
||||
% }
|
||||
% for my $backend (@{ stash('backends') // [] }) {
|
||||
<div class="row">
|
||||
<div class="col s12 m4 l4 center-align">
|
||||
<button class="btn waves-effect waves-light <%= $backend->{id} == $user->{backend_id} ? 'disabled' : q{} %>" type="submit" name="backend" value="<%= $backend->{id} %>">
|
||||
<%= $backend->{name} %> (<%= $backend->{type} %>)
|
||||
</button>
|
||||
</div>
|
||||
<div class="col s12 m8 l8">
|
||||
% if ($backend->{longname}) {
|
||||
<%= $backend->{longname} %>
|
||||
% }
|
||||
% if ($backend->{id} == $user->{backend_id}) {
|
||||
(aktuell ausgewählt)
|
||||
% }
|
||||
</div>
|
||||
</div>
|
||||
% }
|
||||
%= end
|
|
@ -157,26 +157,21 @@
|
|||
<div>
|
||||
<label>
|
||||
%= check_box toot => 1
|
||||
<span>… Checkin auf Mastodon veröffentlichen</span>
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<label>
|
||||
%= check_box tweet => 1
|
||||
<span>… Checkin auf Twitter veröffentlichen</span>
|
||||
<span>… Checkin im Fediverse veröffentlichen</span>
|
||||
</label>
|
||||
</div>
|
||||
<p>Die Synchronisierung erfolgt spätestens drei Minuten nach der
|
||||
Zielwahl. Beachte, dass die Synchronisierung travelynx
|
||||
→ Träwelling unabhängig von der eingestellten Sichtbarkeit
|
||||
des Checkins erfolgt. travelynx reicht die Sichtbarkeit
|
||||
aber an Träwelling weiter.
|
||||
Träwelling-Checkins können von travelynx aktuell nicht
|
||||
rückgängig gemacht werden. Eine nachträgliche Änderung der
|
||||
Zielstation wird nicht übernommen. Mastodon und Twitter beziehen
|
||||
sich auf die in den <a
|
||||
Zielwahl. Es werden ausschließlich Checkins mittels
|
||||
DB (IRIS-TTS) und DB (HAFAS) synchornisiert. Beachte, dass
|
||||
die Synchronisierung travelynx → Träwelling unabhängig von
|
||||
der eingestellten Sichtbarkeit des Checkins erfolgt.
|
||||
travelynx reicht die Sichtbarkeit aber an Träwelling
|
||||
weiter. Träwelling-Checkins können von travelynx aktuell
|
||||
nicht rückgängig gemacht werden. Eine nachträgliche
|
||||
Änderung der Zielstation wird nicht übernommen. Fediverse
|
||||
bezieht sich auf den in den <a
|
||||
href="https://traewelling.de/settings">Träwelling-Einstellungen</a>
|
||||
verknüpften Accounts.</p>
|
||||
verknüpften Account.</p>
|
||||
</div>
|
||||
<div class="input-field col s12">
|
||||
<div>
|
||||
|
|
|
@ -5,9 +5,13 @@
|
|||
Travelynx kann anhand deiner vergangenen Fahrten Verbindungen zum
|
||||
Einchecken vorschlagen. Fährst zu z.B regelmäßig von Dortmund Hbf
|
||||
nach Essen Hbf, werden dir in Dortmund bevorzugt Fahrten angezeigt, die
|
||||
Essen passieren. Bei Auswahl dieser wird nicht nur in die Fahrt eingecheckt,
|
||||
Essen passieren. Bei Auswahl dieser wird nicht nur in die Fahrt eingecheckt,
|
||||
sondern auch direkt Essen Hbf als Ziel eingetragen.
|
||||
<p/>
|
||||
<p>
|
||||
Beachte, dass nicht alle von travelynx unterstützten Backends die
|
||||
für dieses Feature notwendigen Daten bereitstellen.
|
||||
</p>
|
||||
<!-- <p>
|
||||
Falls du das nicht nützlich findest oder nicht möchtest, dass deine
|
||||
regelmäßigen (Anschluss-)Züge auf deinem Bildschirm sichtbar sind,
|
||||
|
|
Loading…
Reference in a new issue