diff options
| author | Birte Kristina Friesel <derf@finalrewind.org> | 2025-06-15 08:23:48 +0200 | 
|---|---|---|
| committer | Birte Kristina Friesel <derf@finalrewind.org> | 2025-06-15 08:23:51 +0200 | 
| commit | c250a2f2c7968966014315f76b25109b83c041ed (patch) | |
| tree | 3c6ee328e7e67beadeac44d9458a048fe3e0b473 /lib/Travelynx.pm | |
| parent | f1da50f9f18f0a2a5fd202daff4b6f0b517f35e0 (diff) | |
Add experimental EFA support
Squashed commit of the following:
commit b7457791ab7ab4859ebf4a5ce173e1aaeed4c7fb
Author: Birte Kristina Friesel <derf@finalrewind.org>
Date:   Sun Jun 15 08:18:46 2025 +0200
    changelog
commit 7f3d61066195cfc3c83a8fc1b2fc3743e7e6171c
Author: Birte Kristina Friesel <derf@finalrewind.org>
Date:   Sat Jun 14 19:55:51 2025 +0200
    Mark EFA backends as experimental for now
    For instance, VRR has very interesting issues when checking into departures
    that do not have real-time data yet.
commit 3370c0f6c25bd6b02cc4d56e9a3aba2a66d1151a
Author: Birte Kristina Friesel <derf@finalrewind.org>
Date:   Sat Jun 14 19:49:48 2025 +0200
    InTransit: remove debug output
commit deb5444fa2965228b537e86fce862436ef2e6e19
Author: Birte Kristina Friesel <derf@finalrewind.org>
Date:   Sat Jun 14 19:12:44 2025 +0200
    frontend js for checked-in view: never show fractional delays
commit d47ff9615b551bbd844a799be7717e9e74a04266
Author: Birte Kristina Friesel <derf@finalrewind.org>
Date:   Sat Jun 14 19:12:31 2025 +0200
    worker: add EFA support
commit 3a955c0105bf13d040a821e2c87a19694202cde6
Author: Birte Kristina Friesel <derf@finalrewind.org>
Date:   Sat Jun 14 17:48:46 2025 +0200
    EFA: checkin support
    worker support and cancellations are still missing
commit 19dea1ad13029d19cba38e7d1338718149c139fb
Author: Birte Kristina Friesel <derf@finalrewind.org>
Date:   Sat Jun 14 14:32:59 2025 +0200
    actions.js: pass on efa parameter
commit 8f18ff2c8f9f906a387dbe16d372e1c4b4a6f259
Author: Birte Kristina Friesel <derf@finalrewind.org>
Date:   Sat Jun 14 14:32:48 2025 +0200
    EFA: implement geolocation lookup
commit bce1139bab9aab167cdab910fa86085529d45b80
Author: Birte Kristina Friesel <derf@finalrewind.org>
Date:   Sat Jun 14 14:32:21 2025 +0200
    EFA: ->id is no longer supported, use ->id_num
commit e4397e6b1538ddfa71da9839d6011a73fadc528f
Author: Birte Kristina Friesel <derf@finalrewind.org>
Date:   Mon Jun 9 20:34:22 2025 +0200
    ... derp
commit e0c4cbf862a8f5a7bca0b1aceab3760af94093e9
Author: Birte Kristina Friesel <derf@finalrewind.org>
Date:   Mon Jun 9 18:28:35 2025 +0200
    database: it's dbris, not ris
commit bfb1e834ce6c3171011dc20b32117065960b8771
Merge: 42f9a00 f1da50f
Author: Birte Kristina Friesel <derf@finalrewind.org>
Date:   Mon Jun 9 18:20:51 2025 +0200
    Merge branch 'main' into efa-support
commit 42f9a00d98dbd675234c05b3e25c3e722cfdd7ba
Author: Birte Kristina Friesel <derf@finalrewind.org>
Date:   Wed Jan 8 18:11:28 2025 +0100
    EFA support (WiP)
