diff options
Diffstat (limited to 'lib/Travelynx/Model')
-rw-r--r-- | lib/Travelynx/Model/InTransit.pm | 131 | ||||
-rwxr-xr-x | lib/Travelynx/Model/Journeys.pm | 28 | ||||
-rw-r--r-- | lib/Travelynx/Model/Stations.pm | 96 | ||||
-rw-r--r-- | lib/Travelynx/Model/Users.pm | 9 |
4 files changed, 246 insertions, 18 deletions
diff --git a/lib/Travelynx/Model/InTransit.pm b/lib/Travelynx/Model/InTransit.pm index eeb1d87..0e3fdc6 100644 --- a/lib/Travelynx/Model/InTransit.pm +++ b/lib/Travelynx/Model/InTransit.pm @@ -1,6 +1,7 @@ package Travelynx::Model::InTransit; # Copyright (C) 2020-2025 Birte Kristina Friesel +# Copyright (C) 2025 networkException <git@nwex.de> # # SPDX-License-Identifier: AGPL-3.0-or-later @@ -99,6 +100,7 @@ sub add { my $train_suffix = $opt{train_suffix}; my $journey = $opt{journey}; my $stop = $opt{stop}; + my $stopover = $opt{stopover}; my $checkin_station_id = $opt{departure_eva}; my $route = $opt{route}; my $data = $opt{data}; @@ -282,6 +284,57 @@ sub add { } ); } + elsif ( $journey and $stopover ) { + + # MOTIS + my @route; + for my $journey_stopover ( $journey->stopovers ) { + push( + @route, + [ + $journey_stopover->stop->name, + $journey_stopover->stop->{eva} // die('eva not set for stopover'), + { + sched_arr => _epoch( $journey_stopover->scheduled_arrival ), + sched_dep => _epoch( $journey_stopover->scheduled_departure ), + rt_arr => _epoch( $journey_stopover->realtime_arrival ), + rt_dep => _epoch( $journey_stopover->realtime_departure ), + arr_delay => $journey_stopover->arrival_delay, + dep_delay => $journey_stopover->departure_delay, + lat => $journey_stopover->stop->lat, + lon => $journey_stopover->stop->lon, + } + ] + ); + } + + $db->insert( + 'in_transit', + { + user_id => $uid, + cancelled => $stopover->{is_cancelled} + ? 1 + : 0, + checkin_station_id => $stopover->stop->{eva}, + checkin_time => DateTime->now( time_zone => 'Europe/Berlin' ), + dep_platform => $stopover->track, + train_type => $journey->mode, + train_no => q{}, + train_id => $journey->id, + train_line => $journey->route_name, + sched_departure => $stopover->scheduled_departure, + real_departure => $stopover->departure, + route => $json->encode( \@route ), + data => JSON->new->encode( + { + rt => $stopover->{is_realtime} ? 1 : 0, + %{ $data // {} } + } + ), + backend_id => $backend_id, + } + ); + } else { die('neither train nor journey specified'); } @@ -331,7 +384,7 @@ sub postprocess { # Note that the departure stop may be present more than once in @route, # e.g. when traveling along ring lines such as S41 / S42 in Berlin. if ( - $ret->{dep_name} + $ret->{dep_name} and $station->[0] eq $ret->{dep_name} and not($station->[2]{sched_dep} and $station->[2]{sched_dep} < $ret->{sched_dep_ts} ) @@ -887,6 +940,33 @@ sub update_departure_dbris { ); } +sub update_departure_motis { + my ( $self, %opt ) = @_; + my $uid = $opt{uid}; + my $db = $opt{db} // $self->{pg}->db; + my $dep_eva = $opt{dep_eva}; + my $arr_eva = $opt{arr_eva}; + my $journey = $opt{journey}; + my $stopover = $opt{stopover}; + my $json = JSON->new; + + # selecting on user_id and train_no avoids a race condition if a user checks + # into a new train while we are fetching data for their previous journey. In + # this case, the new train would receive data from the previous journey. + $db->update( + 'in_transit', + { + real_departure => $stopover->{realtime_departure}, + }, + { + user_id => $uid, + train_id => $opt{train_id}, + checkin_station_id => $dep_eva, + checkout_station_id => $arr_eva, + } + ); +} + sub update_departure_hafas { my ( $self, %opt ) = @_; my $uid = $opt{uid}; @@ -1053,6 +1133,55 @@ sub update_arrival_dbris { ); } +sub update_arrival_motis { + my ( $self, %opt ) = @_; + my $uid = $opt{uid}; + my $db = $opt{db} // $self->{pg}->db; + my $dep_eva = $opt{dep_eva}; + my $arr_eva = $opt{arr_eva}; + my $journey = $opt{journey}; + my $stopover = $opt{stopover}; + my $json = JSON->new; + + my @route; + for my $journey_stopover ( $journey->stopovers ) { + push( + @route, + [ + $journey_stopover->stop->name, + $journey_stopover->stop->{eva} // die('eva not set for stopover'), + { + sched_arr => _epoch( $journey_stopover->scheduled_arrival ), + sched_dep => _epoch( $journey_stopover->scheduled_departure ), + rt_arr => _epoch( $journey_stopover->realtime_arrival ), + rt_dep => _epoch( $journey_stopover->realtime_departure ), + arr_delay => $journey_stopover->arrival_delay, + dep_delay => $journey_stopover->departure_delay, + lat => $journey_stopover->stop->lat, + lon => $journey_stopover->stop->lon, + } + ] + ); + } + + # selecting on user_id and train_no avoids a race condition if a user checks + # into a new train while we are fetching data for their previous journey. In + # this case, the new train would receive data from the previous journey. + $db->update( + 'in_transit', + { + real_arrival => $stopover->{realtime_arrival}, + route => $json->encode( [@route] ), + }, + { + user_id => $uid, + train_id => $opt{train_id}, + checkin_station_id => $dep_eva, + checkout_station_id => $arr_eva, + } + ); +} + sub update_arrival_hafas { my ( $self, %opt ) = @_; my $uid = $opt{uid}; diff --git a/lib/Travelynx/Model/Journeys.pm b/lib/Travelynx/Model/Journeys.pm index f5bc9f1..fff59f9 100755 --- a/lib/Travelynx/Model/Journeys.pm +++ b/lib/Travelynx/Model/Journeys.pm @@ -549,7 +549,7 @@ sub get { my @select = ( - qw(journey_id is_dbris 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) + qw(journey_id is_dbris is_iris is_hafas is_motis 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, @@ -610,6 +610,7 @@ sub get { is_dbris => $entry->{is_dbris}, is_iris => $entry->{is_iris}, is_hafas => $entry->{is_hafas}, + is_motis => $entry->{is_motis}, backend_name => $entry->{backend_name}, backend_id => $entry->{backend_id}, type => $entry->{train_type}, @@ -871,8 +872,9 @@ sub get_latest_checkout_stations { my $res = $db->select( 'journeys_str', [ - 'arr_name', 'arr_eva', 'train_id', 'backend_id', - 'backend_name', 'is_dbris', 'is_hafas' + 'arr_name', 'arr_eva', 'arr_external_id', 'train_id', + 'backend_id', 'backend_name', 'is_dbris', 'is_hafas', + 'is_motis' ], { user_id => $uid, @@ -894,11 +896,13 @@ sub get_latest_checkout_stations { push( @ret, { - name => $row->{arr_name}, - eva => $row->{arr_eva}, - dbris => $row->{is_dbris} ? $row->{backend_name} : 0, - hafas => $row->{is_hafas} ? $row->{backend_name} : 0, - backend_id => $row->{backend_id}, + name => $row->{arr_name}, + eva => $row->{arr_eva}, + external_id_or_eva => $row->{arr_external_id} // $row->{arr_eva}, + dbris => $row->{is_dbris} ? $row->{backend_name} : 0, + hafas => $row->{is_hafas} ? $row->{backend_name} : 0, + motis => $row->{is_motis} ? $row->{backend_name} : 0, + backend_id => $row->{backend_id}, } ); } @@ -1392,7 +1396,7 @@ sub compute_review { if ( not $most_undelay or $speedup > ( - $most_undelay->{sched_duration} + $most_undelay->{sched_duration} - $most_undelay->{rt_duration} ) ) @@ -1665,7 +1669,7 @@ sub compute_stats { @inconsistencies, { conflict => { - train => $journey->{type} . ' ' + train => ( $journey->{is_motis} ? '' : $journey->{type} ) . ' ' . ( $journey->{line} // $journey->{no} ), arr => epoch_to_dt( $journey->{rt_arr_ts} ) ->strftime('%d.%m.%Y %H:%M'), @@ -1691,7 +1695,7 @@ sub compute_stats { $next_departure = $journey->{rt_dep_ts}; $next_id = $journey->{id}; $next_train - = $journey->{type} . ' ' . ( $journey->{line} // $journey->{no} ),; + = ( $journey->{is_motis} ? '' : $journey->{type} ) . ' ' . ( $journey->{line} // $journey->{no} ),; } my $ret = { km_route => $km_route, @@ -1740,7 +1744,7 @@ sub get_stats { # checks out of a train or manually edits/adds a journey. if ( - not $opt{write_only} + not $opt{write_only} and not $opt{review} and my $stats = $self->stats_cache->get( uid => $uid, diff --git a/lib/Travelynx/Model/Stations.pm b/lib/Travelynx/Model/Stations.pm index 3d6549f..761c5de 100644 --- a/lib/Travelynx/Model/Stations.pm +++ b/lib/Travelynx/Model/Stations.pm @@ -1,6 +1,7 @@ package Travelynx::Model::Stations; # Copyright (C) 2022 Birte Kristina Friesel +# Copyright (C) 2025 networkException <git@nwex.de> # # SPDX-License-Identifier: AGPL-3.0-or-later @@ -28,6 +29,9 @@ sub get_backend_id { if ( $opt{dbris} and $self->{backend_id}{dbris}{ $opt{dbris} } ) { return $self->{backend_id}{dbris}{ $opt{dbris} }; } + if ( $opt{motis} and $self->{backend_id}{motis}{ $opt{motis} } ) { + return $self->{backend_id}{motis}{ $opt{motis} }; + } my $db = $opt{db} // $self->{pg}->db; my $backend_id = 0; @@ -54,6 +58,17 @@ sub get_backend_id { )->hash->{id}; $self->{backend_id}{hafas}{ $opt{hafas} } = $backend_id; } + elsif ( $opt{motis} ) { + $backend_id = $db->select( + 'backends', + ['id'], + { + motis => 1, + name => $opt{motis} + } + )->hash->{id}; + $self->{backend_id}{motis}{ $opt{motis} } = $backend_id; + } return $backend_id; } @@ -85,7 +100,7 @@ sub get_backends { $opt{db} //= $self->{pg}->db; my $res = $opt{db} - ->select( 'backends', [ 'id', 'name', 'iris', 'hafas', 'dbris' ] ); + ->select( 'backends', [ 'id', 'name', 'iris', 'hafas', 'dbris', 'motis' ] ); my @ret; while ( my $row = $res->hash ) { @@ -97,6 +112,7 @@ sub get_backends { iris => $row->{iris}, dbris => $row->{dbris}, hafas => $row->{hafas}, + motis => $row->{motis}, } ); } @@ -149,6 +165,61 @@ sub add_or_update { return; } + if ( $opt{motis} ) { + if ( + my $s = $self->get_by_external_id( + external_id => $stop->id, + db => $opt{db}, + backend_id => $opt{backend_id} + ) + ) + { + $opt{db}->update( + 'stations', + { + name => $stop->name, + lat => $stop->lat, + lon => $stop->lon, + archived => 0 + }, + { + eva => $s->{eva}, + source => $opt{backend_id} + } + ); + + $stop->{eva} = $s->{eva}; + + return; + } + + my $s = $opt{db}->query( + qq { + with new_station as ( + insert into stations_external_ids (backend_id, external_id) + values (?, ?) + returning eva, backend_id + ) + + insert into stations (eva, name, lat, lon, source, archived) + values ((select eva from new_station), ?, ?, ?, (select backend_id from new_station), ?) + returning * + }, + ( + $opt{backend_id}, + $stop->id, + $stop->name, + $stop->lat, + $stop->lon, + 0, + ) + ); + + $stop->{eva} = $s->hash->{eva}; + + return; + } + my $loc = $stop->loc; if ( my $s = $self->get_by_eva( @@ -184,6 +255,8 @@ sub add_or_update { archived => 0 } ); + + return; } sub add_meta { @@ -276,6 +349,27 @@ sub get_by_eva { )->hash; } +# Slow +sub get_by_external_id { + my ( $self, %opt ) = @_; + + if ( not $opt{external_id} ) { + return; + } + + $opt{db} //= $self->{pg}->db; + $opt{backend_id} //= $self->get_backend_id(%opt); + + return $opt{db}->select( + 'stations_with_external_ids', + '*', + { + external_id => $opt{external_id}, + source => $opt{backend_id}, + } + )->hash; +} + # Fast sub get_by_evas { my ( $self, %opt ) = @_; diff --git a/lib/Travelynx/Model/Users.pm b/lib/Travelynx/Model/Users.pm index 750e889..10ab17e 100644 --- a/lib/Travelynx/Model/Users.pm +++ b/lib/Travelynx/Model/Users.pm @@ -418,7 +418,7 @@ sub get { . 'extract(epoch from registered_at) as registered_at_ts, ' . 'extract(epoch from last_seen) as last_seen_ts, ' . 'extract(epoch from deletion_requested) as deletion_requested_ts, ' - . 'backend_id, backend_name, hafas, dbris', + . 'backend_id, backend_name, hafas, dbris, motis', { id => $uid } )->hash; if ($user) { @@ -459,6 +459,7 @@ sub get { backend_name => $user->{backend_name}, backend_dbris => $user->{dbris}, backend_hafas => $user->{hafas}, + backend_motis => $user->{motis}, }; } return undef; @@ -1026,11 +1027,11 @@ sub get_followers { id => $row->{id}, name => $row->{name}, following_back => ( - $row->{inverse_predicate} + $row->{inverse_predicate} and $row->{inverse_predicate} == $predicate_atoi{follows} ) ? 1 : 0, followback_requested => ( - $row->{inverse_predicate} + $row->{inverse_predicate} and $row->{inverse_predicate} == $predicate_atoi{requests_follow} ) ? 1 : 0, @@ -1102,7 +1103,7 @@ sub get_followees { id => $row->{id}, name => $row->{name}, following_back => ( - $row->{inverse_predicate} + $row->{inverse_predicate} and $row->{inverse_predicate} == $predicate_atoi{follows} ) ? 1 : 0, } |