2019-03-21 21:12:45 +00:00
package Travelynx ;
2020-11-28 20:03:51 +00:00
2023-07-03 15:59:25 +00:00
# Copyright (C) 2020-2023 Birte Kristina Friesel
2020-11-27 21:12:56 +00:00
#
2021-01-29 17:32:13 +00:00
# SPDX-License-Identifier: AGPL-3.0-or-later
2020-11-27 21:12:56 +00:00
2019-03-21 21:12:45 +00:00
use Mojo::Base 'Mojolicious' ;
2019-04-17 11:10:49 +00:00
use Mojo::Pg ;
2019-05-31 18:18:22 +00:00
use Mojo::Promise ;
2019-03-21 21:12:45 +00:00
use Mojolicious::Plugin::Authentication ;
use Cache::File ;
use Crypt::Eksblowfish::Bcrypt qw( bcrypt en_base64 ) ;
use DateTime ;
2019-05-31 18:18:22 +00:00
use DateTime::Format::Strptime ;
2022-08-17 19:51:24 +00:00
use Encode qw( decode encode ) ;
2020-01-06 16:59:40 +00:00
use File::Slurp qw( read_file ) ;
2019-04-07 14:55:35 +00:00
use JSON ;
2019-12-14 20:53:23 +00:00
use List::Util ;
2022-08-17 19:51:24 +00:00
use List::UtilsBy qw( uniq_by ) ;
2020-08-16 19:20:23 +00:00
use List::MoreUtils qw( first_index ) ;
2019-06-23 15:08:47 +00:00
use Travel::Status::DE::DBWagenreihung ;
2020-09-06 10:41:36 +00:00
use Travelynx::Helper::DBDB ;
2020-07-28 11:01:44 +00:00
use Travelynx::Helper::HAFAS ;
2020-08-06 14:04:12 +00:00
use Travelynx::Helper::IRIS ;
2019-04-02 18:10:48 +00:00
use Travelynx::Helper::Sendmail ;
2020-09-30 17:12:29 +00:00
use Travelynx::Helper::Traewelling ;
2020-10-11 17:38:01 +00:00
use Travelynx::Model::InTransit ;
2020-08-16 19:20:23 +00:00
use Travelynx::Model::Journeys ;
2020-11-28 20:03:51 +00:00
use Travelynx::Model::JourneyStatsCache ;
2022-12-07 18:42:48 +00:00
use Travelynx::Model::Stations ;
2020-09-30 17:12:29 +00:00
use Travelynx::Model::Traewelling ;
2020-07-27 16:53:22 +00:00
use Travelynx::Model::Users ;
2019-03-21 21:12:45 +00:00
sub check_password {
my ( $ password , $ hash ) = @ _ ;
2022-03-07 17:12:43 +00:00
if ( bcrypt ( substr ( $ password , 0 , 10000 ) , $ hash ) eq $ hash ) {
2019-03-21 21:12:45 +00:00
return 1 ;
}
return 0 ;
}
sub epoch_to_dt {
my ( $ epoch ) = @ _ ;
# Bugs (and user errors) may lead to undefined timestamps. Set them to
# 1970-01-01 to avoid crashing and show obviously wrong data instead.
$ epoch // = 0 ;
return DateTime - > from_epoch (
epoch = > $ epoch ,
2020-04-19 15:09:02 +00:00
time_zone = > 'Europe/Berlin' ,
locale = > 'de-DE' ,
2019-03-21 21:12:45 +00:00
) ;
}
sub startup {
my ( $ self ) = @ _ ;
2019-03-22 20:18:03 +00:00
push ( @ { $ self - > commands - > namespaces } , 'Travelynx::Command' ) ;
2019-03-22 15:57:24 +00:00
$ self - > defaults ( layout = > 'default' ) ;
2019-03-21 21:12:45 +00:00
2020-04-19 16:26:20 +00:00
$ self - > types - > type ( csv = > 'text/csv; charset=utf-8' ) ;
2019-03-22 15:57:24 +00:00
$ self - > types - > type ( json = > 'application/json; charset=utf-8' ) ;
2019-04-13 10:17:19 +00:00
$ self - > plugin ( 'Config' ) ;
if ( $ self - > config - > { secrets } ) {
$ self - > secrets ( $ self - > config - > { secrets } ) ;
}
2020-12-17 19:58:47 +00:00
chomp $ self - > config - > { version } ;
2023-06-29 19:54:51 +00:00
$ self - > defaults ( version = > $ self - > config - > { version } // 'UNKNOWN' ) ;
2019-12-29 22:06:39 +00:00
2019-03-22 15:57:24 +00:00
$ self - > plugin (
authentication = > {
autoload_user = > 1 ,
fail_render = > { template = > 'login' } ,
load_user = > sub {
my ( $ self , $ uid ) = @ _ ;
return $ self - > get_user_data ( $ uid ) ;
} ,
validate_user = > sub {
my ( $ self , $ username , $ password , $ extradata ) = @ _ ;
2020-07-27 16:53:22 +00:00
my $ user_info
= $ self - > users - > get_login_data ( name = > $ username ) ;
2019-03-22 15:57:24 +00:00
if ( not $ user_info ) {
return undef ;
}
if ( $ user_info - > { status } != 1 ) {
return undef ;
}
if ( check_password ( $ password , $ user_info - > { password_hash } ) )
{
return $ user_info - > { id } ;
}
2019-03-21 21:12:45 +00:00
return undef ;
2019-03-22 15:57:24 +00:00
} ,
}
) ;
2023-08-07 19:17:10 +00:00
if ( my $ oa = $ self - > config - > { traewelling } { oauth } ) {
$ self - > plugin (
OAuth2 = > {
providers = > {
traewelling = > {
key = > $ oa - > { id } ,
secret = > $ oa - > { secret } ,
authorize_url = >
'https://traewelling.de/oauth/authorize?response_type=code' ,
token_url = > 'https://traewelling.de/oauth/token' ,
}
}
}
) ;
}
2019-03-22 15:57:24 +00:00
$ self - > sessions - > default_expiration ( 60 * 60 * 24 * 180 ) ;
2019-06-01 16:04:50 +00:00
# Starting with v8.11, Mojolicious sends SameSite=Lax Cookies by default.
# In theory, "The default lax value provides a reasonable balance between
# security and usability for websites that want to maintain user's logged-in
# session after the user arrives from an external link". In practice,
# Safari (both iOS and macOS) does not send a SameSite=lax cookie when
2022-08-19 15:21:41 +00:00
# following a link from an external site. So, bahn.expert providing a
2019-06-01 16:04:50 +00:00
# checkin link to travelynx.de/s/whatever does not work because the user
# is not logged in due to Safari not sending the cookie.
#
# This looks a lot like a Safari bug, but we can't do anything about it. So
# we don't set the SameSite flag at all for now.
#
# --derf, 2019-05-01
$ self - > sessions - > samesite ( undef ) ;
2019-03-22 15:57:24 +00:00
$ self - > defaults ( layout = > 'default' ) ;
2019-05-18 06:23:59 +00:00
$ self - > hook (
before_dispatch = > sub {
my ( $ self ) = @ _ ;
2023-07-15 14:11:21 +00:00
# The "theme" cookie is set client-side if the theme we delivered was
# changed by dark mode detection or by using the theme switcher. It's
# not part of Mojolicious' session data (and can't be, due to
# signing and HTTPOnly), so we need to add it here.
2019-05-18 06:23:59 +00:00
for my $ cookie ( @ { $ self - > req - > cookies } ) {
if ( $ cookie - > name eq 'theme' ) {
$ self - > session ( theme = > $ cookie - > value ) ;
return ;
}
}
}
) ;
2019-04-13 10:17:19 +00:00
$ self - > attr (
cache_iris_main = > sub {
my ( $ self ) = @ _ ;
return Cache::File - > new (
cache_root = > $ self - > app - > config - > { cache } - > { schedule } ,
default_expires = > '6 hours' ,
lock_level = > Cache::File:: LOCK_LOCAL ( ) ,
) ;
}
) ;
$ self - > attr (
cache_iris_rt = > sub {
my ( $ self ) = @ _ ;
return Cache::File - > new (
cache_root = > $ self - > app - > config - > { cache } - > { realtime } ,
default_expires = > '70 seconds' ,
lock_level = > Cache::File:: LOCK_LOCAL ( ) ,
) ;
}
) ;
2019-11-16 14:24:16 +00:00
$ self - > attr (
coordinates_by_station = > sub {
2020-01-25 14:46:42 +00:00
my $ legacy_names = $ self - > app - > renamed_station ;
2022-12-07 18:42:48 +00:00
my $ location = $ self - > stations - > get_latlon_by_name ;
2020-01-06 17:24:28 +00:00
while ( my ( $ old_name , $ new_name ) = each % { $ legacy_names } ) {
2022-12-07 18:42:48 +00:00
$ location - > { $ old_name } = $ location - > { $ new_name } ;
2019-12-28 13:35:07 +00:00
}
2022-12-07 18:42:48 +00:00
return $ location ;
2019-11-16 14:24:16 +00:00
}
) ;
2023-07-15 14:11:21 +00:00
# https://de.wikipedia.org/wiki/Liste_nach_Gemeinden_und_Regionen_benannter_IC/ICE-Fahrzeuge#Namensgebung_ICE-Triebz%C3%BCge_nach_Gemeinden
# via https://github.com/marudor/bahn.expert/blob/main/src/server/coachSequence/TrainNames.ts
2020-01-04 17:48:46 +00:00
$ self - > attr (
ice_name = > sub {
2020-01-06 17:03:50 +00:00
my $ id_to_name = JSON - > new - > utf8 - > decode (
scalar read_file ( 'share/ice_names.json' ) ) ;
2020-01-06 16:59:40 +00:00
return $ id_to_name ;
2020-01-04 17:48:46 +00:00
}
) ;
2020-01-25 14:46:42 +00:00
$ self - > attr (
renamed_station = > sub {
my $ legacy_to_new = JSON - > new - > utf8 - > decode (
scalar read_file ( 'share/old_station_names.json' ) ) ;
return $ legacy_to_new ;
}
) ;
2022-02-16 20:29:28 +00:00
if ( not $ self - > app - > config - > { base_url } ) {
$ self - > app - > log - > error (
"travelynx.conf: 'base_url' is missing. Links in maintenance/work/worker-generated E-Mails will be incorrect. This variable was introduced in travelynx 1.22; see examples/travelynx.conf for documentation."
) ;
}
2022-02-14 20:41:32 +00:00
$ self - > helper (
base_url_for = > sub {
my ( $ self , $ path ) = @ _ ;
2022-02-16 20:29:28 +00:00
if ( ( my $ url = $ self - > url_for ( $ path ) ) - > base ne q{ }
2023-01-08 08:59:18 +00:00
or not $ self - > app - > config - > { base_url } )
2022-02-16 20:29:28 +00:00
{
2022-02-14 20:41:32 +00:00
return $ url ;
}
return $ self - > url_for ( $ path )
- > base ( $ self - > app - > config - > { base_url } ) ;
}
) ;
2019-04-17 11:10:49 +00:00
$ self - > helper (
2020-07-28 11:01:44 +00:00
hafas = > sub {
2020-07-27 16:53:22 +00:00
my ( $ self ) = @ _ ;
2020-07-28 11:01:44 +00:00
state $ hafas = Travelynx::Helper::HAFAS - > new (
log = > $ self - > app - > log ,
main_cache = > $ self - > app - > cache_iris_main ,
realtime_cache = > $ self - > app - > cache_iris_rt ,
2022-02-14 20:41:32 +00:00
root_url = > $ self - > base_url_for ( '/' ) - > to_abs ,
2020-07-28 11:01:44 +00:00
user_agent = > $ self - > ua ,
version = > $ self - > app - > config - > { version } ,
) ;
2020-07-27 16:53:22 +00:00
}
) ;
2020-08-06 14:04:12 +00:00
$ self - > helper (
iris = > sub {
my ( $ self ) = @ _ ;
2020-09-06 10:41:36 +00:00
state $ iris = Travelynx::Helper::IRIS - > new (
2020-08-06 14:04:12 +00:00
log = > $ self - > app - > log ,
main_cache = > $ self - > app - > cache_iris_main ,
realtime_cache = > $ self - > app - > cache_iris_rt ,
2022-02-14 20:41:32 +00:00
root_url = > $ self - > base_url_for ( '/' ) - > to_abs ,
2020-08-06 14:04:12 +00:00
version = > $ self - > app - > config - > { version } ,
) ;
}
) ;
2020-09-30 17:12:29 +00:00
$ self - > helper (
traewelling = > sub {
my ( $ self ) = @ _ ;
state $ trwl = Travelynx::Model::Traewelling - > new ( pg = > $ self - > pg ) ;
}
) ;
$ self - > helper (
traewelling_api = > sub {
my ( $ self ) = @ _ ;
state $ trwl_api = Travelynx::Helper::Traewelling - > new (
log = > $ self - > app - > log ,
model = > $ self - > traewelling ,
2022-02-14 20:41:32 +00:00
root_url = > $ self - > base_url_for ( '/' ) - > to_abs ,
2020-09-30 17:12:29 +00:00
user_agent = > $ self - > ua ,
version = > $ self - > app - > config - > { version } ,
) ;
}
) ;
2020-10-11 17:38:01 +00:00
$ self - > helper (
in_transit = > sub {
my ( $ self ) = @ _ ;
state $ in_transit = Travelynx::Model::InTransit - > new (
log = > $ self - > app - > log ,
pg = > $ self - > pg ,
) ;
}
) ;
2020-11-28 20:03:51 +00:00
$ self - > helper (
journey_stats_cache = > sub {
my ( $ self ) = @ _ ;
state $ journey_stats_cache
= Travelynx::Model::JourneyStatsCache - > new (
log = > $ self - > app - > log ,
pg = > $ self - > pg ,
) ;
}
) ;
2020-08-16 19:20:23 +00:00
$ self - > helper (
journeys = > sub {
my ( $ self ) = @ _ ;
state $ journeys = Travelynx::Model::Journeys - > new (
2022-12-07 21:46:04 +00:00
log = > $ self - > app - > log ,
pg = > $ self - > pg ,
in_transit = > $ self - > in_transit ,
stats_cache = > $ self - > journey_stats_cache ,
renamed_station = > $ self - > app - > renamed_station ,
latlon_by_station = > $ self - > app - > coordinates_by_station ,
stations = > $ self - > stations ,
2020-08-16 19:20:23 +00:00
) ;
}
) ;
2019-04-04 16:26:53 +00:00
$ self - > helper (
2019-04-17 11:10:49 +00:00
pg = > sub {
my ( $ self ) = @ _ ;
my $ config = $ self - > app - > config ;
my $ dbname = $ config - > { db } - > { database } ;
my $ host = $ config - > { db } - > { host } // 'localhost' ;
my $ port = $ config - > { db } - > { port } // 5432 ;
my $ user = $ config - > { db } - > { user } ;
my $ pw = $ config - > { db } - > { password } ;
state $ pg
= Mojo::Pg - > new ( "postgresql://${user}\@${host}:${port}/${dbname}" )
- > password ( $ pw ) ;
2021-09-03 18:24:54 +00:00
$ pg - > on (
connection = > sub {
my ( $ pg , $ dbh ) = @ _ ;
$ dbh - > do ( "set time zone 'Europe/Berlin'" ) ;
}
) ;
return $ pg ;
2019-04-13 15:09:10 +00:00
}
2019-04-04 16:26:53 +00:00
) ;
2019-04-02 18:10:48 +00:00
2020-07-28 11:01:44 +00:00
$ self - > helper (
sendmail = > sub {
state $ sendmail = Travelynx::Helper::Sendmail - > new (
config = > ( $ self - > config - > { mail } // { } ) ,
log = > $ self - > log
) ;
}
) ;
2022-12-07 18:42:48 +00:00
$ self - > helper (
stations = > sub {
my ( $ self ) = @ _ ;
state $ stations
= Travelynx::Model::Stations - > new ( pg = > $ self - > pg ) ;
}
) ;
2020-07-28 11:01:44 +00:00
$ self - > helper (
users = > sub {
my ( $ self ) = @ _ ;
state $ users = Travelynx::Model::Users - > new ( pg = > $ self - > pg ) ;
}
) ;
2020-09-06 10:41:36 +00:00
$ self - > helper (
dbdb = > sub {
my ( $ self ) = @ _ ;
state $ dbdb = Travelynx::Helper::DBDB - > new (
log = > $ self - > app - > log ,
cache = > $ self - > app - > cache_iris_main ,
2022-02-14 20:41:32 +00:00
root_url = > $ self - > base_url_for ( '/' ) - > to_abs ,
2020-09-06 10:41:36 +00:00
user_agent = > $ self - > ua ,
version = > $ self - > app - > config - > { version } ,
) ;
}
) ;
2019-05-19 08:32:57 +00:00
$ self - > helper (
'now' = > sub {
return DateTime - > now ( time_zone = > 'Europe/Berlin' ) ;
}
) ;
2019-04-21 13:46:19 +00:00
$ self - > helper (
'numify_skipped_stations' = > sub {
my ( $ self , $ count ) = @ _ ;
if ( $ count == 0 ) {
return 'INTERNAL ERROR' ;
}
if ( $ count == 1 ) {
return
'Eine Station ohne Geokoordinaten wurde nicht berücksichtigt.' ;
}
return
"${count} Stationen ohne Geookordinaten wurden nicht berücksichtigt." ;
}
) ;
2022-11-12 10:10:58 +00:00
$ self - > helper (
'load_icon' = > sub {
my ( $ self , $ load ) = @ _ ;
my $ first = $ load - > { FIRST } // 0 ;
my $ second = $ load - > { SECOND } // 0 ;
my @ symbols
= (
qw( help_outline person_outline people priority_high not_interested )
) ;
return ( $ symbols [ $ first ] , $ symbols [ $ second ] ) ;
}
) ;
2023-02-27 21:14:54 +00:00
$ self - > helper (
'visibility_icon' = > sub {
my ( $ self , $ visibility ) = @ _ ;
if ( $ visibility eq 'public' ) {
return 'language' ;
}
if ( $ visibility eq 'travelynx' ) {
return 'lock_open' ;
}
2023-06-04 16:21:36 +00:00
if ( $ visibility eq 'followers' ) {
return 'group' ;
}
2023-02-27 21:14:54 +00:00
if ( $ visibility eq 'unlisted' ) {
return 'lock_outline' ;
}
if ( $ visibility eq 'private' ) {
return 'lock' ;
}
return 'help_outline' ;
}
) ;
2019-03-22 15:57:24 +00:00
$ self - > helper (
2023-07-09 12:46:28 +00:00
'checkin_p' = > sub {
2020-09-30 17:12:29 +00:00
my ( $ self , % opt ) = @ _ ;
2019-12-14 20:53:23 +00:00
2020-09-30 17:12:29 +00:00
my $ station = $ opt { station } ;
my $ train_id = $ opt { train_id } ;
my $ uid = $ opt { uid } // $ self - > current_user - > { id } ;
2021-12-12 16:13:51 +00:00
my $ db = $ opt { db } // $ self - > pg - > db ;
2023-07-09 12:46:28 +00:00
my $ hafas ;
2019-03-21 21:12:45 +00:00
2023-07-09 05:16:22 +00:00
my $ user = $ self - > get_user_status ( $ uid , $ db ) ;
if ( $ user - > { checked_in } or $ user - > { cancelled } ) {
2023-07-09 12:46:28 +00:00
return Mojo::Promise - > reject ( 'You are already checked in' ) ;
}
2023-07-09 05:16:22 +00:00
2023-08-13 10:51:15 +00:00
if ( $ train_id =~ m {[|]} ) {
return $ self - > _checkin_hafas_p ( % opt ) ;
}
2023-07-09 12:46:28 +00:00
my $ promise = Mojo::Promise - > new ;
2023-07-09 05:16:22 +00:00
2023-07-09 12:46:28 +00:00
$ self - > iris - > get_departures_p (
station = > $ station ,
lookbehind = > 140 ,
lookahead = > 40
) - > then (
sub {
my ( $ status ) = @ _ ;
2023-07-09 05:16:22 +00:00
2023-07-09 12:46:28 +00:00
if ( $ status - > { errstr } ) {
$ promise - > reject ( $ status - > { errstr } ) ;
return ;
}
2023-07-09 05:16:22 +00:00
2023-07-09 12:46:28 +00:00
my $ eva = $ status - > { station_eva } ;
my $ train = List::Util:: first { $ _ - > train_id eq $ train_id }
@ { $ status - > { results } } ;
if ( not defined $ train ) {
$ promise - > reject ( "Train ${train_id} not found" ) ;
return ;
}
eval {
$ self - > in_transit - > add (
uid = > $ uid ,
db = > $ db ,
departure_eva = > $ eva ,
train = > $ train ,
route = > [ $ self - > iris - > route_diff ( $ train ) ] ,
) ;
} ;
if ( $@ ) {
$ self - > app - > log - > error (
"Checkin($uid): INSERT failed: $@" ) ;
$ promise - > reject ( 'INSERT failed: ' . $@ ) ;
return ;
}
# mustn't be called during a transaction
if ( not $ opt { in_transaction } ) {
$ self - > add_route_timestamps ( $ uid , $ train , 1 ) ;
$ self - > run_hook ( $ uid , 'checkin' ) ;
}
$ promise - > resolve ( $ train ) ;
return ;
}
) - > catch (
sub {
2023-07-09 15:41:47 +00:00
my ( $ err , $ status ) = @ _ ;
2023-07-09 12:46:28 +00:00
$ promise - > reject ( $ status - > { errstr } ) ;
return ;
}
) - > wait ;
return $ promise ;
2019-03-22 15:57:24 +00:00
}
) ;
2023-08-13 10:51:15 +00:00
$ self - > helper (
'_checkin_hafas_p' = > sub {
my ( $ self , % opt ) = @ _ ;
my $ station = $ opt { station } ;
my $ train_id = $ opt { train_id } ;
my $ uid = $ opt { uid } // $ self - > current_user - > { id } ;
my $ db = $ opt { db } // $ self - > pg - > db ;
my $ hafas ;
my $ promise = Mojo::Promise - > new ;
$ self - > hafas - > get_journey_p ( trip_id = > $ train_id ) - > then (
sub {
my ( $ journey ) = @ _ ;
my $ found ;
for my $ stop ( $ journey - > route ) {
if ( $ stop - > eva == $ station ) {
$ found = $ stop ;
last ;
}
}
if ( not $ found ) {
$ promise - > reject (
"Did not find journey $train_id at $station" ) ;
return ;
}
for my $ stop ( $ journey - > route ) {
$ self - > stations - > add_or_update (
stop = > $ stop ,
db = > $ db ,
) ;
}
eval {
$ self - > in_transit - > add (
uid = > $ uid ,
db = > $ db ,
journey = > $ journey ,
stop = > $ found ,
) ;
} ;
if ( $@ ) {
$ self - > app - > log - > error (
"Checkin($uid): INSERT failed: $@" ) ;
$ promise - > reject ( 'INSERT failed: ' . $@ ) ;
return ;
}
$ self - > in_transit - > update_data (
uid = > $ uid ,
db = > $ db ,
data = > { trip_id = > $ journey - > id }
) ;
$ promise - > resolve ( $ journey ) ;
}
) - > catch (
sub {
my ( $ err ) = @ _ ;
$ promise - > reject ( $ err ) ;
return ;
}
) - > wait ;
return $ promise ;
}
) ;
2019-03-22 15:57:24 +00:00
$ self - > helper (
'undo' = > sub {
2019-12-14 20:53:23 +00:00
my ( $ self , $ journey_id , $ uid ) = @ _ ;
$ uid // = $ self - > current_user - > { id } ;
2019-03-22 15:57:24 +00:00
2019-04-23 16:08:07 +00:00
if ( $ journey_id eq 'in_transit' ) {
2020-10-11 17:38:01 +00:00
eval { $ self - > in_transit - > delete ( uid = > $ uid ) ; } ;
2019-04-23 16:08:07 +00:00
if ( $@ ) {
$ self - > app - > log - > error ( "Undo($uid, $journey_id): $@" ) ;
return "Undo($journey_id): $@" ;
}
2019-05-05 16:09:11 +00:00
$ self - > run_hook ( $ uid , 'undo' ) ;
2019-04-23 16:08:07 +00:00
return undef ;
}
if ( $ journey_id !~ m { ^ \d+ $ }x ) {
return 'Invalid Journey ID' ;
2019-03-21 21:12:45 +00:00
}
2019-04-17 11:10:49 +00:00
eval {
2019-04-23 16:08:07 +00:00
my $ db = $ self - > pg - > db ;
my $ tx = $ db - > begin ;
2020-10-11 19:08:22 +00:00
my $ journey = $ self - > journeys - > pop (
uid = > $ uid ,
db = > $ db ,
journey_id = > $ journey_id
2019-04-23 16:08:07 +00:00
) ;
if ( $ journey - > { edited } ) {
die (
"Cannot undo a journey which has already been edited. Please delete manually.\n"
) ;
}
delete $ journey - > { edited } ;
delete $ journey - > { id } ;
2023-06-07 21:04:00 +00:00
# users may force checkouts at stations that are not part of
# the train's scheduled (or real-time) route. re-adding those
# to in-transit violates the assumption that each train has
# a valid destination. Remove the target in this case.
my $ route = JSON - > new - > decode ( $ journey - > { route } ) ;
my $ found_checkout_id ;
for my $ stop ( @ { $ route } ) {
if ( $ stop - > [ 1 ] == $ journey - > { checkout_station_id } ) {
$ found_checkout_id = 1 ;
last ;
}
}
if ( not $ found_checkout_id ) {
$ journey - > { checkout_station_id } = undef ;
$ journey - > { checkout_time } = undef ;
$ journey - > { arr_platform } = undef ;
$ journey - > { sched_arrival } = undef ;
$ journey - > { real_arrival } = undef ;
}
2020-10-11 17:38:01 +00:00
$ self - > in_transit - > add_from_journey (
db = > $ db ,
journey = > $ journey
) ;
2019-04-23 16:08:07 +00:00
my $ cache_ts = DateTime - > now ( time_zone = > 'Europe/Berlin' ) ;
if ( $ journey - > { real_departure }
=~ m { ^ (?<year> \d{4} ) - ( ? <month> \ d { 2 } ) } x )
{
$ cache_ts - > set (
year = > $+ { year } ,
month = > $+ { month }
) ;
}
2020-11-28 20:03:51 +00:00
$ self - > journey_stats_cache - > invalidate (
2020-08-16 19:20:23 +00:00
ts = > $ cache_ts ,
db = > $ db ,
uid = > $ uid
) ;
2019-04-23 16:08:07 +00:00
$ tx - > commit ;
2019-04-17 11:10:49 +00:00
} ;
if ( $@ ) {
2019-04-23 16:08:07 +00:00
$ self - > app - > log - > error ( "Undo($uid, $journey_id): $@" ) ;
return "Undo($journey_id): $@" ;
2019-03-21 21:12:45 +00:00
}
2019-05-05 16:09:11 +00:00
$ self - > run_hook ( $ uid , 'undo' ) ;
2019-04-23 16:08:07 +00:00
return undef ;
2019-03-21 21:12:45 +00:00
}
2019-03-22 15:57:24 +00:00
) ;
2019-03-21 21:12:45 +00:00
2019-03-22 15:57:24 +00:00
$ self - > helper (
2023-07-23 18:18:10 +00:00
'checkout_p' = > sub {
2020-09-30 17:12:29 +00:00
my ( $ self , % opt ) = @ _ ;
2019-03-21 21:12:45 +00:00
2023-07-23 10:21:37 +00:00
my $ station = $ opt { station } ;
my $ dep_eva = $ opt { dep_eva } ;
my $ arr_eva = $ opt { arr_eva } ;
my $ with_related = $ opt { with_related } // 0 ;
my $ force = $ opt { force } ;
2023-07-23 18:18:10 +00:00
my $ uid = $ opt { uid } // $ self - > current_user - > { id } ;
my $ db = $ opt { db } // $ self - > pg - > db ;
my $ user = $ self - > get_user_status ( $ uid , $ db ) ;
my $ train_id = $ user - > { train_id } ;
my $ promise = Mojo::Promise - > new ;
2019-03-21 21:12:45 +00:00
2021-12-12 16:13:51 +00:00
if ( not $ station ) {
$ self - > app - > log - > error ( "Checkout($uid): station is empty" ) ;
2023-07-23 18:18:10 +00:00
return $ promise - > resolve ( 1 ,
'BUG: Checkout station is empty.' ) ;
2021-12-12 16:13:51 +00:00
}
2019-03-22 15:57:24 +00:00
if ( not $ user - > { checked_in } and not $ user - > { cancelled } ) {
2023-07-23 18:18:10 +00:00
return $ promise - > resolve ( 0 ,
'You are not checked into any train' ) ;
2019-03-22 15:57:24 +00:00
}
2023-07-23 18:18:10 +00:00
2023-03-10 16:18:08 +00:00
if ( $ dep_eva and $ dep_eva != $ user - > { dep_eva } ) {
2023-07-23 18:18:10 +00:00
return $ promise - > resolve ( 0 , 'race condition' ) ;
2023-03-10 16:18:08 +00:00
}
if ( $ arr_eva and $ arr_eva != $ user - > { arr_eva } ) {
2023-07-23 18:18:10 +00:00
return $ promise - > resolve ( 0 , 'race condition' ) ;
2023-03-10 16:18:08 +00:00
}
2019-03-21 21:12:45 +00:00
2023-08-13 10:51:15 +00:00
if ( $ train_id =~ m {[|]} ) {
return $ self - > _checkout_hafas_p ( % opt ) ;
}
2020-10-11 17:38:01 +00:00
my $ now = DateTime - > now ( time_zone = > 'Europe/Berlin' ) ;
my $ journey = $ self - > in_transit - > get (
uid = > $ uid ,
with_data = > 1
) ;
2019-12-15 12:42:11 +00:00
2023-07-23 18:18:10 +00:00
$ self - > iris - > get_departures_p (
station = > $ station ,
lookbehind = > 120 ,
lookahead = > 180 ,
with_related = > $ with_related ,
) - > then (
sub {
my ( $ status ) = @ _ ;
2019-12-15 12:42:11 +00:00
2023-07-23 18:18:10 +00:00
my $ new_checkout_station_id = $ status - > { station_eva } ;
2019-04-23 16:08:07 +00:00
2023-07-23 18:18:10 +00:00
# Store the intended checkout station regardless of this operation's
# success.
# TODO for with_related == 1, the correct EVA may be different
# and should be fetched from $train later on
$ self - > in_transit - > set_arrival_eva (
uid = > $ uid ,
db = > $ db ,
arrival_eva = > $ new_checkout_station_id
) ;
2019-12-23 21:57:45 +00:00
2023-07-23 18:18:10 +00:00
# If in_transit already contains arrival data for another estimated
# destination, we must invalidate it.
if ( defined $ journey - > { checkout_station_id }
and $ journey - > { checkout_station_id }
!= $ new_checkout_station_id )
{
$ self - > in_transit - > unset_arrival_data (
uid = > $ uid ,
db = > $ db
) ;
}
2019-04-23 16:08:07 +00:00
2023-07-23 18:18:10 +00:00
# Note that a train may pass the same station several times.
# Notable example: S41 / S42 ("Ringbahn") both starts and
# terminates at Berlin Südkreuz
my $ train = List::Util:: first {
$ _ - > train_id eq $ train_id
and $ _ - > sched_arrival
and $ _ - > sched_arrival - > epoch
> $ user - > { sched_departure } - > epoch
}
@ { $ status - > { results } } ;
$ train // = List::Util:: first { $ _ - > train_id eq $ train_id }
@ { $ status - > { results } } ;
2019-04-23 16:08:07 +00:00
2023-07-23 18:18:10 +00:00
if ( not defined $ train ) {
2019-06-01 09:52:42 +00:00
2023-07-23 18:18:10 +00:00
# Arrival time via IRIS is unknown, so the train probably
# has not arrived yet. Fall back to HAFAS.
# TODO support cases where $station is EVA or DS100 code
if (
my $ station_data
= List::Util:: first { $ _ - > [ 0 ] eq $ station }
@ { $ journey - > { route } }
)
{
$ station_data = $ station_data - > [ 2 ] ;
if ( $ station_data - > { sched_arr } ) {
my $ sched_arr
= epoch_to_dt ( $ station_data - > { sched_arr } ) ;
my $ rt_arr
= epoch_to_dt ( $ station_data - > { rt_arr } ) ;
if ( $ rt_arr - > epoch == 0 ) {
$ rt_arr = $ sched_arr - > clone ;
if ( $ station_data - > { arr_delay }
and $ station_data - > { arr_delay }
=~ m {^\d+$} )
{
$ rt_arr - > add ( minutes = >
$ station_data - > { arr_delay } ) ;
}
}
$ self - > in_transit - > set_arrival_times (
uid = > $ uid ,
db = > $ db ,
sched_arrival = > $ sched_arr ,
rt_arrival = > $ rt_arr
) ;
2022-11-05 18:19:52 +00:00
}
2019-06-01 09:52:42 +00:00
}
2023-07-23 18:18:10 +00:00
if ( not $ force ) {
2020-09-30 17:12:29 +00:00
2023-07-23 18:18:10 +00:00
# mustn't be called during a transaction
if ( not $ opt { in_transaction } ) {
$ self - > run_hook ( $ uid , 'update' ) ;
}
$ promise - > resolve ( 1 , undef ) ;
return ;
}
2020-09-30 17:12:29 +00:00
}
2023-07-23 18:18:10 +00:00
my $ has_arrived = 0 ;
2019-04-23 16:08:07 +00:00
2023-07-23 18:18:10 +00:00
eval {
2019-04-23 16:08:07 +00:00
2023-07-23 18:18:10 +00:00
my $ tx ;
if ( not $ opt { in_transaction } ) {
$ tx = $ db - > begin ;
}
2019-04-23 16:08:07 +00:00
2023-07-23 18:18:10 +00:00
if ( defined $ train
and not $ train - > arrival
and not $ force )
{
my $ train_no = $ train - > train_no ;
die ( "Train ${train_no} has no arrival timestamp\n" ) ;
}
elsif ( defined $ train and $ train - > arrival ) {
$ self - > in_transit - > set_arrival (
uid = > $ uid ,
db = > $ db ,
train = > $ train ,
route = > [ $ self - > iris - > route_diff ( $ train ) ]
) ;
2019-12-20 17:33:59 +00:00
2023-07-23 18:18:10 +00:00
$ has_arrived
= $ train - > arrival - > epoch < $ now - > epoch ? 1 : 0 ;
if ( $ has_arrived ) {
my @ unknown_stations
= $ self - > stations - > grep_unknown (
$ train - > route ) ;
if ( @ unknown_stations ) {
$ self - > app - > log - > warn (
sprintf (
2020-10-08 18:04:25 +00:00
'Route of %s %s (%s -> %s) contains unknown stations: %s' ,
2023-07-23 18:18:10 +00:00
$ train - > type ,
$ train - > train_no ,
$ train - > origin ,
$ train - > destination ,
join ( ', ' , @ unknown_stations )
)
) ;
}
}
}
$ journey = $ self - > in_transit - > get (
uid = > $ uid ,
db = > $ db
) ;
if ( $ has_arrived or $ force ) {
$ self - > journeys - > add_from_in_transit (
db = > $ db ,
journey = > $ journey
) ;
$ self - > in_transit - > delete (
uid = > $ uid ,
db = > $ db
) ;
my $ cache_ts = $ now - > clone ;
if ( $ journey - > { real_departure }
=~ m { ^ (?<year> \d{4} ) - ( ? <month> \ d { 2 } ) } x
)
{
$ cache_ts - > set (
year = > $+ { year } ,
month = > $+ { month }
) ;
}
$ self - > journey_stats_cache - > invalidate (
ts = > $ cache_ts ,
db = > $ db ,
uid = > $ uid
2020-10-08 18:04:25 +00:00
) ;
2019-11-22 16:59:50 +00:00
}
2023-07-23 18:18:10 +00:00
elsif ( defined $ train
and $ train - > arrival_is_cancelled )
{
2019-04-23 16:08:07 +00:00
2023-07-23 18:18:10 +00:00
# This branch is only taken if the deparure was not cancelled,
# i.e., if the train was supposed to go here but got
# redirected or cancelled on the way and not from the start on.
# If the departure itself was cancelled, the user route is
# cancelled_from action -> 'cancelled journey' panel on main page
# -> cancelled_to action -> force checkout (causing the
# previous branch to be taken due to $force)
$ journey - > { cancelled } = 1 ;
$ self - > journeys - > add_from_in_transit (
db = > $ db ,
journey = > $ journey
) ;
$ self - > in_transit - > set_cancelled_destination (
uid = > $ uid ,
db = > $ db ,
cancelled_destination = > $ train - > station ,
) ;
}
2019-04-23 16:08:07 +00:00
2023-07-23 18:18:10 +00:00
if ( not $ opt { in_transaction } ) {
$ tx - > commit ;
}
} ;
2019-04-23 16:08:07 +00:00
2023-07-23 18:18:10 +00:00
if ( $@ ) {
$ self - > app - > log - > error ( "Checkout($uid): $@" ) ;
$ promise - > resolve ( 1 , 'Checkout error: ' . $@ ) ;
return ;
2019-04-23 16:08:07 +00:00
}
2023-07-23 18:18:10 +00:00
if ( $ has_arrived or $ force ) {
if ( not $ opt { in_transaction } ) {
$ self - > run_hook ( $ uid , 'checkout' ) ;
}
$ promise - > resolve ( 0 , undef ) ;
return ;
}
if ( not $ opt { in_transaction } ) {
$ self - > run_hook ( $ uid , 'update' ) ;
$ self - > add_route_timestamps ( $ uid , $ train , 0 , 1 ) ;
}
$ promise - > resolve ( 1 , undef ) ;
return ;
2019-04-23 16:08:07 +00:00
2020-09-30 17:12:29 +00:00
}
2023-07-23 18:18:10 +00:00
) - > catch (
sub {
my ( $ err ) = @ _ ;
$ promise - > resolve ( 1 , $ err ) ;
return ;
}
) - > wait ;
return $ promise ;
2019-03-22 15:57:24 +00:00
}
2019-03-21 21:12:45 +00:00
) ;
2023-08-13 10:51:15 +00:00
$ self - > helper (
'_checkout_hafas_p' = > sub {
my ( $ self , % opt ) = @ _ ;
my $ station = $ opt { station } ;
my $ force = $ opt { force } ;
my $ uid = $ opt { uid } // $ self - > current_user - > { id } ;
my $ db = $ opt { db } // $ self - > pg - > db ;
my $ promise = Mojo::Promise - > new ;
my $ now = DateTime - > now ( time_zone = > 'Europe/Berlin' ) ;
my $ journey = $ self - > in_transit - > get (
uid = > $ uid ,
with_data = > 1 ,
with_timestamps = > 1 ,
with_visibility = > 1 ,
postprocess = > 1 ,
) ;
# with_visibility needed due to postprocess
my $ found ;
my $ has_arrived ;
for my $ stop ( @ { $ journey - > { route_after } } ) {
if ( $ station eq $ stop - > [ 0 ] or $ station eq $ stop - > [ 1 ] ) {
$ found = 1 ;
$ self - > in_transit - > set_arrival_eva (
uid = > $ uid ,
db = > $ db ,
arrival_eva = > $ stop - > [ 1 ] ,
) ;
if ( defined $ journey - > { checkout_station_id }
and $ journey - > { checkout_station_id } != $ stop - > { eva } )
{
$ self - > in_transit - > unset_arrival_data (
uid = > $ uid ,
db = > $ db
) ;
}
$ self - > in_transit - > set_arrival_times (
uid = > $ uid ,
db = > $ db ,
sched_arrival = > $ stop - > [ 2 ] { sched_arr } ,
rt_arrival = >
( $ stop - > [ 2 ] { rt_arr } || $ stop - > [ 2 ] { sched_arr } )
) ;
if (
$ now > ( $ stop - > [ 2 ] { rt_arr } || $ stop - > [ 2 ] { sched_arr } ) )
{
$ has_arrived = 1 ;
}
last ;
}
}
if ( not $ found ) {
return $ promise - > resolve ( 1 , 'station not found in route' ) ;
}
eval {
my $ tx ;
if ( not $ opt { in_transaction } ) {
$ tx = $ db - > begin ;
}
if ( $ has_arrived or $ force ) {
$ journey = $ self - > in_transit - > get (
uid = > $ uid ,
db = > $ db
) ;
$ self - > journeys - > add_from_in_transit (
db = > $ db ,
journey = > $ journey
) ;
$ self - > in_transit - > delete (
uid = > $ uid ,
db = > $ db
) ;
my $ cache_ts = $ now - > clone ;
if ( $ journey - > { real_departure }
=~ m { ^ (?<year> \d{4} ) - ( ? <month> \ d { 2 } ) } x )
{
$ cache_ts - > set (
year = > $+ { year } ,
month = > $+ { month }
) ;
}
$ self - > journey_stats_cache - > invalidate (
ts = > $ cache_ts ,
db = > $ db ,
uid = > $ uid
) ;
}
$ tx - > commit ;
} ;
if ( $@ ) {
$ self - > app - > log - > error ( "Checkout($uid): $@" ) ;
return $ promise - > resolve ( 1 , 'Checkout error: ' . $@ ) ;
}
if ( $ has_arrived or $ force ) {
if ( not $ opt { in_transaction } ) {
$ self - > run_hook ( $ uid , 'checkout' ) ;
}
return $ promise - > resolve ( 0 , undef ) ;
}
if ( not $ opt { in_transaction } ) {
$ self - > run_hook ( $ uid , 'update' ) ;
}
return $ promise - > resolve ( 1 , undef ) ;
}
) ;
2019-03-22 15:57:24 +00:00
# This helper should only be called directly when also providing a user ID.
# If you don't have one, use current_user() instead (get_user_data will
# delegate to it anyways).
$ self - > helper (
'get_user_data' = > sub {
my ( $ self , $ uid ) = @ _ ;
2019-03-21 21:12:45 +00:00
2019-03-22 15:57:24 +00:00
$ uid // = $ self - > current_user - > { id } ;
2019-04-22 07:58:39 +00:00
2023-01-22 11:44:19 +00:00
return $ self - > users - > get ( uid = > $ uid ) ;
2019-03-21 21:12:45 +00:00
}
2019-03-22 15:57:24 +00:00
) ;
2019-05-05 16:09:11 +00:00
$ self - > helper (
'run_hook' = > sub {
2019-05-14 18:05:45 +00:00
my ( $ self , $ uid , $ reason , $ callback ) = @ _ ;
2019-05-05 16:09:11 +00:00
2022-02-20 16:35:30 +00:00
my $ hook = $ self - > users - > get_webhook ( uid = > $ uid ) ;
2019-05-05 16:09:11 +00:00
if ( not $ hook - > { enabled } or not $ hook - > { url } =~ m {^ https?:// }x )
{
2019-05-14 18:05:45 +00:00
if ( $ callback ) {
& $ callback ( ) ;
}
2019-05-05 16:09:11 +00:00
return ;
}
2023-05-21 17:44:43 +00:00
my $ status = $ self - > get_user_status_json_v1 ( uid = > $ uid ) ;
2019-05-05 16:09:11 +00:00
my $ header = { } ;
my $ hook_body = {
reason = > $ reason ,
status = > $ status ,
} ;
if ( $ hook - > { token } ) {
$ header - > { Authorization } = "Bearer $hook->{token}" ;
2019-12-29 22:06:51 +00:00
$ header - > { 'User-Agent' }
= 'travelynx/' . $ self - > app - > config - > { version } ;
2019-05-05 16:09:11 +00:00
}
my $ ua = $ self - > ua ;
2019-05-14 18:05:45 +00:00
if ( $ callback ) {
$ ua - > request_timeout ( 4 ) ;
}
else {
$ ua - > request_timeout ( 10 ) ;
}
2019-05-05 16:09:11 +00:00
$ ua - > post_p ( $ hook - > { url } = > $ header = > json = > $ hook_body ) - > then (
sub {
my ( $ tx ) = @ _ ;
if ( my $ err = $ tx - > error ) {
2022-02-20 16:35:30 +00:00
$ self - > users - > update_webhook_status (
uid = > $ uid ,
url = > $ hook - > { url } ,
success = > 0 ,
text = > "HTTP $err->{code} $err->{message}"
) ;
2019-05-05 16:09:11 +00:00
}
else {
2022-02-20 16:35:30 +00:00
$ self - > users - > update_webhook_status (
uid = > $ uid ,
url = > $ hook - > { url } ,
success = > 1 ,
text = > $ tx - > result - > body
) ;
2019-05-05 16:09:11 +00:00
}
2019-05-14 18:05:45 +00:00
if ( $ callback ) {
& $ callback ( ) ;
}
2020-09-20 08:01:03 +00:00
return ;
2019-05-05 16:09:11 +00:00
}
) - > catch (
sub {
my ( $ err ) = @ _ ;
2022-02-20 16:35:30 +00:00
$ self - > users - > update_webhook_status (
uid = > $ uid ,
url = > $ hook - > { url } ,
success = > 0 ,
text = > $ err
) ;
2019-05-14 18:05:45 +00:00
if ( $ callback ) {
& $ callback ( ) ;
}
2020-09-20 08:01:03 +00:00
return ;
2019-05-05 16:09:11 +00:00
}
) - > wait ;
}
) ;
2019-05-31 18:18:22 +00:00
$ self - > helper (
'add_route_timestamps' = > sub {
2023-04-01 16:05:32 +00:00
my ( $ self , $ uid , $ train , $ is_departure , $ update_polyline ) = @ _ ;
2019-05-31 18:18:22 +00:00
$ uid // = $ self - > current_user - > { id } ;
my $ db = $ self - > pg - > db ;
2023-07-15 14:11:21 +00:00
# TODO "with_timestamps" is misleading, there are more differences between in_transit and in_transit_str
# Here it's only needed because of dep_eva / arr_eva names
2023-04-01 16:05:32 +00:00
my $ in_transit = $ self - > in_transit - > get (
2020-10-11 17:38:01 +00:00
db = > $ db ,
uid = > $ uid ,
with_data = > 1 ,
with_timestamps = > 1
) ;
2019-05-31 18:18:22 +00:00
2023-04-01 16:05:32 +00:00
if ( not $ in_transit ) {
2019-05-31 18:18:22 +00:00
return ;
}
2019-06-22 15:02:04 +00:00
my ( $ platform ) = ( ( $ train - > platform // 0 ) =~ m {(\d+)} ) ;
2023-04-01 16:05:32 +00:00
my $ route = $ in_transit - > { route } ;
2019-05-31 18:18:22 +00:00
my $ base
= 'https://reiseauskunft.bahn.de/bin/trainsearch.exe/dn?L=vs_json.vs_hap&start=yes&rt=1' ;
my $ date_yy = $ train - > start - > strftime ( '%d.%m.%y' ) ;
my $ date_yyyy = $ train - > start - > strftime ( '%d.%m.%Y' ) ;
my $ train_no = $ train - > type . ' ' . $ train - > train_no ;
2020-07-28 11:01:44 +00:00
$ self - > hafas - > get_json_p (
2019-05-31 18:18:22 +00:00
"${base}&date=${date_yy}&trainname=${train_no}" ) - > then (
sub {
my ( $ trainsearch ) = @ _ ;
# Fallback: Take first result
2020-10-11 10:04:33 +00:00
my $ result = $ trainsearch - > { suggestions } [ 0 ] ;
2019-05-31 18:18:22 +00:00
# Try finding a result for the current date
for
my $ suggestion ( @ { $ trainsearch - > { suggestions } // [] } )
{
2023-07-15 14:11:21 +00:00
# Drunken API, sail with care. Both date formats are used interchangeably
2020-02-01 09:59:46 +00:00
if (
$ suggestion - > { depDate }
and ( $ suggestion - > { depDate } eq $ date_yy
or $ suggestion - > { depDate } eq $ date_yyyy )
)
2019-05-31 18:18:22 +00:00
{
2023-07-15 14:11:21 +00:00
# Train numbers are not unique, e.g. IC 149 refers both to the
# InterCity service Amsterdam -> Berlin and to the InterCity service
# Koebenhavns Lufthavn st -> Aarhus. One workaround is making
# requests with the stationFilter=80 parameter. Checking the origin
# station seems to be the more generic solution, so we do that
# instead.
2019-07-14 15:17:44 +00:00
if ( $ suggestion - > { dep } eq $ train - > origin ) {
2022-11-05 18:19:52 +00:00
$ result = $ suggestion ;
2019-07-14 15:17:44 +00:00
last ;
}
2019-05-31 18:18:22 +00:00
}
}
2022-11-05 18:19:52 +00:00
if ( not $ result ) {
2019-05-31 18:18:22 +00:00
$ self - > app - > log - > debug ( "trainlink not found" ) ;
return Mojo::Promise - > reject ( "trainlink not found" ) ;
}
2020-10-11 10:04:33 +00:00
2023-07-15 14:11:21 +00:00
# Calculate and store trip_id.
# The trip_id's date part doesn't seem to matter -- so far,
# HAFAS is happy as long as the date part starts with a number.
# HAFAS-internal tripIDs use this format (withouth leading zero
# for day of month < 10) though, so let's stick with it.
2020-10-11 10:04:33 +00:00
my $ date_map = $ date_yyyy ;
$ date_map =~ tr { . } { } d ;
2020-10-11 17:38:01 +00:00
my $ trip_id = sprintf ( '1|%d|%d|%d|%s' ,
2020-10-11 10:04:33 +00:00
$ result - > { id } , $ result - > { cycle } ,
$ result - > { pool } , $ date_map ) ;
2020-10-11 17:38:01 +00:00
$ self - > in_transit - > update_data (
uid = > $ uid ,
db = > $ db ,
data = > { trip_id = > $ trip_id }
2020-10-11 10:04:33 +00:00
) ;
2022-11-05 18:19:52 +00:00
return $ self - > hafas - > get_route_timestamps_p (
2022-11-05 21:01:51 +00:00
train = > $ train ,
trip_id = > $ trip_id ,
2023-04-01 16:05:32 +00:00
with_polyline = > (
$ update_polyline
or not $ in_transit - > { polyline }
) ? 1 : 0 ,
2022-11-05 21:01:51 +00:00
) ;
2019-05-31 18:18:22 +00:00
}
) - > then (
sub {
2022-11-05 21:01:51 +00:00
my ( $ route_data , $ journey , $ polyline ) = @ _ ;
2019-05-31 18:18:22 +00:00
for my $ station ( @ { $ route } ) {
2022-12-12 17:20:40 +00:00
if ( $ station - > [ 0 ]
=~ m {^Betriebsstelle nicht bekannt (\d+)$} )
{
my $ eva = $ 1 ;
if ( $ route_data - > { $ eva } ) {
$ station - > [ 0 ] = $ route_data - > { $ eva } { name } ;
2023-01-15 15:37:32 +00:00
$ station - > [ 1 ] = $ route_data - > { $ eva } { eva } ;
2022-12-12 17:20:40 +00:00
}
}
2023-01-15 15:37:32 +00:00
if ( my $ sd = $ route_data - > { $ station - > [ 0 ] } ) {
$ station - > [ 1 ] = $ sd - > { eva } ;
if ( $ station - > [ 2 ] { isAdditional } ) {
$ sd - > { isAdditional } = 1 ;
}
if ( $ station - > [ 2 ] { isCancelled } ) {
$ sd - > { isCancelled } = 1 ;
}
2023-04-30 05:42:48 +00:00
# keep rt_dep / rt_arr if they are no longer present
my % old ;
for my $ k ( qw( rt_arr rt_dep arr_delay dep_delay ) ) {
$ old { $ k } = $ station - > [ 2 ] { $ k } ;
}
2023-01-15 15:37:32 +00:00
$ station - > [ 2 ] = $ sd ;
2023-05-01 09:38:11 +00:00
if ( not $ station - > [ 2 ] { rt_arr } ) {
$ station - > [ 2 ] { rt_arr } = $ old { rt_arr } ;
$ station - > [ 2 ] { arr_delay } = $ old { arr_delay } ;
}
if ( not $ station - > [ 2 ] { rt_dep } ) {
$ station - > [ 2 ] { rt_dep } = $ old { rt_dep } ;
$ station - > [ 2 ] { dep_delay } = $ old { dep_delay } ;
2023-04-30 05:42:48 +00:00
}
2023-01-15 15:37:32 +00:00
}
2019-05-31 18:18:22 +00:00
}
2019-06-04 19:12:36 +00:00
2022-11-05 18:19:52 +00:00
my @ messages ;
for my $ m ( $ journey - > messages ) {
2022-11-10 16:46:43 +00:00
if ( not $ m - > code ) {
push (
@ messages ,
{
header = > $ m - > short ,
lead = > $ m - > text ,
}
) ;
}
2022-11-05 18:19:52 +00:00
}
2020-10-11 17:38:01 +00:00
$ self - > in_transit - > set_route_data (
uid = > $ uid ,
db = > $ db ,
route = > $ route ,
delay_messages = > [
map { [ $ _ - > [ 0 ] - > epoch , $ _ - > [ 1 ] ] }
$ train - > delay_messages
] ,
qos_messages = > [
map { [ $ _ - > [ 0 ] - > epoch , $ _ - > [ 1 ] ] }
$ train - > qos_messages
] ,
2022-11-05 18:19:52 +00:00
him_messages = > \ @ messages ,
2019-05-31 18:18:22 +00:00
) ;
2022-11-05 21:01:51 +00:00
if ( $ polyline ) {
my $ coords = $ polyline - > { coords } ;
my $ from_eva = $ polyline - > { from_eva } ;
my $ to_eva = $ polyline - > { to_eva } ;
my $ polyline_str = JSON - > new - > encode ( $ coords ) ;
my $ pl_res = $ db - > select (
'polylines' ,
[ 'id' ] ,
{
origin_eva = > $ from_eva ,
destination_eva = > $ to_eva ,
polyline = > $ polyline_str
} ,
{ limit = > 1 }
) ;
my $ polyline_id ;
if ( my $ h = $ pl_res - > hash ) {
$ polyline_id = $ h - > { id } ;
}
else {
eval {
$ polyline_id = $ db - > insert (
'polylines' ,
{
origin_eva = > $ from_eva ,
destination_eva = > $ to_eva ,
polyline = > $ polyline_str
} ,
{ returning = > 'id' }
) - > hash - > { id } ;
} ;
if ( $@ ) {
$ self - > app - > log - > warn (
"add_route_timestamps: insert polyline: $@"
) ;
}
}
2023-04-01 16:05:32 +00:00
if (
$ polyline_id
and ( not $ in_transit - > { polyline_id }
or $ polyline_id != $ in_transit - > { polyline_id } )
)
{
2022-11-05 21:01:51 +00:00
$ self - > in_transit - > set_polyline_id (
uid = > $ uid ,
db = > $ db ,
polyline_id = > $ polyline_id
) ;
}
}
2020-09-20 08:01:03 +00:00
return ;
2019-05-31 18:18:22 +00:00
}
2020-09-10 19:20:26 +00:00
) - > catch (
sub {
my ( $ err ) = @ _ ;
2022-12-23 20:22:03 +00:00
$ self - > app - > log - > debug ( "add_route_timestamps: $err" ) ;
2020-09-20 08:01:03 +00:00
return ;
2020-09-10 19:20:26 +00:00
}
2019-05-31 18:18:22 +00:00
) - > wait ;
2019-06-22 15:02:04 +00:00
2019-12-11 20:36:41 +00:00
if ( $ train - > sched_departure ) {
2020-09-06 10:41:36 +00:00
$ self - > dbdb - > has_wagonorder_p ( $ train - > sched_departure ,
2019-06-22 15:02:04 +00:00
$ train - > train_no ) - > then (
2019-12-11 20:36:41 +00:00
sub {
2021-02-06 11:31:35 +00:00
my ( $ api ) = @ _ ;
return $ self - > dbdb - > get_wagonorder_p ( $ api ,
2020-09-06 10:41:36 +00:00
$ train - > sched_departure , $ train - > train_no ) ;
2019-12-11 20:36:41 +00:00
}
) - > then (
2019-06-22 15:02:04 +00:00
sub {
my ( $ wagonorder ) = @ _ ;
2020-10-11 17:38:01 +00:00
my $ data = { } ;
my $ user_data = { } ;
2019-06-22 15:02:04 +00:00
2020-01-15 20:48:50 +00:00
if ( $ is_departure and not exists $ wagonorder - > { error } )
{
2020-10-11 17:38:01 +00:00
$ data - > { wagonorder_dep } = $ wagonorder ;
$ user_data - > { wagongroups } = [] ;
2019-12-12 18:21:39 +00:00
for my $ group (
@ {
$ wagonorder - > { data } { istformation }
{ allFahrzeuggruppe } // []
}
)
{
2019-12-19 19:16:10 +00:00
my @ wagons ;
2019-12-12 18:21:39 +00:00
for
my $ wagon ( @ { $ group - > { allFahrzeug } // [] } )
{
push (
2019-12-19 19:16:10 +00:00
@ wagons ,
2019-12-12 18:21:39 +00:00
{
2021-12-12 16:13:51 +00:00
id = > $ wagon - > { fahrzeugnummer } ,
2019-12-12 18:21:39 +00:00
number = >
$ wagon - > { wagenordnungsnummer } ,
type = > $ wagon - > { fahrzeugtyp } ,
}
) ;
}
2019-12-19 19:16:10 +00:00
push (
@ { $ user_data - > { wagongroups } } ,
{
name = >
$ group - > { fahrzeuggruppebezeichnung } ,
from = >
$ group - > { startbetriebsstellename } ,
to = > $ group - > { zielbetriebsstellename } ,
no = > $ group - > { verkehrlichezugnummer } ,
wagons = > [ @ wagons ] ,
}
) ;
2023-05-18 06:25:19 +00:00
if ( $ group - > { fahrzeuggruppebezeichnung }
and $ group - > { fahrzeuggruppebezeichnung } eq
'ICE0304' )
{
$ data - > { wagonorder_pride } = 1 ;
}
2019-12-12 18:21:39 +00:00
}
2020-10-11 17:38:01 +00:00
$ self - > in_transit - > update_data (
uid = > $ uid ,
db = > $ db ,
data = > $ data
) ;
$ self - > in_transit - > update_user_data (
uid = > $ uid ,
db = > $ db ,
user_data = > $ user_data
2019-12-12 18:21:39 +00:00
) ;
2019-06-22 15:02:04 +00:00
}
2020-01-15 20:48:50 +00:00
elsif ( not $ is_departure
and not exists $ wagonorder - > { error } )
{
2019-06-22 15:02:04 +00:00
$ data - > { wagonorder_arr } = $ wagonorder ;
2020-10-11 17:38:01 +00:00
$ self - > in_transit - > update_data (
uid = > $ uid ,
db = > $ db ,
data = > $ data
2019-12-12 18:21:39 +00:00
) ;
2019-06-22 15:02:04 +00:00
}
2020-09-20 08:01:03 +00:00
return ;
2019-06-22 15:02:04 +00:00
}
2020-09-10 19:20:26 +00:00
) - > catch (
sub {
# no wagonorder? no problem.
return ;
}
2019-06-22 15:02:04 +00:00
) - > wait ;
}
2019-08-03 10:35:47 +00:00
if ( $ is_departure ) {
2023-04-01 16:05:32 +00:00
$ self - > dbdb - > get_stationinfo_p ( $ in_transit - > { dep_eva } ) - > then (
2019-08-03 10:35:47 +00:00
sub {
my ( $ station_info ) = @ _ ;
2020-10-11 17:38:01 +00:00
my $ data = { stationinfo_dep = > $ station_info } ;
2019-08-03 10:35:47 +00:00
2020-10-11 17:38:01 +00:00
$ self - > in_transit - > update_data (
uid = > $ uid ,
db = > $ db ,
data = > $ data
2019-08-03 10:35:47 +00:00
) ;
2020-09-20 08:01:03 +00:00
return ;
2019-08-03 10:35:47 +00:00
}
2020-09-10 19:20:26 +00:00
) - > catch (
sub {
# no stationinfo? no problem.
return ;
}
2019-08-03 10:35:47 +00:00
) - > wait ;
}
2023-04-01 16:05:32 +00:00
if ( $ in_transit - > { arr_eva } and not $ is_departure ) {
$ self - > dbdb - > get_stationinfo_p ( $ in_transit - > { arr_eva } ) - > then (
2019-06-22 15:02:04 +00:00
sub {
my ( $ station_info ) = @ _ ;
2020-10-11 17:38:01 +00:00
my $ data = { stationinfo_arr = > $ station_info } ;
2019-06-22 15:02:04 +00:00
2020-10-11 17:38:01 +00:00
$ self - > in_transit - > update_data (
uid = > $ uid ,
db = > $ db ,
data = > $ data
2019-06-22 15:02:04 +00:00
) ;
2020-09-20 08:01:03 +00:00
return ;
2019-06-22 15:02:04 +00:00
}
2020-09-10 19:20:26 +00:00
) - > catch (
sub {
# no stationinfo? no problem.
return ;
}
2019-06-22 15:02:04 +00:00
) - > wait ;
}
2019-05-31 18:18:22 +00:00
}
) ;
2022-07-11 20:09:26 +00:00
$ self - > helper (
'resolve_sb_template' = > sub {
my ( $ self , $ template , % opt ) = @ _ ;
2023-03-04 09:55:05 +00:00
my $ ret = $ template ;
my $ name = $ opt { name } =~ s{/} {%2F}g r ;
2022-07-11 20:09:26 +00:00
$ ret =~ s{[{]eva[} ] } {$opt{eva} } g ;
2023-03-04 09:55:05 +00:00
$ ret =~ s{[{]name[} ] } {$name}g ;
2022-07-15 20:26:50 +00:00
$ ret =~ s{[{]tt[} ] } {$opt{tt} } g ;
$ ret =~ s{[{]tn[} ] } {$opt{tn} } g ;
2022-07-16 18:34:11 +00:00
$ ret =~ s{[{]id[} ] } {$opt{id} } g ;
2022-07-11 20:09:26 +00:00
return $ ret ;
}
) ;
2019-08-03 09:11:27 +00:00
$ self - > helper (
'stationinfo_to_direction' = > sub {
2019-08-03 10:35:47 +00:00
my ( $ self , $ platform_info , $ wagonorder , $ prev_stop , $ next_stop )
= @ _ ;
2019-08-03 09:11:27 +00:00
if ( $ platform_info - > { kopfgleis } ) {
2019-08-03 10:35:47 +00:00
if ( $ next_stop ) {
return $ platform_info - > { direction } eq 'r' ? 'l' : 'r' ;
}
2019-08-03 09:11:27 +00:00
return $ platform_info - > { direction } ;
}
elsif ( $ prev_stop
and exists $ platform_info - > { direction_from } { $ prev_stop } )
{
return $ platform_info - > { direction_from } { $ prev_stop } ;
}
2019-08-03 10:35:47 +00:00
elsif ( $ next_stop
and exists $ platform_info - > { direction_from } { $ next_stop } )
{
return $ platform_info - > { direction_from } { $ next_stop } eq 'r'
? 'l'
: 'r' ;
}
2019-08-03 09:11:27 +00:00
elsif ( $ wagonorder ) {
my $ wr ;
eval {
$ wr
= Travel::Status::DE::DBWagenreihung - > new (
from_json = > $ wagonorder ) ;
} ;
if ( $ wr
and $ wr - > sections
and defined $ wr - > direction )
{
my $ section_0 = ( $ wr - > sections ) [ 0 ] ;
my $ direction = $ wr - > direction ;
if ( $ section_0 - > name eq 'A'
and $ direction == 0 )
{
return $ platform_info - > { direction } ;
}
elsif ( $ section_0 - > name ne 'A'
and $ direction == 100 )
{
return $ platform_info - > { direction } ;
}
elsif ( $ platform_info - > { direction } ) {
return $ platform_info - > { direction } eq 'r'
? 'l'
: 'r' ;
}
return ;
}
}
}
) ;
2019-10-29 20:18:46 +00:00
$ self - > helper (
'journey_to_ajax_route' = > sub {
my ( $ self , $ journey ) = @ _ ;
my @ route ;
for my $ station ( @ { $ journey - > { route_after } } ) {
my $ station_desc = $ station - > [ 0 ] ;
2023-05-11 16:32:27 +00:00
if ( $ station - > [ 2 ] { sched_arr } and $ station - > [ 2 ] { rt_arr } ) {
2023-01-15 15:37:32 +00:00
$ station_desc . = $ station - > [ 2 ] { sched_arr } - > strftime ( ';%s' ) ;
$ station_desc . = $ station - > [ 2 ] { rt_arr } - > strftime ( ';%s' ) ;
2023-05-11 16:32:27 +00:00
if ( $ station - > [ 2 ] { sched_dep } and $ station - > [ 2 ] { rt_dep } ) {
2019-10-29 20:18:46 +00:00
$ station_desc
2023-01-15 15:37:32 +00:00
. = $ station - > [ 2 ] { sched_dep } - > strftime ( ';%s' ) ;
$ station_desc . = $ station - > [ 2 ] { rt_dep } - > strftime ( ';%s' ) ;
2019-10-29 20:18:46 +00:00
}
else {
$ station_desc . = ';0;0' ;
}
}
else {
$ station_desc . = ';0;0;0;0' ;
}
push ( @ route , $ station_desc ) ;
}
return join ( '|' , @ route ) ;
}
) ;
2019-03-22 15:57:24 +00:00
$ self - > helper (
'get_user_status' = > sub {
2020-09-30 17:12:29 +00:00
my ( $ self , $ uid , $ db ) = @ _ ;
2019-03-21 21:12:45 +00:00
2019-03-22 15:57:24 +00:00
$ uid // = $ self - > current_user - > { id } ;
2020-09-30 17:12:29 +00:00
$ db // = $ self - > pg - > db ;
2019-03-21 21:12:45 +00:00
2019-05-31 18:18:22 +00:00
my $ now = DateTime - > now ( time_zone = > 'Europe/Berlin' ) ;
my $ epoch = $ now - > epoch ;
2019-03-21 21:12:45 +00:00
2020-10-11 17:38:01 +00:00
my $ in_transit = $ self - > in_transit - > get (
uid = > $ uid ,
db = > $ db ,
with_data = > 1 ,
2023-02-27 21:14:54 +00:00
with_timestamps = > 1 ,
with_visibility = > 1 ,
2023-07-15 11:28:54 +00:00
postprocess = > 1 ,
2020-10-11 17:38:01 +00:00
) ;
2019-03-22 15:57:24 +00:00
2019-04-23 16:08:07 +00:00
if ( $ in_transit ) {
2023-07-15 11:28:54 +00:00
my $ ret = $ in_transit ;
2019-04-23 19:30:31 +00:00
2022-02-16 19:56:28 +00:00
my $ traewelling = $ self - > traewelling - > get (
uid = > $ uid ,
db = > $ db
) ;
if ( $ traewelling - > { latest_run }
>= epoch_to_dt ( $ in_transit - > { checkin_ts } ) )
{
$ ret - > { traewelling } = $ traewelling ;
if ( @ { $ traewelling - > { data } { log } // [] }
and ( my $ log_entry = $ traewelling - > { data } { log } [ 0 ] ) )
{
if ( $ log_entry - > [ 2 ] ) {
$ ret - > { traewelling_status } = $ log_entry - > [ 2 ] ;
$ ret - > { traewelling_url }
= 'https://traewelling.de/status/'
. $ log_entry - > [ 2 ] ;
}
$ ret - > { traewelling_log_latest } = $ log_entry - > [ 1 ] ;
}
}
2023-07-15 17:07:21 +00:00
my $ stop_after_dep
= scalar @ { $ ret - > { route_after } }
? $ ret - > { route_after } [ 0 ] [ 0 ]
: undef ;
2023-07-15 17:16:01 +00:00
my $ stop_before_dest ;
for my $ i ( 1 .. $# { $ ret - > { route_after } } ) {
if ( $ ret - > { arr_name }
and $ ret - > { route_after } [ $ i ] [ 0 ] eq $ ret - > { arr_name } )
{
$ stop_before_dest = $ ret - > { route_after } [ $ i - 1 ] [ 0 ] ;
last ;
}
}
2023-07-15 17:07:21 +00:00
my ( $ dep_platform_number )
= ( ( $ ret - > { dep_platform } // 0 ) =~ m {(\d+)} ) ;
if ( $ dep_platform_number
and
exists $ ret - > { data } { stationinfo_dep } { $ dep_platform_number } )
{
$ ret - > { dep_direction } = $ self - > stationinfo_to_direction (
$ ret - > { data } { stationinfo_dep } { $ dep_platform_number } ,
$ ret - > { data } { wagonorder_dep } ,
undef , $ stop_after_dep
) ;
}
my ( $ arr_platform_number )
= ( ( $ ret - > { arr_platform } // 0 ) =~ m {(\d+)} ) ;
if ( $ arr_platform_number
and
exists $ ret - > { data } { stationinfo_arr } { $ arr_platform_number } )
{
$ ret - > { arr_direction } = $ self - > stationinfo_to_direction (
$ ret - > { data } { stationinfo_arr } { $ arr_platform_number } ,
$ ret - > { data } { wagonorder_arr } ,
$ stop_before_dest ,
undef
) ;
}
2019-06-24 16:02:45 +00:00
if ( $ ret - > { departure_countdown } > 0
and $ in_transit - > { data } { wagonorder_dep } )
{
my $ wr ;
eval {
$ wr
= Travel::Status::DE::DBWagenreihung - > new (
from_json = > $ in_transit - > { data } { wagonorder_dep } ) ;
} ;
if ( $ wr
and $ wr - > wagons
and defined $ wr - > direction )
{
$ ret - > { wagonorder } = $ wr ;
}
}
2019-04-23 19:30:31 +00:00
return $ ret ;
2019-03-21 21:12:45 +00:00
}
2019-04-23 16:08:07 +00:00
2020-10-11 19:08:22 +00:00
my ( $ latest , $ latest_cancellation ) = $ self - > journeys - > get_latest (
uid = > $ uid ,
2023-03-04 08:03:50 +00:00
db = > $ db ,
2020-10-11 19:08:22 +00:00
) ;
2020-02-17 20:13:07 +00:00
if ( $ latest_cancellation and $ latest_cancellation - > { cancelled } ) {
2022-07-11 20:09:26 +00:00
if (
2022-12-07 18:42:48 +00:00
my $ station = $ self - > stations - > get_by_eva (
2022-07-11 20:09:26 +00:00
$ latest_cancellation - > { dep_eva }
2022-12-07 18:42:48 +00:00
)
2022-07-11 20:09:26 +00:00
)
2020-02-17 20:13:07 +00:00
{
2022-12-07 18:42:48 +00:00
$ latest_cancellation - > { dep_ds100 } = $ station - > { ds100 } ;
$ latest_cancellation - > { dep_name } = $ station - > { name } ;
2020-02-17 20:13:07 +00:00
}
2022-07-11 20:09:26 +00:00
if (
2022-12-07 18:42:48 +00:00
my $ station = $ self - > stations - > get_by_eva (
2022-07-11 20:09:26 +00:00
$ latest_cancellation - > { arr_eva }
2022-12-07 18:42:48 +00:00
)
2022-07-11 20:09:26 +00:00
)
2020-02-17 20:13:07 +00:00
{
2022-12-07 18:42:48 +00:00
$ latest_cancellation - > { arr_ds100 } = $ station - > { ds100 } ;
$ latest_cancellation - > { arr_name } = $ station - > { name } ;
2020-02-17 20:13:07 +00:00
}
}
else {
$ latest_cancellation = undef ;
}
2019-04-23 16:08:07 +00:00
if ( $ latest ) {
my $ ts = $ latest - > { checkout_ts } ;
my $ action_time = epoch_to_dt ( $ ts ) ;
2019-12-23 21:57:45 +00:00
if ( my $ station
2022-12-07 18:42:48 +00:00
= $ self - > stations - > get_by_eva ( $ latest - > { dep_eva } ) )
2019-12-23 21:57:45 +00:00
{
2022-12-07 18:42:48 +00:00
$ latest - > { dep_ds100 } = $ station - > { ds100 } ;
$ latest - > { dep_name } = $ station - > { name } ;
2019-12-23 21:57:45 +00:00
}
if ( my $ station
2022-12-07 18:42:48 +00:00
= $ self - > stations - > get_by_eva ( $ latest - > { arr_eva } ) )
2019-12-23 21:57:45 +00:00
{
2022-12-07 18:42:48 +00:00
$ latest - > { arr_ds100 } = $ station - > { ds100 } ;
$ latest - > { arr_name } = $ station - > { name } ;
2019-12-23 21:57:45 +00:00
}
2019-04-23 16:08:07 +00:00
return {
checked_in = > 0 ,
cancelled = > 0 ,
2020-02-17 20:13:07 +00:00
cancellation = > $ latest_cancellation ,
2019-04-23 16:08:07 +00:00
journey_id = > $ latest - > { journey_id } ,
timestamp = > $ action_time ,
timestamp_delta = > $ now - > epoch - $ action_time - > epoch ,
train_type = > $ latest - > { train_type } ,
train_line = > $ latest - > { train_line } ,
train_no = > $ latest - > { train_no } ,
train_id = > $ latest - > { train_id } ,
sched_departure = > epoch_to_dt ( $ latest - > { sched_dep_ts } ) ,
real_departure = > epoch_to_dt ( $ latest - > { real_dep_ts } ) ,
dep_ds100 = > $ latest - > { dep_ds100 } ,
2019-12-23 21:57:45 +00:00
dep_eva = > $ latest - > { dep_eva } ,
2019-04-23 16:08:07 +00:00
dep_name = > $ latest - > { dep_name } ,
2023-05-16 18:02:20 +00:00
dep_lat = > $ latest - > { dep_lat } ,
dep_lon = > $ latest - > { dep_lon } ,
2019-05-18 15:10:53 +00:00
dep_platform = > $ latest - > { dep_platform } ,
2019-04-23 16:08:07 +00:00
sched_arrival = > epoch_to_dt ( $ latest - > { sched_arr_ts } ) ,
real_arrival = > epoch_to_dt ( $ latest - > { real_arr_ts } ) ,
arr_ds100 = > $ latest - > { arr_ds100 } ,
2019-12-23 21:57:45 +00:00
arr_eva = > $ latest - > { arr_eva } ,
2019-04-23 16:08:07 +00:00
arr_name = > $ latest - > { arr_name } ,
2023-05-16 18:02:20 +00:00
arr_lat = > $ latest - > { arr_lat } ,
arr_lon = > $ latest - > { arr_lon } ,
2019-05-18 15:10:53 +00:00
arr_platform = > $ latest - > { arr_platform } ,
2019-12-06 20:40:51 +00:00
comment = > $ latest - > { user_data } { comment } ,
2023-03-04 08:03:50 +00:00
visibility = > $ latest - > { visibility } ,
visibility_str = > $ latest - > { visibility_str } ,
2023-06-26 18:53:08 +00:00
effective_visibility = > $ latest - > { effective_visibility } ,
effective_visibility_str = >
$ latest - > { effective_visibility_str } ,
2019-04-23 16:08:07 +00:00
} ;
}
2019-03-22 15:57:24 +00:00
return {
2019-04-23 16:08:07 +00:00
checked_in = > 0 ,
cancelled = > 0 ,
2020-02-17 20:13:07 +00:00
cancellation = > $ latest_cancellation ,
2019-04-23 16:08:07 +00:00
no_journeys_yet = > 1 ,
timestamp = > epoch_to_dt ( 0 ) ,
timestamp_delta = > $ now - > epoch ,
2019-03-22 15:57:24 +00:00
} ;
2019-03-21 21:12:45 +00:00
}
2019-03-22 15:57:24 +00:00
) ;
2019-03-21 21:12:45 +00:00
2019-05-06 14:59:48 +00:00
$ self - > helper (
'get_user_status_json_v1' = > sub {
2023-05-21 17:44:43 +00:00
my ( $ self , % opt ) = @ _ ;
2023-05-24 19:41:51 +00:00
my $ uid = $ opt { uid } ;
my $ privacy = $ opt { privacy }
// $ self - > users - > get_privacy_by ( uid = > $ uid ) ;
2023-05-21 17:57:56 +00:00
my $ status = $ opt { status } // $ self - > get_user_status ( $ uid ) ;
2019-05-06 14:59:48 +00:00
my $ ret = {
deprecated = > \ 0 ,
checkedIn = > (
$ status - > { checked_in }
or $ status - > { cancelled }
) ? \ 1 : \ 0 ,
2023-05-29 12:50:04 +00:00
comment = > $ status - > { comment } ,
2019-05-06 14:59:48 +00:00
fromStation = > {
ds100 = > $ status - > { dep_ds100 } ,
name = > $ status - > { dep_name } ,
2019-12-23 21:57:45 +00:00
uic = > $ status - > { dep_eva } ,
2023-05-16 18:02:20 +00:00
longitude = > $ status - > { dep_lon } ,
latitude = > $ status - > { dep_lat } ,
2020-01-27 19:22:46 +00:00
scheduledTime = > $ status - > { sched_departure }
? $ status - > { sched_departure } - > epoch
: undef ,
realTime = > $ status - > { real_departure }
? $ status - > { real_departure } - > epoch
: undef ,
2019-05-06 14:59:48 +00:00
} ,
toStation = > {
ds100 = > $ status - > { arr_ds100 } ,
name = > $ status - > { arr_name } ,
2019-12-23 21:57:45 +00:00
uic = > $ status - > { arr_eva } ,
2023-05-16 18:02:20 +00:00
longitude = > $ status - > { arr_lon } ,
latitude = > $ status - > { arr_lat } ,
2020-01-27 19:22:46 +00:00
scheduledTime = > $ status - > { sched_arrival }
? $ status - > { sched_arrival } - > epoch
: undef ,
realTime = > $ status - > { real_arrival }
? $ status - > { real_arrival } - > epoch
: undef ,
2019-05-06 14:59:48 +00:00
} ,
train = > {
type = > $ status - > { train_type } ,
line = > $ status - > { train_line } ,
no = > $ status - > { train_no } ,
id = > $ status - > { train_id } ,
} ,
2020-01-26 08:59:45 +00:00
intermediateStops = > [] ,
2023-05-24 19:41:51 +00:00
visibility = > {
2023-06-26 18:53:08 +00:00
level = > $ status - > { effective_visibility } ,
desc = > $ status - > { effective_visibility_str } ,
2023-05-24 19:41:51 +00:00
}
2019-05-06 14:59:48 +00:00
} ;
2023-05-29 12:50:04 +00:00
if ( $ opt { public } ) {
if ( not $ privacy - > { comments_visible } ) {
delete $ ret - > { comment } ;
}
}
else {
2023-05-21 17:44:43 +00:00
$ ret - > { actionTime }
= $ status - > { timestamp }
? $ status - > { timestamp } - > epoch
: undef ;
}
2020-01-23 17:18:50 +00:00
for my $ stop ( @ { $ status - > { route_after } // [] } ) {
if ( $ status - > { arr_name } and $ stop - > [ 0 ] eq $ status - > { arr_name } )
{
last ;
}
push (
2020-01-26 08:59:45 +00:00
@ { $ ret - > { intermediateStops } } ,
2020-01-23 17:18:50 +00:00
{
name = > $ stop - > [ 0 ] ,
2023-01-15 15:37:32 +00:00
scheduledArrival = > $ stop - > [ 2 ] { sched_arr }
? $ stop - > [ 2 ] { sched_arr } - > epoch
2020-01-23 17:18:50 +00:00
: undef ,
2023-01-15 15:37:32 +00:00
realArrival = > $ stop - > [ 2 ] { rt_arr }
? $ stop - > [ 2 ] { rt_arr } - > epoch
2020-01-23 17:18:50 +00:00
: undef ,
2023-01-15 15:37:32 +00:00
scheduledDeparture = > $ stop - > [ 2 ] { sched_dep }
? $ stop - > [ 2 ] { sched_dep } - > epoch
2020-01-23 17:18:50 +00:00
: undef ,
2023-01-15 15:37:32 +00:00
realDeparture = > $ stop - > [ 2 ] { rt_dep }
? $ stop - > [ 2 ] { rt_dep } - > epoch
2020-01-23 17:18:50 +00:00
: undef ,
}
) ;
}
2019-05-06 14:59:48 +00:00
return $ ret ;
}
) ;
2020-09-30 17:12:29 +00:00
$ self - > helper (
2023-07-09 12:46:28 +00:00
'traewelling_to_travelynx_p' = > sub {
2020-09-30 17:12:29 +00:00
my ( $ self , % opt ) = @ _ ;
my $ traewelling = $ opt { traewelling } ;
my $ user_data = $ opt { user_data } ;
my $ uid = $ user_data - > { user_id } ;
2023-07-09 12:46:28 +00:00
my $ promise = Mojo::Promise - > new ;
2020-09-30 17:12:29 +00:00
if ( not $ traewelling - > { checkin }
or $ self - > now - > epoch - $ traewelling - > { checkin } - > epoch > 900 )
{
$ self - > log - > debug ( "... not checked in" ) ;
2023-07-09 12:46:28 +00:00
return $ promise - > resolve ;
2020-09-30 17:12:29 +00:00
}
if ( $ traewelling - > { status_id }
and $ user_data - > { data } { latest_pull_status_id }
and $ traewelling - > { status_id }
== $ user_data - > { data } { latest_pull_status_id } )
{
$ self - > log - > debug ( "... already handled" ) ;
2023-07-09 12:46:28 +00:00
return $ promise - > resolve ;
2020-09-30 17:12:29 +00:00
}
2022-08-17 19:51:24 +00:00
$ self - > log - > debug (
"... checked in : $traewelling->{dep_name} $traewelling->{dep_eva} -> $traewelling->{arr_name} $traewelling->{arr_eva}"
) ;
2020-09-30 17:12:29 +00:00
my $ user_status = $ self - > get_user_status ( $ uid ) ;
if ( $ user_status - > { checked_in } ) {
$ self - > log - > debug (
"... also checked in via travelynx. aborting." ) ;
2023-07-09 12:46:28 +00:00
return $ promise - > resolve ;
2020-09-30 17:12:29 +00:00
}
if ( $ traewelling - > { category }
2020-10-04 11:08:45 +00:00
!~ m {^ (?: national .* | regional .* | suburban ) $ }x )
2020-09-30 17:12:29 +00:00
{
2020-10-04 11:04:52 +00:00
$ self - > log - > debug (
"... status is not a train, but $traewelling->{category}" ) ;
2020-09-30 17:12:29 +00:00
$ self - > traewelling - > log (
2021-12-12 16:13:51 +00:00
uid = > $ uid ,
2020-09-30 17:12:29 +00:00
message = >
2022-02-17 20:40:47 +00:00
"$traewelling->{line} nach $traewelling->{arr_name} ist keine Zugfahrt (HAFAS-Kategorie '$traewelling->{category}')" ,
2020-09-30 17:12:29 +00:00
status_id = > $ traewelling - > { status_id } ,
) ;
$ self - > traewelling - > set_latest_pull_status_id (
uid = > $ uid ,
status_id = > $ traewelling - > { status_id }
) ;
2023-07-09 12:46:28 +00:00
return $ promise - > resolve ;
2020-09-30 17:12:29 +00:00
}
2023-07-09 12:46:28 +00:00
$ self - > iris - > get_departures_p (
2020-09-30 17:12:29 +00:00
station = > $ traewelling - > { dep_eva } ,
lookbehind = > 60 ,
lookahead = > 40
2023-07-09 12:46:28 +00:00
) - > then (
sub {
my ( $ dep ) = @ _ ;
my ( $ train_ref , $ train_id ) ;
if ( $ dep - > { errstr } ) {
$ self - > traewelling - > log (
uid = > $ uid ,
message = >
2022-02-17 20:40:47 +00:00
"Konnte $traewelling->{line} nach $traewelling->{arr_name} nicht übernehmen: $dep->{errstr}" ,
2023-07-09 12:46:28 +00:00
status_id = > $ traewelling - > { status_id } ,
is_error = > 1 ,
) ;
$ promise - > resolve ;
return ;
}
2020-09-30 17:12:29 +00:00
2023-07-09 12:46:28 +00:00
for my $ train ( @ { $ dep - > { results } } ) {
if ( $ train - > line ne $ traewelling - > { line } ) {
next ;
}
if ( not $ train - > sched_departure
or $ train - > sched_departure - > epoch
!= $ traewelling - > { dep_dt } - > epoch )
{
next ;
}
if (
not
List::Util:: first { $ _ eq $ traewelling - > { arr_name } }
$ train - > route_post
)
{
next ;
}
$ train_id = $ train - > train_id ;
$ train_ref = $ train ;
last ;
}
2020-09-30 17:12:29 +00:00
2023-07-09 12:46:28 +00:00
if ( not $ train_id ) {
$ self - > log - > debug (
"... train $traewelling->{line} not found" ) ;
$ self - > traewelling - > log (
uid = > $ uid ,
message = >
"Konnte $traewelling->{line} nach $traewelling->{arr_name} nicht übernehmen: Zug nicht gefunden" ,
status_id = > $ traewelling - > { status_id } ,
is_error = > 1
) ;
return $ promise - > resolve ;
}
$ self - > log - > debug ( "... found train: $train_id" ) ;
my $ db = $ self - > pg - > db ;
my $ tx = $ db - > begin ;
2020-09-30 17:12:29 +00:00
2023-07-09 12:46:28 +00:00
$ self - > checkin_p (
station = > $ traewelling - > { dep_eva } ,
train_id = > $ train_id ,
2020-09-30 17:12:29 +00:00
uid = > $ uid ,
in_transaction = > 1 ,
db = > $ db
2023-07-09 12:46:28 +00:00
) - > then (
sub {
$ self - > log - > debug ( "... handled origin" ) ;
2023-07-23 18:18:10 +00:00
return $ self - > checkout_p (
2023-07-09 12:46:28 +00:00
station = > $ traewelling - > { arr_eva } ,
train_id = > 0 ,
uid = > $ uid ,
in_transaction = > 1 ,
db = > $ db
2020-10-11 17:38:01 +00:00
) ;
2023-07-23 18:18:10 +00:00
}
) - > then (
sub {
my ( undef , $ err ) = @ _ ;
2023-07-09 12:46:28 +00:00
if ( $ err ) {
$ self - > log - > debug ( "... error: $err" ) ;
return Mojo::Promise - > reject ( $ err ) ;
}
$ self - > log - > debug ( "... handled destination" ) ;
if ( $ traewelling - > { message } ) {
$ self - > in_transit - > update_user_data (
uid = > $ uid ,
db = > $ db ,
user_data = >
{ comment = > $ traewelling - > { message } }
) ;
}
$ self - > traewelling - > log (
uid = > $ uid ,
db = > $ db ,
message = >
2020-09-30 17:12:29 +00:00
"Eingecheckt in $traewelling->{line} nach $traewelling->{arr_name}" ,
2023-07-09 12:46:28 +00:00
status_id = > $ traewelling - > { status_id } ,
) ;
$ self - > traewelling - > set_latest_pull_status_id (
uid = > $ uid ,
status_id = > $ traewelling - > { status_id } ,
db = > $ db
) ;
2020-09-30 17:12:29 +00:00
2023-07-09 12:46:28 +00:00
$ tx - > commit ;
$ promise - > resolve ;
return ;
}
) - > catch (
sub {
my ( $ err ) = @ _ ;
$ self - > log - > debug ( "... error: $err" ) ;
$ self - > traewelling - > log (
uid = > $ uid ,
message = >
"Konnte $traewelling->{line} nach $traewelling->{arr_name} nicht übernehmen: $err" ,
status_id = > $ traewelling - > { status_id } ,
is_error = > 1
) ;
$ promise - > resolve ;
return ;
}
) - > wait ;
2020-09-30 17:12:29 +00:00
}
2023-07-09 12:46:28 +00:00
) - > catch (
sub {
2023-07-09 15:41:47 +00:00
my ( $ err , $ dep ) = @ _ ;
2020-09-30 17:12:29 +00:00
$ self - > traewelling - > log (
2021-12-12 16:13:51 +00:00
uid = > $ uid ,
2020-09-30 17:12:29 +00:00
message = >
2023-07-09 12:46:28 +00:00
"Konnte $traewelling->{line} nach $traewelling->{arr_name} nicht übernehmen: $dep->{errstr}" ,
2020-09-30 17:12:29 +00:00
status_id = > $ traewelling - > { status_id } ,
2023-07-09 12:46:28 +00:00
is_error = > 1 ,
2020-09-30 17:12:29 +00:00
) ;
2023-07-09 12:46:28 +00:00
$ promise - > resolve ;
return ;
2020-09-30 17:12:29 +00:00
}
2023-07-09 12:46:28 +00:00
) - > wait ;
return $ promise ;
2020-09-30 17:12:29 +00:00
}
) ;
2020-02-23 17:35:56 +00:00
$ self - > helper (
'journeys_to_map_data' = > sub {
my ( $ self , % opt ) = @ _ ;
my @ journeys = @ { $ opt { journeys } // [] } ;
my $ route_type = $ opt { route_type } // 'polybee' ;
my $ include_manual = $ opt { include_manual } ? 1 : 0 ;
my $ location = $ self - > app - > coordinates_by_station ;
my $ with_polyline = $ route_type eq 'beeline' ? 0 : 1 ;
if ( not @ journeys ) {
return {
skipped_journeys = > [] ,
station_coordinates = > [] ,
polyline_groups = > [] ,
} ;
}
my $ json = JSON - > new - > utf8 ;
my $ first_departure = $ journeys [ - 1 ] - > { rt_departure } ;
my $ last_departure = $ journeys [ 0 ] - > { rt_departure } ;
my @ stations = List::Util:: uniq map { $ _ - > { to_name } } @ journeys ;
push ( @ stations ,
List::Util:: uniq map { $ _ - > { from_name } } @ journeys ) ;
@ stations = List::Util:: uniq @ stations ;
my @ station_coordinates = map { [ $ location - > { $ _ } , $ _ ] }
grep { exists $ location - > { $ _ } } @ stations ;
my @ station_pairs ;
my @ polylines ;
my % seen ;
my @ skipped_journeys ;
my @ polyline_journeys = grep { $ _ - > { polyline } } @ journeys ;
my @ beeline_journeys = grep { not $ _ - > { polyline } } @ journeys ;
if ( $ route_type eq 'polyline' ) {
@ beeline_journeys = ( ) ;
}
elsif ( $ route_type eq 'beeline' ) {
push ( @ beeline_journeys , @ polyline_journeys ) ;
@ polyline_journeys = ( ) ;
}
for my $ journey ( @ polyline_journeys ) {
my @ polyline = @ { $ journey - > { polyline } } ;
my $ from_eva = $ journey - > { from_eva } ;
my $ to_eva = $ journey - > { to_eva } ;
my $ from_index
= first_index { $ _ - > [ 2 ] and $ _ - > [ 2 ] == $ from_eva } @ polyline ;
my $ to_index
= first_index { $ _ - > [ 2 ] and $ _ - > [ 2 ] == $ to_eva } @ polyline ;
if ( $ from_index == - 1
or $ to_index == - 1 )
{
# Fall back to route
delete $ journey - > { polyline } ;
next ;
}
my $ key
= $ from_eva . '!'
. $ to_eva . '!'
. ( $ to_index - $ from_index ) ;
if ( $ seen { $ key } ) {
next ;
}
$ seen { $ key } = 1 ;
# direction does not matter at the moment
$ key
= $ to_eva . '!'
. $ from_eva . '!'
. ( $ to_index - $ from_index ) ;
$ seen { $ key } = 1 ;
@ polyline = @ polyline [ $ from_index .. $ to_index ] ;
my @ polyline_coords ;
for my $ coord ( @ polyline ) {
push ( @ polyline_coords , [ $ coord - > [ 1 ] , $ coord - > [ 0 ] ] ) ;
}
push ( @ polylines , [ @ polyline_coords ] ) ;
}
for my $ journey ( @ beeline_journeys ) {
my @ route = map { $ _ - > [ 0 ] } @ { $ journey - > { route } } ;
my $ from_index
= first_index { $ _ eq $ journey - > { from_name } } @ route ;
my $ to_index = first_index { $ _ eq $ journey - > { to_name } } @ route ;
if ( $ from_index == - 1 ) {
my $ rename = $ self - > app - > renamed_station ;
$ from_index = first_index {
( $ rename - > { $ _ } // $ _ ) eq $ journey - > { from_name }
}
@ route ;
}
if ( $ to_index == - 1 ) {
my $ rename = $ self - > app - > renamed_station ;
$ to_index = first_index {
( $ rename - > { $ _ } // $ _ ) eq $ journey - > { to_name }
}
@ route ;
}
if ( $ from_index == - 1
or $ to_index == - 1 )
{
push ( @ skipped_journeys ,
[ $ journey , 'Start/Ziel nicht in Route gefunden' ] ) ;
next ;
}
2023-07-15 14:11:21 +00:00
# Manual journey entries are only included if one of the following
# conditions is satisfied:
# * their route has more than two elements (-> probably more than just
# start and stop station), or
# * $include_manual is true (-> user wants to see incomplete routes)
# This avoids messing up the map in case an A -> B connection has been
# tracked both with a regular checkin (-> detailed route shown on map)
# and entered manually (-> beeline also shown on map, typically
# significantly differs from detailed route) -- unless the user
# sets include_manual, of course.
2020-02-23 17:35:56 +00:00
if ( $ journey - > { edited } & 0x0010
and @ route <= 2
and not $ include_manual )
{
push ( @ skipped_journeys ,
[ $ journey , 'Manueller Eintrag ohne Unterwegshalte' ] ) ;
next ;
}
@ route = @ route [ $ from_index .. $ to_index ] ;
my $ key = join ( '|' , @ route ) ;
if ( $ seen { $ key } ) {
next ;
}
$ seen { $ key } = 1 ;
# direction does not matter at the moment
$ seen { join ( '|' , reverse @ route ) } = 1 ;
my $ prev_station = shift @ route ;
for my $ station ( @ route ) {
push ( @ station_pairs , [ $ prev_station , $ station ] ) ;
$ prev_station = $ station ;
}
}
@ station_pairs = uniq_by { $ _ - > [ 0 ] . '|' . $ _ - > [ 1 ] } @ station_pairs ;
@ station_pairs = grep {
exists $ location - > { $ _ - > [ 0 ] }
and exists $ location - > { $ _ - > [ 1 ] }
} @ station_pairs ;
@ station_pairs
= map { [ $ location - > { $ _ - > [ 0 ] } , $ location - > { $ _ - > [ 1 ] } ] }
@ station_pairs ;
2020-02-23 18:04:05 +00:00
my $ ret = {
2020-02-23 17:35:56 +00:00
skipped_journeys = > \ @ skipped_journeys ,
station_coordinates = > \ @ station_coordinates ,
polyline_groups = > [
{
polylines = > $ json - > encode ( \ @ station_pairs ) ,
color = > '#673ab7' ,
opacity = > $ with_polyline ? 0.4 : 0.6 ,
} ,
{
polylines = > $ json - > encode ( \ @ polylines ) ,
color = > '#673ab7' ,
opacity = > 0.8 ,
}
] ,
} ;
2020-02-23 18:04:05 +00:00
if ( @ station_coordinates ) {
2021-12-12 16:13:51 +00:00
my @ lats = map { $ _ - > [ 0 ] [ 0 ] } @ station_coordinates ;
my @ lons = map { $ _ - > [ 0 ] [ 1 ] } @ station_coordinates ;
2020-02-23 18:04:05 +00:00
my $ min_lat = List::Util:: min @ lats ;
my $ max_lat = List::Util:: max @ lats ;
my $ min_lon = List::Util:: min @ lons ;
my $ max_lon = List::Util:: max @ lons ;
$ ret - > { bounds }
= [ [ $ min_lat , $ min_lon ] , [ $ max_lat , $ max_lon ] ] ;
}
return $ ret ;
2020-02-23 17:35:56 +00:00
}
) ;
2019-03-22 15:57:24 +00:00
$ self - > helper (
'navbar_class' = > sub {
my ( $ self , $ path ) = @ _ ;
2019-03-21 21:12:45 +00:00
2019-03-22 15:57:24 +00:00
if ( $ self - > req - > url eq $ self - > url_for ( $ path ) ) {
return 'active' ;
}
return q{ } ;
2019-03-21 21:12:45 +00:00
}
2019-03-22 15:57:24 +00:00
) ;
2019-03-21 21:12:45 +00:00
2019-03-22 15:57:24 +00:00
my $ r = $ self - > routes ;
$ r - > get ( '/' ) - > to ( 'traveling#homepage' ) ;
$ r - > get ( '/about' ) - > to ( 'static#about' ) ;
2019-05-02 09:29:43 +00:00
$ r - > get ( '/api' ) - > to ( 'api#documentation' ) ;
2019-05-03 08:32:58 +00:00
$ r - > get ( '/changelog' ) - > to ( 'static#changelog' ) ;
2019-03-22 15:57:24 +00:00
$ r - > get ( '/impressum' ) - > to ( 'static#imprint' ) ;
$ r - > get ( '/imprint' ) - > to ( 'static#imprint' ) ;
2022-09-23 15:35:02 +00:00
$ r - > get ( '/legend' ) - > to ( 'static#legend' ) ;
2022-07-15 17:27:47 +00:00
$ r - > get ( '/offline.html' ) - > to ( 'static#offline' ) ;
2019-04-24 05:34:41 +00:00
$ r - > get ( '/api/v1/:user_action/:token' ) - > to ( 'api#get_v1' ) ;
2019-03-22 15:57:24 +00:00
$ r - > get ( '/login' ) - > to ( 'account#login_form' ) ;
2019-04-29 18:12:59 +00:00
$ r - > get ( '/recover' ) - > to ( 'account#request_password_reset' ) ;
$ r - > get ( '/recover/:id/:token' ) - > to ( 'account#recover_password' ) ;
2019-03-22 15:57:24 +00:00
$ r - > get ( '/reg/:id/:token' ) - > to ( 'account#verify' ) ;
2023-03-13 20:06:53 +00:00
$ r - > get ( '/status/:name' ) - > to ( 'profile#user_status' ) ;
$ r - > get ( '/status/:name/:ts' ) - > to ( 'profile#user_status' ) ;
$ r - > get ( '/ajax/status/#name' ) - > to ( 'profile#status_card' ) ;
$ r - > get ( '/ajax/status/:name/:ts' ) - > to ( 'profile#status_card' ) ;
$ r - > get ( '/p/:name' ) - > to ( 'profile#profile' ) ;
2023-02-15 19:01:43 +00:00
$ r - > get ( '/p/:name/j/:id' = > 'public_journey' )
2023-03-13 20:06:53 +00:00
- > to ( 'profile#journey_details' ) ;
2023-01-08 08:40:49 +00:00
$ r - > get ( '/.well-known/webfinger' ) - > to ( 'account#webfinger' ) ;
2019-12-14 19:46:02 +00:00
$ r - > post ( '/api/v1/import' ) - > to ( 'api#import_v1' ) ;
2019-12-14 20:53:23 +00:00
$ r - > post ( '/api/v1/travel' ) - > to ( 'api#travel_v1' ) ;
2022-08-02 20:22:10 +00:00
$ r - > post ( '/action' ) - > to ( 'traveling#travel_action' ) ;
2019-03-22 15:57:24 +00:00
$ r - > post ( '/geolocation' ) - > to ( 'traveling#geolocation' ) ;
$ r - > post ( '/list_departures' ) - > to ( 'traveling#redirect_to_station' ) ;
$ r - > post ( '/login' ) - > to ( 'account#do_login' ) ;
2019-04-29 18:12:59 +00:00
$ r - > post ( '/recover' ) - > to ( 'account#request_password_reset' ) ;
2019-03-22 15:57:24 +00:00
2023-08-07 19:17:10 +00:00
if ( $ self - > config - > { traewelling } { oauth } ) {
$ r - > get ( '/oauth/traewelling' ) - > to ( 'traewelling#oauth' ) ;
$ r - > post ( '/oauth/traewelling' ) - > to ( 'traewelling#oauth' ) ;
}
2021-09-12 06:15:59 +00:00
if ( not $ self - > config - > { registration } { disabled } ) {
$ r - > get ( '/register' ) - > to ( 'account#registration_form' ) ;
$ r - > post ( '/register' ) - > to ( 'account#register' ) ;
}
2019-03-22 15:57:24 +00:00
my $ authed_r = $ r - > under (
sub {
my ( $ self ) = @ _ ;
if ( $ self - > is_user_authenticated ) {
return 1 ;
}
2023-06-09 05:55:59 +00:00
$ self - > render (
'login' ,
redirect_to = > $ self - > req - > url ,
from = > 'auth_required'
) ;
2019-03-22 15:57:24 +00:00
return undef ;
}
) ;
2019-03-21 21:12:45 +00:00
2019-03-22 15:57:24 +00:00
$ authed_r - > get ( '/account' ) - > to ( 'account#account' ) ;
2019-04-30 21:23:49 +00:00
$ authed_r - > get ( '/account/privacy' ) - > to ( 'account#privacy' ) ;
2023-06-04 16:21:36 +00:00
$ authed_r - > get ( '/account/social' ) - > to ( 'account#social' ) ;
$ authed_r - > get ( '/account/social/:kind' ) - > to ( 'account#social_list' ) ;
2023-06-04 12:28:04 +00:00
$ authed_r - > get ( '/account/profile' ) - > to ( 'account#profile' ) ;
2019-05-05 16:09:11 +00:00
$ authed_r - > get ( '/account/hooks' ) - > to ( 'account#webhook' ) ;
2020-09-30 17:12:29 +00:00
$ authed_r - > get ( '/account/traewelling' ) - > to ( 'traewelling#settings' ) ;
2019-05-20 17:15:21 +00:00
$ authed_r - > get ( '/account/insight' ) - > to ( 'account#insight' ) ;
2022-07-11 20:09:26 +00:00
$ authed_r - > get ( '/account/services' ) - > to ( 'account#services' ) ;
2019-04-23 20:27:13 +00:00
$ authed_r - > get ( '/ajax/status_card.html' ) - > to ( 'traveling#status_card' ) ;
2019-04-07 16:44:33 +00:00
$ authed_r - > get ( '/cancelled' ) - > to ( 'traveling#cancelled' ) ;
2019-09-13 21:39:28 +00:00
$ authed_r - > get ( '/fgr' ) - > to ( 'passengerrights#list_candidates' ) ;
2019-05-03 16:57:24 +00:00
$ authed_r - > get ( '/account/password' ) - > to ( 'account#password_form' ) ;
$ authed_r - > get ( '/account/mail' ) - > to ( 'account#change_mail' ) ;
2021-06-12 17:00:42 +00:00
$ authed_r - > get ( '/account/name' ) - > to ( 'account#change_name' ) ;
2019-03-22 15:57:24 +00:00
$ authed_r - > get ( '/export.json' ) - > to ( 'account#json_export' ) ;
2019-04-07 16:44:33 +00:00
$ authed_r - > get ( '/history.json' ) - > to ( 'traveling#json_history' ) ;
2020-04-19 16:26:20 +00:00
$ authed_r - > get ( '/history.csv' ) - > to ( 'traveling#csv_history' ) ;
2019-03-22 15:57:24 +00:00
$ authed_r - > get ( '/history' ) - > to ( 'traveling#history' ) ;
2020-04-19 14:49:31 +00:00
$ authed_r - > get ( '/history/commute' ) - > to ( 'traveling#commute' ) ;
2019-11-15 23:01:50 +00:00
$ authed_r - > get ( '/history/map' ) - > to ( 'traveling#map_history' ) ;
2019-04-07 16:44:33 +00:00
$ authed_r - > get ( '/history/:year' ) - > to ( 'traveling#yearly_history' ) ;
2022-12-27 10:07:16 +00:00
$ authed_r - > get ( '/history/:year/review' ) - > to ( 'traveling#year_in_review' ) ;
2019-03-27 20:20:59 +00:00
$ authed_r - > get ( '/history/:year/:month' ) - > to ( 'traveling#monthly_history' ) ;
2019-04-07 18:20:37 +00:00
$ authed_r - > get ( '/journey/add' ) - > to ( 'traveling#add_journey_form' ) ;
2019-12-06 20:40:51 +00:00
$ authed_r - > get ( '/journey/comment' ) - > to ( 'traveling#comment_form' ) ;
2023-02-27 21:14:54 +00:00
$ authed_r - > get ( '/journey/visibility' ) - > to ( 'traveling#visibility_form' ) ;
2019-03-22 15:57:24 +00:00
$ authed_r - > get ( '/journey/:id' ) - > to ( 'traveling#journey_details' ) ;
$ authed_r - > get ( '/s/*station' ) - > to ( 'traveling#station' ) ;
2019-04-30 10:08:51 +00:00
$ authed_r - > get ( '/confirm_mail/:token' ) - > to ( 'account#confirm_mail' ) ;
2019-04-30 21:23:49 +00:00
$ authed_r - > post ( '/account/privacy' ) - > to ( 'account#privacy' ) ;
2023-06-04 16:21:36 +00:00
$ authed_r - > post ( '/account/social' ) - > to ( 'account#social' ) ;
2023-06-04 12:28:04 +00:00
$ authed_r - > post ( '/account/profile' ) - > to ( 'account#profile' ) ;
2019-05-05 16:09:11 +00:00
$ authed_r - > post ( '/account/hooks' ) - > to ( 'account#webhook' ) ;
2020-09-30 17:12:29 +00:00
$ authed_r - > post ( '/account/traewelling' ) - > to ( 'traewelling#settings' ) ;
2019-05-20 17:15:21 +00:00
$ authed_r - > post ( '/account/insight' ) - > to ( 'account#insight' ) ;
2022-07-11 20:09:26 +00:00
$ authed_r - > post ( '/account/services' ) - > to ( 'account#services' ) ;
2019-04-13 08:43:05 +00:00
$ authed_r - > post ( '/journey/add' ) - > to ( 'traveling#add_journey_form' ) ;
2019-12-06 20:40:51 +00:00
$ authed_r - > post ( '/journey/comment' ) - > to ( 'traveling#comment_form' ) ;
2023-02-27 21:14:54 +00:00
$ authed_r - > post ( '/journey/visibility' ) - > to ( 'traveling#visibility_form' ) ;
2019-04-07 18:20:37 +00:00
$ authed_r - > post ( '/journey/edit' ) - > to ( 'traveling#edit_journey' ) ;
2019-09-12 16:23:23 +00:00
$ authed_r - > post ( '/journey/passenger_rights/*filename' )
2019-09-12 16:07:21 +00:00
- > to ( 'passengerrights#generate' ) ;
2019-05-03 16:57:24 +00:00
$ authed_r - > post ( '/account/password' ) - > to ( 'account#change_password' ) ;
$ authed_r - > post ( '/account/mail' ) - > to ( 'account#change_mail' ) ;
2021-06-12 17:00:42 +00:00
$ authed_r - > post ( '/account/name' ) - > to ( 'account#change_name' ) ;
2023-06-04 16:21:36 +00:00
$ authed_r - > post ( '/social-action' ) - > to ( 'account#social_action' ) ;
2019-03-22 15:57:24 +00:00
$ authed_r - > post ( '/delete' ) - > to ( 'account#delete' ) ;
$ authed_r - > post ( '/logout' ) - > to ( 'account#do_logout' ) ;
$ authed_r - > post ( '/set_token' ) - > to ( 'api#set_token' ) ;
2023-07-15 17:20:37 +00:00
$ authed_r - > get ( '/timeline/in-transit' ) - > to ( 'profile#checked_in' ) ;
2019-03-21 21:12:45 +00:00
}
1 ;