Diffstat (limited to 'lib/Travelynx.pm')
| -rwxr-xr-x | lib/Travelynx.pm | 170 | 
1 files changed, 162 insertions, 8 deletions
| diff --git a/lib/Travelynx.pm b/lib/Travelynx.pm index f554d08..3d892ec 100755 --- a/lib/Travelynx.pm +++ b/lib/Travelynx.pm @@ -23,6 +23,7 @@ use List::MoreUtils qw(first_index);  use Travel::Status::DE::DBRIS::Formation;  use Travelynx::Helper::DBDB;  use Travelynx::Helper::DBRIS; +use Travelynx::Helper::EFA;  use Travelynx::Helper::HAFAS;  use Travelynx::Helper::IRIS;  use Travelynx::Helper::MOTIS; @@ -160,11 +161,12 @@ sub startup {  		cache_iris_main => sub {  			my ($self) = @_; -			return Cache::File->new( +			state $cache = Cache::File->new(  				cache_root      => $self->app->config->{cache}->{schedule},  				default_expires => '6 hours',  				lock_level      => Cache::File::LOCK_LOCAL(),  			); +			return $cache;  		}  	); @@ -172,11 +174,12 @@ sub startup {  		cache_iris_rt => sub {  			my ($self) = @_; -			return Cache::File->new( +			state $cache = Cache::File->new(  				cache_root      => $self->app->config->{cache}->{realtime},  				default_expires => '70 seconds',  				lock_level      => Cache::File::LOCK_LOCAL(),  			); +			return $cache;  		}  	); @@ -194,7 +197,7 @@ sub startup {  	$self->attr(  		renamed_station => sub { -			my $legacy_to_new = JSON->new->utf8->decode( +			state $legacy_to_new = JSON->new->utf8->decode(  				scalar read_file('share/old_station_names.json') );  			return $legacy_to_new;  		} @@ -220,6 +223,20 @@ sub startup {  	);  	$self->helper( +		efa => sub { +			my ($self) = @_; +			state $efa = Travelynx::Helper::EFA->new( +				log            => $self->app->log, +				main_cache     => $self->app->cache_iris_main, +				realtime_cache => $self->app->cache_iris_rt, +				root_url       => $self->base_url_for('/')->to_abs, +				user_agent     => $self->ua, +				version        => $self->app->config->{version}, +			); +		} +	); + +	$self->helper(  		dbris => sub {  			my ($self) = @_;  			state $dbris = Travelynx::Helper::DBRIS->new( @@ -490,15 +507,18 @@ sub startup {  				return Mojo::Promise->reject('You are already checked in');  			} -			if ( $opt{motis} ) { -				return $self->_checkin_motis_p(%opt); -			}  			if ( $opt{dbris} ) {  				return $self->_checkin_dbris_p(%opt);  			} +			if ( $opt{efa} ) { +				return $self->_checkin_efa_p(%opt); +			}  			if ( $opt{hafas} ) {  				return $self->_checkin_hafas_p(%opt);  			} +			if ( $opt{motis} ) { +				return $self->_checkin_motis_p(%opt); +			}  			my $promise = Mojo::Promise->new; @@ -869,6 +889,137 @@ sub startup {  	);  	$self->helper( +		'_checkin_efa_p' => sub { +			my ( $self, %opt ) = @_; +			my $station = $opt{station}; +			my $trip_id = $opt{train_id}; +			my $ts      = $opt{ts}; +			my $uid     = $opt{uid} // $self->current_user->{id}; +			my $db      = $opt{db}  // $self->pg->db; + +			my $promise = Mojo::Promise->new; +			$self->efa->get_journey_p( +				service => $opt{efa}, +				trip_id => $trip_id +			)->then( +				sub { +					my ($journey) = @_; + +					my $found; +					for my $stop ( $journey->route ) { +						if ( $stop->id_num == $station ) { +							$found = $stop; + +							# Lines may serve the same stop several times. +							# Keep looking until the scheduled departure +							# matches the one passed while checking in. +							if ( $ts and $stop->sched_dep->epoch == $ts ) { +								last; +							} +						} +					} +					if ( not $found ) { +						$promise->reject( +"Did not find stop '$station' within journey '$trip_id'" +						); +						return; +					} + +					for my $stop ( $journey->route ) { +						$self->stations->add_or_update( +							stop => $stop, +							db   => $db, +							efa  => $opt{efa}, +						); +					} + +					eval { +						$self->in_transit->add( +							uid        => $uid, +							db         => $db, +							journey    => $journey, +							stop       => $found, +							trip_id    => $trip_id, +							backend_id => $self->stations->get_backend_id( +								efa => $opt{efa} +							), +						); +					}; +					if ($@) { +						$self->app->log->error( +							"Checkin($uid): INSERT failed: $@"); +						$promise->reject( 'INSERT failed: ' . $@ ); +						return; +					} + +					my $polyline; +					if ( $journey->polyline ) { +						my @station_list; +						my @coordinate_list; +						for my $coord ( $journey->polyline ) { +							if ( $coord->{stop} ) { +								push( +									@coordinate_list, +									[ +										$coord->{lon}, $coord->{lat}, +										$coord->{stop}->id_num +									] +								); +								push( @station_list, +									$coord->{stop}->full_name ); +							} +							else { +								push( @coordinate_list, +									[ $coord->{lon}, $coord->{lat} ] ); +							} +						} + +						# equal length → polyline only consists of straight +						# lines between stops. that's not helpful. +						if ( @station_list == @coordinate_list ) { +							$self->log->debug( 'Ignoring polyline for ' +								  . $journey->line +								  . ' as it only consists of straight lines between stops.' +							); +						} +						else { +							$polyline = { +								from_eva => ( $journey->route )[0]->id_num, +								to_eva   => ( $journey->route )[-1]->id_num, +								coords   => \@coordinate_list, +							}; +						} +					} + +					if ($polyline) { +						$self->in_transit->set_polyline( +							uid      => $uid, +							db       => $db, +							polyline => $polyline, +						); +					} + +					# mustn't be called during a transaction +					if ( not $opt{in_transaction} ) { +						$self->run_hook( $uid, 'checkin' ); +					} + +					$promise->resolve($journey); + +					return; +				} +			)->catch( +				sub { +					my ($err) = @_; +					$promise->reject($err); +					return; +				} +			)->wait; +			return $promise; +		} +	); + +	$self->helper(  		'_checkin_hafas_p' => sub {  			my ( $self, %opt ) = @_; @@ -877,7 +1028,6 @@ sub startup {  			my $ts       = $opt{ts};  			my $uid      = $opt{uid} // $self->current_user->{id};  			my $db       = $opt{db}  // $self->pg->db; -			my $hafas;  			my $promise = Mojo::Promise->new; @@ -1136,7 +1286,11 @@ sub startup {  				return $promise->resolve( 0, 'race condition' );  			} -			if ( $user->{is_dbris} or $user->{is_hafas} or $user->{is_motis} ) { +			if (   $user->{is_dbris} +				or $user->{is_efa} +				or $user->{is_hafas} +				or $user->{is_motis} ) +			{  				return $self->_checkout_journey_p(%opt);  			} | 
