diff options
Diffstat (limited to 'lib/Travelynx/Model/Stations.pm')
-rw-r--r-- | lib/Travelynx/Model/Stations.pm | 468 |
1 files changed, 423 insertions, 45 deletions
diff --git a/lib/Travelynx/Model/Stations.pm b/lib/Travelynx/Model/Stations.pm index 75b4174..5316118 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 @@ -14,40 +15,370 @@ sub new { return bless( \%opt, $class ); } +sub get_backend_id { + my ( $self, %opt ) = @_; + + if ( $opt{iris} ) { + + # special case + return 0; + } + if ( $opt{dbris} and $self->{backend_id}{dbris}{ $opt{dbris} } ) { + return $self->{backend_id}{dbris}{ $opt{dbris} }; + } + if ( $opt{efa} and $self->{backend_id}{efa}{ $opt{efa} } ) { + return $self->{backend_id}{efa}{ $opt{efa} }; + } + if ( $opt{hafas} and $self->{backend_id}{hafas}{ $opt{hafas} } ) { + return $self->{backend_id}{hafas}{ $opt{hafas} }; + } + 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; + + if ( $opt{dbris} ) { + $backend_id = $db->select( + 'backends', + ['id'], + { + dbris => 1, + name => $opt{dbris} + } + )->hash->{id}; + $self->{backend_id}{dbris}{ $opt{dbris} } = $backend_id; + } + elsif ( $opt{efa} ) { + $backend_id = $db->select( + 'backends', + ['id'], + { + efa => 1, + name => $opt{efa} + } + )->hash->{id}; + $self->{backend_id}{efa}{ $opt{efa} } = $backend_id; + } + elsif ( $opt{hafas} ) { + $backend_id = $db->select( + 'backends', + ['id'], + { + hafas => 1, + name => $opt{hafas} + } + )->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; +} + +sub get_backend { + my ( $self, %opt ) = @_; + + if ( $self->{backend_cache}{ $opt{backend_id} } ) { + return $self->{backend_cache}{ $opt{backend_id} }; + } + + my $db = $opt{db} // $self->{pg}->db; + my $ret = $db->select( + 'backends', + '*', + { + id => $opt{backend_id}, + } + )->hash; + + $self->{backend_cache}{ $opt{backend_id} } = $ret; + + return $ret; +} + +sub get_backends { + my ( $self, %opt ) = @_; + + $opt{db} //= $self->{pg}->db; + + my $res = $opt{db}->select( 'backends', + [ 'id', 'name', 'dbris', 'efa', 'hafas', 'iris', 'motis' ] ); + my @ret; + + while ( my $row = $res->hash ) { + push( + @ret, + { + id => $row->{id}, + name => $row->{name}, + dbris => $row->{dbris}, + efa => $row->{efa}, + hafas => $row->{hafas}, + iris => $row->{iris}, + motis => $row->{motis}, + } + ); + } + + return @ret; +} + +# Slow for MOTIS backends sub add_or_update { my ( $self, %opt ) = @_; - my $stop = $opt{stop}; - my $source = 1; - my $db = $opt{db} // $self->{pg}->db; + my $stop = $opt{stop}; + $opt{db} //= $self->{pg}->db; - if ( my $s = $self->get_by_eva( $stop->eva, db => $db ) ) { - if ( $source == 1 and $s->{source} == 0 and not $s->{archived} ) { + $opt{backend_id} //= $self->get_backend_id(%opt); + + if ( $opt{dbris} ) { + if ( + my $s = $self->get_by_eva( + $stop->eva, + db => $opt{db}, + backend_id => $opt{backend_id} + ) + ) + { + $opt{db}->update( + 'stations', + { + name => $stop->name, + lat => $stop->lat, + lon => $stop->lon, + archived => 0 + }, + { + eva => $stop->eva, + source => $opt{backend_id} + } + ); return; } - $db->update( + $opt{db}->insert( 'stations', { + eva => $stop->eva, name => $stop->name, lat => $stop->lat, lon => $stop->lon, - source => $source, + source => $opt{backend_id}, + archived => 0 + } + ); + return; + } + + if ( $opt{efa} ) { + if ( + my $s = $self->get_by_eva( + $stop->id_num, + db => $opt{db}, + backend_id => $opt{backend_id} + ) + ) + { + $opt{db}->update( + 'stations', + { + name => $stop->full_name, + lat => $stop->latlon->[0], + lon => $stop->latlon->[1], + archived => 0 + }, + { + eva => $stop->id_num, + source => $opt{backend_id} + } + ); + return; + } + if (not $stop->latlon) { + die('Backend Error: Stop "' . $stop->full_name . '" has no geo coordinates'); + } + $opt{db}->insert( + 'stations', + { + eva => $stop->id_num, + name => $stop->full_name, + lat => $stop->latlon->[0], + lon => $stop->latlon->[1], + source => $opt{backend_id}, + archived => 0 + } + ); + 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} + } + ); + + # MOTIS backends do not provide a numeric ID, so we set our ID here. + $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, + ) + ); + + # MOTIS backends do not provide a numeric ID, so we set our ID here. + $stop->{eva} = $s->hash->{eva}; + return; + } + + my $loc = $stop->loc; + if ( + my $s = $self->get_by_eva( + $loc->eva, + db => $opt{db}, + backend_id => $opt{backend_id} + ) + ) + { + $opt{db}->update( + 'stations', + { + name => $loc->name, + lat => $loc->lat, + lon => $loc->lon, archived => 0 }, - { eva => $stop->eva } + { + eva => $loc->eva, + source => $opt{backend_id} + } ); return; } - $db->insert( + $opt{db}->insert( 'stations', { - eva => $stop->eva, - name => $stop->name, - lat => $stop->lat, - lon => $stop->lon, - source => $source, + eva => $loc->eva, + name => $loc->name, + lat => $loc->lat, + lon => $loc->lon, + source => $opt{backend_id}, archived => 0 } ); + + return; +} + +sub add_meta { + my ( $self, %opt ) = @_; + my $eva = $opt{eva}; + my @meta = @{ $opt{meta} }; + + $opt{db} //= $self->{pg}->db; + $opt{backend_id} //= $self->get_backend_id(%opt); + + for my $meta (@meta) { + if ( $meta != $eva ) { + $opt{db}->insert( + 'related_stations', + { + eva => $eva, + meta => $meta, + backend_id => $opt{backend_id}, + }, + { on_conflict => undef } + ); + } + } +} + +sub get_db_iterator { + my ($self) = @_; + + return $self->{pg}->db->select( 'stations_str', '*' ); +} + +sub get_meta { + my ( $self, %opt ) = @_; + my $db = $opt{db} // $self->{pg}->db; + my $eva = $opt{eva}; + + $opt{backend_id} //= $self->get_backend_id( %opt, db => $db ); + + my $res = $db->select( + 'related_stations', + ['meta'], + { + eva => $eva, + backend_id => $opt{backend_id} + } + ); + my @ret; + + while ( my $row = $res->hash ) { + push( @ret, $row->{meta} ); + } + + return @ret; +} + +sub get_for_autocomplete { + my ( $self, %opt ) = @_; + + $opt{backend_id} //= $self->get_backend_id(%opt); + + my $res = $self->{pg} + ->db->select( 'stations', ['name'], { source => $opt{backend_id} } ); + my %ret; + + while ( my $row = $res->hash ) { + $ret{ $row->{name} } = undef; + } + + return \%ret; } # Fast @@ -58,52 +389,88 @@ sub get_by_eva { return; } - my $db = $opt{db} // $self->{pg}->db; + $opt{db} //= $self->{pg}->db; + $opt{backend_id} //= $self->get_backend_id(%opt); - return $db->select( 'stations', '*', { eva => $eva } )->hash; + return $opt{db}->select( + 'stations', + '*', + { + eva => $eva, + source => $opt{backend_id} + } + )->hash; } -# Fast -sub get_by_evas { - my ( $self, @evas ) = @_; +# Slow +sub get_by_external_id { + my ( $self, %opt ) = @_; - my @ret - = $self->{pg}->db->select( 'stations', '*', { eva => { '=', \@evas } } ) - ->hashes->each; - return @ret; + 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; } -# Slow -sub get_latlon_by_name { +# Fast +sub get_by_evas { my ( $self, %opt ) = @_; - my $db = $opt{db} // $self->{pg}->db; + $opt{db} //= $self->{pg}->db; + $opt{backend_id} //= $self->get_backend_id(%opt); - my %location; - my $res = $db->select( 'stations', [ 'name', 'lat', 'lon' ] ); - while ( my $row = $res->hash ) { - $location{ $row->{name} } = [ $row->{lat}, $row->{lon} ]; - } - return \%location; + my @ret = $self->{pg}->db->select( + 'stations', + '*', + { + eva => { '=', $opt{evas} }, + source => $opt{backend_id} + } + )->hashes->each; + return @ret; } # Slow sub get_by_name { my ( $self, $name, %opt ) = @_; - my $db = $opt{db} // $self->{pg}->db; + $opt{db} //= $self->{pg}->db; + $opt{backend_id} //= $self->get_backend_id(%opt); - return $db->select( 'stations', '*', { name => $name }, { limit => 1 } ) - ->hash; + return $opt{db}->select( + 'stations', + '*', + { + name => $name, + source => $opt{backend_id} + }, + { limit => 1 } + )->hash; } # Slow sub get_by_names { - my ( $self, @names ) = @_; + my ( $self, %opt ) = @_; - my @ret - = $self->{pg}->db->select( 'stations', '*', { name => { '=', \@names } } ) - ->hashes->each; + my @ret = $self->{pg}->db->select( + 'stations', + '*', + { + name => { '=', $opt{names} }, + source => $opt{backend_id} + } + )->hashes->each; return @ret; } @@ -111,16 +478,27 @@ sub get_by_names { sub get_by_ds100 { my ( $self, $ds100, %opt ) = @_; - my $db = $opt{db} // $self->{pg}->db; + $opt{db} //= $self->{pg}->db; + $opt{backend_id} //= $self->get_backend_id(%opt); - return $db->select( 'stations', '*', { ds100 => $ds100 }, { limit => 1 } ) - ->hash; + return $opt{db}->select( + 'stations', + '*', + { + ds100 => $ds100, + source => $opt{backend_id} + }, + { limit => 1 } + )->hash; } # Can be slow sub search { my ( $self, $identifier, %opt ) = @_; + $opt{db} //= $self->{pg}->db; + $opt{backend_id} //= $self->get_backend_id(%opt); + if ( $identifier =~ m{ ^ \d+ $ }x ) { return $self->get_by_eva( $identifier, %opt ) // $self->get_by_ds100( $identifier, %opt ) @@ -133,10 +511,10 @@ sub search { # Slow sub grep_unknown { - my ( $self, @stations ) = @_; + my ( $self, %opt ) = @_; - my %station = map { $_->{name} => 1 } $self->get_by_names(@stations); - my @unknown_stations = grep { not $station{$_} } @stations; + my %station = map { $_->{name} => 1 } $self->get_by_names(%opt); + my @unknown_stations = grep { not $station{$_} } @{ $opt{names} }; return @unknown_stations; } |