summaryrefslogtreecommitdiff
path: root/lib/Travelynx/Model
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Travelynx/Model')
-rw-r--r--lib/Travelynx/Model/InTransit.pm131
-rwxr-xr-xlib/Travelynx/Model/Journeys.pm28
-rw-r--r--lib/Travelynx/Model/Stations.pm96
-rw-r--r--lib/Travelynx/Model/Users.pm9
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,
}