diff options
Diffstat (limited to 'lib/Travelynx/Command')
| -rw-r--r-- | lib/Travelynx/Command/database.pm | 440 | ||||
| -rw-r--r-- | lib/Travelynx/Command/dumpstops.pm | 6 | ||||
| -rw-r--r-- | lib/Travelynx/Command/integritycheck.pm | 9 | ||||
| -rw-r--r-- | lib/Travelynx/Command/maintenance.pm | 2 | ||||
| -rw-r--r-- | lib/Travelynx/Command/stats.pm | 59 | ||||
| -rw-r--r-- | lib/Travelynx/Command/translation.pm | 99 | ||||
| -rw-r--r-- | lib/Travelynx/Command/work.pm | 365 |
7 files changed, 804 insertions, 176 deletions
diff --git a/lib/Travelynx/Command/database.pm b/lib/Travelynx/Command/database.pm index 675f0a7..5792e5f 100644 --- a/lib/Travelynx/Command/database.pm +++ b/lib/Travelynx/Command/database.pm @@ -7,7 +7,7 @@ package Travelynx::Command::database; use Mojo::Base 'Mojolicious::Command'; use DateTime; -use File::Slurp qw(read_file); +use File::Slurp qw(read_dir read_file); use List::Util qw(); use JSON; use Travel::Status::DE::EFA; @@ -3184,8 +3184,446 @@ qq{select distinct checkout_station_id from in_transit where backend_id = 0;} } ); }, + + # v64 -> v65 + # stations_str: add is_motis + sub { + my ($db) = @_; + $db->query( + qq{ + drop view stations_str; + create view stations_str as + select stations.name as name, + eva, lat, lon, + backends.name as backend, + dbris as is_dbris, + efa as is_efa, + iris as is_iris, + hafas as is_hafas, + motis as is_motis + from stations + left join backends + on source = backends.id; + update schema_version set version = 65; + } + ); + }, + + # v65 -> v66 + # Relax platform and line length constraints for EFA APIs (and possibly MOTIS) + sub { + my ($db) = @_; + $db->query( + qq{ + drop view in_transit_str; + drop view journeys_str; + drop view users_with_backend; + drop view follows_in_transit; + + alter table in_transit alter column train_line type varchar(64); + alter table in_transit alter column arr_platform type varchar(64); + alter table in_transit alter column dep_platform type varchar(64); + alter table journeys alter column train_line type varchar(64); + alter table journeys alter column arr_platform type varchar(64); + alter table journeys alter column dep_platform type varchar(64); + + create view in_transit_str as select + user_id, + backend.iris as is_iris, backend.hafas as is_hafas, + backend.efa as is_efa, backend.dbris as is_dbris, + backend.motis as is_motis, + backend.name as backend_name, in_transit.backend_id as backend_id, + train_type, train_line, train_no, train_id, + extract(epoch from checkin_time) as checkin_ts, + extract(epoch from sched_departure) as sched_dep_ts, + extract(epoch from real_departure) as real_dep_ts, + checkin_station_id as dep_eva, + dep_station.ds100 as dep_ds100, + dep_station.name as dep_name, + dep_station.lat as dep_lat, + dep_station.lon as dep_lon, + dep_station_external_id.external_id as dep_external_id, + extract(epoch from checkout_time) as checkout_ts, + extract(epoch from sched_arrival) as sched_arr_ts, + extract(epoch from real_arrival) as real_arr_ts, + checkout_station_id as arr_eva, + arr_station.ds100 as arr_ds100, + arr_station.name as arr_name, + arr_station.lat as arr_lat, + arr_station.lon as arr_lon, + arr_station_external_id.external_id as arr_external_id, + polyline_id, + polylines.polyline as polyline, + visibility, + coalesce(visibility, users.public_level & 127) as effective_visibility, + cancelled, route, messages, user_data, + dep_platform, arr_platform, data + from in_transit + left join polylines on polylines.id = polyline_id + left join users on users.id = user_id + left join stations as dep_station on checkin_station_id = dep_station.eva and in_transit.backend_id = dep_station.source + left join stations as arr_station on checkout_station_id = arr_station.eva and in_transit.backend_id = arr_station.source + left join stations_external_ids as dep_station_external_id on checkin_station_id = dep_station_external_id.eva and in_transit.backend_id = dep_station_external_id.backend_id + left join stations_external_ids as arr_station_external_id on checkout_station_id = arr_station_external_id.eva and in_transit.backend_id = arr_station_external_id.backend_id + left join backends as backend on in_transit.backend_id = backend.id + ; + create view journeys_str as select + journeys.id as journey_id, user_id, + backend.iris as is_iris, backend.hafas as is_hafas, + backend.efa as is_efa, backend.dbris as is_dbris, + backend.motis as is_motis, + backend.name as backend_name, journeys.backend_id as backend_id, + train_type, train_line, train_no, train_id, + extract(epoch from checkin_time) as checkin_ts, + extract(epoch from sched_departure) as sched_dep_ts, + extract(epoch from real_departure) as real_dep_ts, + checkin_station_id as dep_eva, + dep_station.ds100 as dep_ds100, + dep_station.name as dep_name, + dep_station.lat as dep_lat, + dep_station.lon as dep_lon, + dep_station_external_id.external_id as dep_external_id, + extract(epoch from checkout_time) as checkout_ts, + extract(epoch from sched_arrival) as sched_arr_ts, + extract(epoch from real_arrival) as real_arr_ts, + checkout_station_id as arr_eva, + arr_station.ds100 as arr_ds100, + arr_station.name as arr_name, + arr_station.lat as arr_lat, + arr_station.lon as arr_lon, + arr_station_external_id.external_id as arr_external_id, + polylines.polyline as polyline, + visibility, + coalesce(visibility, users.public_level & 127) as effective_visibility, + cancelled, edited, route, messages, user_data, + dep_platform, arr_platform + from journeys + left join polylines on polylines.id = polyline_id + left join users on users.id = user_id + left join stations as dep_station on checkin_station_id = dep_station.eva and journeys.backend_id = dep_station.source + left join stations as arr_station on checkout_station_id = arr_station.eva and journeys.backend_id = arr_station.source + left join stations_external_ids as dep_station_external_id on checkin_station_id = dep_station_external_id.eva and journeys.backend_id = dep_station_external_id.backend_id + left join stations_external_ids as arr_station_external_id on checkout_station_id = arr_station_external_id.eva and journeys.backend_id = arr_station_external_id.backend_id + left join backends as backend on journeys.backend_id = backend.id + ; + create view users_with_backend as select + users.id as id, users.name as name, status, public_level, + email, password, registered_at, last_seen, + deletion_requested, deletion_notified, use_history, + accept_follows, notifications, profile, backend_id, iris, + hafas, efa, dbris, motis, backend.name as backend_name + from users + left join backends as backend on users.backend_id = backend.id + ; + create view follows_in_transit as select + r1.subject_id as follower_id, user_id as followee_id, + users.name as followee_name, + train_type, train_line, train_no, train_id, + backend.iris as is_iris, backend.hafas as is_hafas, + backend.efa as is_efa, backend.dbris as is_dbris, + backend.motis as is_motis, + backend.name as backend_name, in_transit.backend_id as backend_id, + extract(epoch from checkin_time) as checkin_ts, + extract(epoch from sched_departure) as sched_dep_ts, + extract(epoch from real_departure) as real_dep_ts, + checkin_station_id as dep_eva, + dep_station.ds100 as dep_ds100, + dep_station.name as dep_name, + dep_station.lat as dep_lat, + dep_station.lon as dep_lon, + extract(epoch from checkout_time) as checkout_ts, + extract(epoch from sched_arrival) as sched_arr_ts, + extract(epoch from real_arrival) as real_arr_ts, + checkout_station_id as arr_eva, + arr_station.ds100 as arr_ds100, + arr_station.name as arr_name, + arr_station.lat as arr_lat, + arr_station.lon as arr_lon, + polyline_id, + polylines.polyline as polyline, + visibility, + coalesce(visibility, users.public_level & 127) as effective_visibility, + cancelled, route, messages, user_data, + dep_platform, arr_platform, data + from in_transit + left join polylines on polylines.id = polyline_id + left join users on users.id = user_id + left join relations as r1 on r1.predicate = 1 and r1.object_id = user_id + left join stations as dep_station on checkin_station_id = dep_station.eva and in_transit.backend_id = dep_station.source + left join stations as arr_station on checkout_station_id = arr_station.eva and in_transit.backend_id = arr_station.source + left join backends as backend on in_transit.backend_id = backend.id + order by checkin_time desc + ; + + update schema_version set version = 66; + } + ); + }, + + # v66 -> v67 + # Add language settings to profile + sub { + my ($db) = @_; + $db->query( + qq{ + drop view users_with_backend; + alter table users add column language varchar(128); + update schema_version set version = 67; + create view users_with_backend as select + users.id as id, users.name as name, status, public_level, + language, email, password, registered_at, last_seen, + deletion_requested, deletion_notified, use_history, + accept_follows, notifications, profile, backend_id, iris, + hafas, efa, dbris, motis, backend.name as backend_name + from users + left join backends as backend on users.backend_id = backend.id + ; + } + ); + }, + + # v67 -> v68 + # Of course there are backends with stop names that are >64 chars long + sub { + my ($db) = @_; + $db->query( + qq{ + drop view stations_str; + drop view stations_with_external_ids; + drop view in_transit_str; + drop view journeys_str; + drop view follows_in_transit; + alter table stations alter column name type varchar(128); + create view stations_str as + select stations.name as name, + eva, lat, lon, + backends.name as backend, + dbris as is_dbris, + efa as is_efa, + iris as is_iris, + hafas as is_hafas, + motis as is_motis + from stations + left join backends + on source = backends.id; + create view stations_with_external_ids as select + stations.*, stations_external_ids.external_id + from stations + left join stations_external_ids on + stations.eva = stations_external_ids.eva and + stations.source = stations_external_ids.backend_id + ; + create view in_transit_str as select + user_id, + backend.iris as is_iris, backend.hafas as is_hafas, + backend.efa as is_efa, backend.dbris as is_dbris, + backend.motis as is_motis, + backend.name as backend_name, in_transit.backend_id as backend_id, + train_type, train_line, train_no, train_id, + extract(epoch from checkin_time) as checkin_ts, + extract(epoch from sched_departure) as sched_dep_ts, + extract(epoch from real_departure) as real_dep_ts, + checkin_station_id as dep_eva, + dep_station.ds100 as dep_ds100, + dep_station.name as dep_name, + dep_station.lat as dep_lat, + dep_station.lon as dep_lon, + dep_station_external_id.external_id as dep_external_id, + extract(epoch from checkout_time) as checkout_ts, + extract(epoch from sched_arrival) as sched_arr_ts, + extract(epoch from real_arrival) as real_arr_ts, + checkout_station_id as arr_eva, + arr_station.ds100 as arr_ds100, + arr_station.name as arr_name, + arr_station.lat as arr_lat, + arr_station.lon as arr_lon, + arr_station_external_id.external_id as arr_external_id, + polyline_id, + polylines.polyline as polyline, + visibility, + coalesce(visibility, users.public_level & 127) as effective_visibility, + cancelled, route, messages, user_data, + dep_platform, arr_platform, data + from in_transit + left join polylines on polylines.id = polyline_id + left join users on users.id = user_id + left join stations as dep_station on checkin_station_id = dep_station.eva and in_transit.backend_id = dep_station.source + left join stations as arr_station on checkout_station_id = arr_station.eva and in_transit.backend_id = arr_station.source + left join stations_external_ids as dep_station_external_id on checkin_station_id = dep_station_external_id.eva and in_transit.backend_id = dep_station_external_id.backend_id + left join stations_external_ids as arr_station_external_id on checkout_station_id = arr_station_external_id.eva and in_transit.backend_id = arr_station_external_id.backend_id + left join backends as backend on in_transit.backend_id = backend.id + ; + create view journeys_str as select + journeys.id as journey_id, user_id, + backend.iris as is_iris, backend.hafas as is_hafas, + backend.efa as is_efa, backend.dbris as is_dbris, + backend.motis as is_motis, + backend.name as backend_name, journeys.backend_id as backend_id, + train_type, train_line, train_no, train_id, + extract(epoch from checkin_time) as checkin_ts, + extract(epoch from sched_departure) as sched_dep_ts, + extract(epoch from real_departure) as real_dep_ts, + checkin_station_id as dep_eva, + dep_station.ds100 as dep_ds100, + dep_station.name as dep_name, + dep_station.lat as dep_lat, + dep_station.lon as dep_lon, + dep_station_external_id.external_id as dep_external_id, + extract(epoch from checkout_time) as checkout_ts, + extract(epoch from sched_arrival) as sched_arr_ts, + extract(epoch from real_arrival) as real_arr_ts, + checkout_station_id as arr_eva, + arr_station.ds100 as arr_ds100, + arr_station.name as arr_name, + arr_station.lat as arr_lat, + arr_station.lon as arr_lon, + arr_station_external_id.external_id as arr_external_id, + polylines.polyline as polyline, + visibility, + coalesce(visibility, users.public_level & 127) as effective_visibility, + cancelled, edited, route, messages, user_data, + dep_platform, arr_platform + from journeys + left join polylines on polylines.id = polyline_id + left join users on users.id = user_id + left join stations as dep_station on checkin_station_id = dep_station.eva and journeys.backend_id = dep_station.source + left join stations as arr_station on checkout_station_id = arr_station.eva and journeys.backend_id = arr_station.source + left join stations_external_ids as dep_station_external_id on checkin_station_id = dep_station_external_id.eva and journeys.backend_id = dep_station_external_id.backend_id + left join stations_external_ids as arr_station_external_id on checkout_station_id = arr_station_external_id.eva and journeys.backend_id = arr_station_external_id.backend_id + left join backends as backend on journeys.backend_id = backend.id + ; + create view follows_in_transit as select + r1.subject_id as follower_id, user_id as followee_id, + users.name as followee_name, + train_type, train_line, train_no, train_id, + backend.iris as is_iris, backend.hafas as is_hafas, + backend.efa as is_efa, backend.dbris as is_dbris, + backend.motis as is_motis, + backend.name as backend_name, in_transit.backend_id as backend_id, + extract(epoch from checkin_time) as checkin_ts, + extract(epoch from sched_departure) as sched_dep_ts, + extract(epoch from real_departure) as real_dep_ts, + checkin_station_id as dep_eva, + dep_station.ds100 as dep_ds100, + dep_station.name as dep_name, + dep_station.lat as dep_lat, + dep_station.lon as dep_lon, + extract(epoch from checkout_time) as checkout_ts, + extract(epoch from sched_arrival) as sched_arr_ts, + extract(epoch from real_arrival) as real_arr_ts, + checkout_station_id as arr_eva, + arr_station.ds100 as arr_ds100, + arr_station.name as arr_name, + arr_station.lat as arr_lat, + arr_station.lon as arr_lon, + polyline_id, + polylines.polyline as polyline, + visibility, + coalesce(visibility, users.public_level & 127) as effective_visibility, + cancelled, route, messages, user_data, + dep_platform, arr_platform, data + from in_transit + left join polylines on polylines.id = polyline_id + left join users on users.id = user_id + left join relations as r1 on r1.predicate = 1 and r1.object_id = user_id + left join stations as dep_station on checkin_station_id = dep_station.eva and in_transit.backend_id = dep_station.source + left join stations as arr_station on checkout_station_id = arr_station.eva and in_transit.backend_id = arr_station.source + left join backends as backend on in_transit.backend_id = backend.id + order by checkin_time desc + ; + update schema_version set version = 68; + } + ); + }, + + # v68 -> v69 + # Incorporate dbdb (entry/exit direction) data into travelynx + # This avoids having to make web requests to lib.finalrewind.org/dbdb, + # and allows for also showing the exit direction for intermediate stops. + sub { + my ($db) = @_; + $db->query( + qq{ + alter table schema_version + add column dbdb varchar(12); + create table bahn_platform_directions ( + eva integer primary key, + data jsonb not null + ); + } + ); + sync_dbdb($db); + $db->query( + qq{ + update schema_version set version = 69; + update schema_version set dbdb = '2025-10-27'; + } + ); + }, ); +sub sync_dbdb { + my ($db) = @_; + + my $json = JSON->new; + + for my $file ( read_dir( 'ext/dbdb/s', prefix => 1 ) ) { + if ( $file !~ m{\.txt$} ) { + next; + } + + my %station; + for my $line ( read_file( $file, { binmode => ':encoding(utf-8)' } ) ) { + if ( $line + =~ m{ ^ \s* (?<platform> \d+ ) \s+ (?<type> \S+ ) \s+ (?<direction> \S+ ) }x + ) + { + $station{ $+{platform} } = { + kopfgleis => $+{type} eq 'K' ? \1 : \0, + direction => $+{direction}, + }; + } + elsif ( $line + =~ m{ ^ @ \s* (?<stations> [^:]+ ) : \s* (?<platforms> .+ ) $ }x + ) + { + my $stations_raw = $+{stations}; + my $platforms_raw = $+{platforms}; + my @stations = split( qr{, }, $stations_raw ); + my @platforms = split( qr{, }, $platforms_raw ); + for my $platform (@platforms) { + my ( $number, $direction ) = split( qr{ }, $platform ); + for my $from_station (@stations) { + $station{$number}{direction_from}{$from_station} + = $direction; + } + } + } + } + my ($station_name) = ( $file =~ m{ s / ([^.]*) . txt $ }x ); + my ($station) + = Travel::Status::DE::IRIS::Stations::get_station($station_name); + if ( $station and $station->[0] eq $station_name ) { + $db->insert( + 'bahn_platform_directions', + { + eva => $station->[2], + data => $json->encode( \%station ) + }, + { on_conflict => \'(eva) do update set data = EXCLUDED.data' } + ); + } + elsif ( not $station ) { + say STDERR "DBDB import: unknown station: $station_name"; + } + else { + say STDERR +"DBDB import: station mismatch: wanted to import $station_name, but got " + . $station->[0]; + } + } +} + sub sync_stations { my ( $db, $iris_version ) = @_; diff --git a/lib/Travelynx/Command/dumpstops.pm b/lib/Travelynx/Command/dumpstops.pm index 4d20bbd..15f5861 100644 --- a/lib/Travelynx/Command/dumpstops.pm +++ b/lib/Travelynx/Command/dumpstops.pm @@ -1,6 +1,6 @@ package Travelynx::Command::dumpstops; -# Copyright (C) 2024 Birte Kristina Friesel +# Copyright (C) 2024-2025 Birte Kristina Friesel # # SPDX-License-Identifier: AGPL-3.0-or-later @@ -24,13 +24,13 @@ sub run { or die("open($filename): $!\n"); my $csv = Text::CSV->new( { eol => "\r\n" } ); - $csv->combine(qw(name eva lat lon backend is_iris is_hafas)); + $csv->combine(qw(name eva lat lon backend is_dbris is_efa is_iris is_hafas is_motis)); print $fh $csv->string; my $iter = $self->app->stations->get_db_iterator; while ( my $row = $iter->hash ) { $csv->combine( - @{$row}{qw{name eva lat lon backend is_iris is_hafas}} ); + @{$row}{qw{name eva lat lon backend is_dbris is_efa is_iris is_hafas is_motis}} ); print $fh $csv->string; } close($fh); diff --git a/lib/Travelynx/Command/integritycheck.pm b/lib/Travelynx/Command/integritycheck.pm index be5fe71..907d484 100644 --- a/lib/Travelynx/Command/integritycheck.pm +++ b/lib/Travelynx/Command/integritycheck.pm @@ -76,7 +76,8 @@ sub run { my %notified; my $rename = $self->app->renamed_station; - my $res = $db->select( 'journeys', [ 'route', 'edited' ] )->expand; + my $res = $db->select( 'journeys', [ 'backend_id', 'route', 'edited' ] ) + ->expand; while ( my $j = $res->hash ) { if ( $j->{edited} & 0x0010 ) { @@ -89,8 +90,10 @@ sub run { $stop->[0] = $rename->{ $stop->[0] }; } } - my @unknown - = $self->app->stations->grep_unknown( map { $_->[0] } @stops ); + my @unknown = $self->app->stations->grep_unknown( + backend_id => $j->{backend_id}, + names => [ map { $_->[0] } @stops ] + ); for my $stop_name (@unknown) { if ( not $notified{$stop_name} ) { if ( not $found ) { diff --git a/lib/Travelynx/Command/maintenance.pm b/lib/Travelynx/Command/maintenance.pm index 7baf762..7a8ae16 100644 --- a/lib/Travelynx/Command/maintenance.pm +++ b/lib/Travelynx/Command/maintenance.pm @@ -121,7 +121,7 @@ sub run { push( @uids_to_delete, $to_delete->arrays->map( sub { shift->[0] } )->each ); - if ( @uids_to_delete > 10 ) { + if ( @uids_to_delete > 60 ) { printf STDERR ( "About to delete %d accounts, which is quite a lot.\n", scalar @uids_to_delete diff --git a/lib/Travelynx/Command/stats.pm b/lib/Travelynx/Command/stats.pm new file mode 100644 index 0000000..953c75d --- /dev/null +++ b/lib/Travelynx/Command/stats.pm @@ -0,0 +1,59 @@ +package Travelynx::Command::stats; + +# Copyright (C) 2020-2023 Birte Kristina Friesel +# +# SPDX-License-Identifier: AGPL-3.0-or-later +use Mojo::Base 'Mojolicious::Command'; + +use DateTime; + +has description => 'Deal with monthly and yearly statistics'; + +has usage => sub { shift->extract_usage }; + +sub refresh_all { + my ($self) = @_; + + my $db = $self->app->pg->db; + my $now = DateTime->now( time_zone => 'Europe/Berlin' ); + + say 'Refreshing all stats, this may take a while ...'; + + my $total = $db->select( 'users', 'count(*) as count', { status => 1 } ) + ->hash->{count}; + my $i = 1; + + for + my $user ( $db->select( 'users', ['id'], { status => 1 } )->hashes->each ) + { + $self->app->journeys->generate_missing_stats( uid => $user->{id} ); + $self->app->journeys->get_stats( + uid => $user->{id}, + year => $now->year, + write_only => 1, + ); + if ( $i == $total or ( $i % 10 ) == 0 ) { + printf( "%.f%% complete\n", $i * 100 / $total ); + } + $i++; + } +} + +sub run { + my ( $self, $cmd, @arg ) = @_; + + if ( $cmd eq 'refresh-all' ) { + $self->refresh_all(@arg); + } + +} + +1; + +__END__ + +=head1 SYNOPSIS + + Usage: index.pl stats refresh-all + + Refreshes all stats diff --git a/lib/Travelynx/Command/translation.pm b/lib/Travelynx/Command/translation.pm new file mode 100644 index 0000000..cc3a5ac --- /dev/null +++ b/lib/Travelynx/Command/translation.pm @@ -0,0 +1,99 @@ +package Travelynx::Command::translation; + +# Copyright (C) 2025 Birte Kristina Friesel +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +use Mojo::Base 'Mojolicious::Command'; +use Travelynx::Helper::Locales; + +has description => 'Export translation status'; + +has usage => sub { shift->extract_usage }; + +sub run { + my ( $self, $command ) = @_; + + my @locales = (qw(de-DE en-GB fr-FR hu-HU pl-PL)); + + my %count; + my %handle; + for my $locale (@locales) { + $handle{$locale} = Travelynx::Helper::Locales->get_handle($locale); + $handle{$locale}->fail_with('failure_handler_auto'); + $count{$locale} = 0; + } + + binmode( STDOUT, ':encoding(utf-8)' ); + + if ( not $command ) { + $self->help; + } + elsif ( $command eq 'update-ref' ) { + my @buf; + + open( my $fh, '<:encoding(utf-8)', 'share/locales/de_DE.po' ); + my $comment; + for my $line (<$fh>) { + chomp $line; + if ( $line =~ m{ ^ [#] \s+ (.*) $ }x ) { + push( @buf, "## $1\n" ); + } + elsif ( $line =~ m{ ^ [#] , \s+ (.*) $ }x ) { + $comment = $1; + } + elsif ( $line =~ m{ ^ msgid \s+ " (.*) " $ }x ) { + my $id = $1; + push( @buf, "### ${id}\n" ); + if ($comment) { + push( @buf, '*' . $comment . "*\n" ); + $comment = undef; + } + for my $locale (@locales) { + my $translation = $handle{$locale}->maketext($id); + if ( $translation ne $id ) { + push( @buf, "* ${locale}: ${translation}" ); + $count{$locale} += 1; + } + else { + push( @buf, "* ${locale} *missing*" ); + } + } + push( @buf, q{} ); + } + } + close($fh); + + open( $fh, '>:encoding(utf-8)', 'share/locales/reference.md' ); + say $fh '# Translation Status'; + say $fh q{}; + for my $locale (@locales) { + say $fh sprintf( + '* %s: %.1f%% complete (%d missing)', + $locale, + $count{$locale} * 100 / $count{'de-DE'}, + $count{'de-DE'} - $count{$locale}, + ); + } + say $fh q{}; + for my $line (@buf) { + say $fh $line; + } + close($fh); + } + else { + $self->help; + } +} + +1; + +__END__ + +=head1 SYNOPSIS + + Usage: index.pl translation <command> + + Supported commands: + + * update-ref: update share/locales/reference.md diff --git a/lib/Travelynx/Command/work.pm b/lib/Travelynx/Command/work.pm index 60417b1..dc58a48 100644 --- a/lib/Travelynx/Command/work.pm +++ b/lib/Travelynx/Command/work.pm @@ -18,7 +18,7 @@ has description => 'Update real-time data of active journeys'; has usage => sub { shift->extract_usage }; sub run { - my ($self) = @_; + my ( $self, $backend ) = @_; my $now = DateTime->now( time_zone => 'Europe/Berlin' ); my $checkin_deadline = $now->clone->subtract( hours => 48 ); @@ -53,16 +53,34 @@ sub run { my $arr = $entry->{arr_eva}; my $train_id = $entry->{train_id}; - if ( $entry->{is_dbris} ) { + if ( $train_id eq 'manual' ) { + if ( $arr + and $entry->{real_arr_ts} + and $now->epoch - $entry->{real_arr_ts} > 900 ) + { + $self->app->checkout_p( + station => $arr, + force => 2, + dep_eva => $dep, + arr_eva => $arr, + uid => $uid + )->wait; + } + } + + elsif ( $entry->{is_dbris} and ( not $backend or $backend eq 'dbris' ) ) + { eval { - Mojo::Promise->timer( $dbris_rate_limited ? 4.5 : 1.0 )->then( + Mojo::Promise->timer( + $dbris_rate_limited ? 4.5 : ( $backend ? 2.0 : 1.0 ) ) + ->then( sub { return $self->app->dbris->get_journey_p( trip_id => $train_id ); } - )->then( + )->then( sub { my ($journey) = @_; @@ -155,7 +173,7 @@ sub run { )->wait; } } - )->catch( + )->catch( sub { my ($err) = @_; $self->app->log->debug( @@ -169,11 +187,11 @@ sub run { $backend_issues += 1; } } - )->wait; + )->wait; if ( $arr and $entry->{real_arr_ts} - and $now->epoch - $entry->{real_arr_ts} > 600 ) + and $now->epoch - $entry->{real_arr_ts} > 900 ) { $self->app->checkout_p( station => $arr, @@ -189,10 +207,9 @@ sub run { $self->app->log->error( "work($uid) @ DBRIS $entry->{backend_name}: $@"); } - next; } - if ( $entry->{is_efa} ) { + elsif ( $entry->{is_efa} and ( not $backend or $backend eq 'efa' ) ) { eval { $self->app->efa->get_journey_p( trip_id => $train_id, @@ -269,7 +286,7 @@ sub run { if ( $arr and $entry->{real_arr_ts} - and $now->epoch - $entry->{real_arr_ts} > 600 ) + and $now->epoch - $entry->{real_arr_ts} > 900 ) { $self->app->checkout_p( station => $arr, @@ -285,10 +302,10 @@ sub run { $self->app->log->error( "work($uid) @ EFA $entry->{backend_name}: $@"); } - next; } - if ( $entry->{is_motis} ) { + elsif ( $entry->{is_motis} and ( not $backend or $backend eq 'motis' ) ) + { eval { $self->app->motis->get_trip_p( @@ -309,6 +326,10 @@ sub run { stop => $stopover->stop, motis => $entry->{backend_name}, ); + + $self->app->log->debug( "mapped " + . $stopover->stop->id . " to " + . $stopover->stop->{eva} ); } } @@ -358,7 +379,7 @@ sub run { )->catch( sub { my ($err) = @_; - $self->app->log->error( + $self->app->log->debug( "work($uid) @ MOTIS $entry->{backend_name}: journey: $err" ); } @@ -366,7 +387,7 @@ sub run { if ( $arr and $entry->{real_arr_ts} - and $now->epoch - $entry->{real_arr_ts} > 600 ) + and $now->epoch - $entry->{real_arr_ts} > 900 ) { $self->app->checkout_p( station => $arr, @@ -382,10 +403,10 @@ sub run { $self->app->log->error( "work($uid) @ MOTIS $entry->{backend_name}: $@"); } - next; } - if ( $entry->{is_hafas} ) { + elsif ( $entry->{is_hafas} and ( not $backend or $backend eq 'hafas' ) ) + { eval { @@ -437,8 +458,9 @@ sub run { is_departure => 1, eva => $dep, datetime => $found_dep->sched_dep, - train_type => $journey->type =~ s{ +$}{}r, - train_no => $journey->number, + train_type => ( $journey->type // q{} ) + =~ s{ +$}{}r, + train_no => $journey->number, ); $self->app->add_stationinfo( $uid, 1, $journey->id, $found_dep->loc->eva ); @@ -500,7 +522,7 @@ sub run { if ( $arr and $entry->{real_arr_ts} - and $now->epoch - $entry->{real_arr_ts} > 600 ) + and $now->epoch - $entry->{real_arr_ts} > 900 ) { $self->app->checkout_p( station => $arr, @@ -516,7 +538,6 @@ sub run { $self->app->log->error( "work($uid) @ HAFAS $entry->{backend_name}: $@"); } - next; } # TODO irgendwo ist hier ne race condition wo ein neuer checkin (in HAFAS) mit IRIS-Daten überschrieben wird. @@ -528,182 +549,186 @@ sub run { # update departure data for up to 15 minutes after departure and # delaying automatic checkout by at least 10 minutes. - eval { - if ( $now->epoch - $entry->{real_dep_ts} < 900 ) { - my $status = $self->app->iris->get_departures( - station => $dep, - lookbehind => 30, - lookahead => 30 - ); - if ( $status->{errstr} ) { - die("get_departures($dep): $status->{errstr}\n"); - } - - my ($train) = List::Util::first { $_->train_id eq $train_id } - @{ $status->{results} }; + elsif ( $entry->{is_iris} and ( not $backend or $backend eq 'iris' ) ) { + eval { + if ( $now->epoch - $entry->{real_dep_ts} < 900 ) { + my $status = $self->app->iris->get_departures( + station => $dep, + lookbehind => 30, + lookahead => 30 + ); + if ( $status->{errstr} ) { + die("get_departures($dep): $status->{errstr}\n"); + } - if ( not $train ) { - $self->app->log->debug( - "could not find train $train_id at $dep\n"); - return; - } + my ($train) + = List::Util::first { $_->train_id eq $train_id } + @{ $status->{results} }; - $self->app->in_transit->update_departure( - uid => $uid, - train => $train, - dep_eva => $dep, - arr_eva => $arr, - route => [ $self->app->iris->route_diff($train) ] - ); + if ( not $train ) { + $self->app->log->debug( + "could not find train $train_id at $dep\n"); + return; + } - if ( $train->departure_is_cancelled and $arr ) { - my $checked_in - = $self->app->in_transit->update_departure_cancelled( + $self->app->in_transit->update_departure( uid => $uid, train => $train, dep_eva => $dep, arr_eva => $arr, - ); - - # depending on the amount of users in transit, some time may - # have passed between fetching $entry from the database and - # now. Only check out if the user is still checked into this - # train. - if ($checked_in) { + route => [ $self->app->iris->route_diff($train) ] + ); - # check out (adds a cancelled journey and resets journey state - # to checkin - $self->app->checkout_p( - station => $arr, - force => 2, + if ( $train->departure_is_cancelled and $arr ) { + my $checked_in + = $self->app->in_transit->update_departure_cancelled( + uid => $uid, + train => $train, dep_eva => $dep, arr_eva => $arr, - uid => $uid - )->wait; + ); + + # depending on the amount of users in transit, some time may + # have passed between fetching $entry from the database and + # now. Only check out if the user is still checked into this + # train. + if ($checked_in) { + + # check out (adds a cancelled journey and resets journey state + # to checkin + $self->app->checkout_p( + station => $arr, + force => 2, + dep_eva => $dep, + arr_eva => $arr, + uid => $uid + )->wait; + } + } + else { + $self->app->add_route_timestamps( $uid, $train, 1 ); + $self->app->add_wagonorder( + uid => $uid, + train_id => $train->train_id, + is_departure => 1, + eva => $dep, + datetime => $train->sched_departure, + train_type => $train->type, + train_no => $train->train_no + ); + $self->app->add_stationinfo( $uid, 1, $train->train_id, + $dep, $arr ); } } - else { - $self->app->add_route_timestamps( $uid, $train, 1 ); - $self->app->add_wagonorder( - uid => $uid, - train_id => $train->train_id, - is_departure => 1, - eva => $dep, - datetime => $train->sched_departure, - train_type => $train->type, - train_no => $train->train_no - ); - $self->app->add_stationinfo( $uid, 1, $train->train_id, - $dep, $arr ); - } + }; + if ($@) { + $errors += 1; + $self->app->log->error("work($uid) @ IRIS: departure: $@"); } - }; - if ($@) { - $errors += 1; - $self->app->log->error("work($uid) @ IRIS: departure: $@"); - } - eval { - if ( - $arr - and ( not $entry->{real_arr_ts} - or $now->epoch - $entry->{real_arr_ts} < 600 ) - ) - { - my $status = $self->app->iris->get_departures( - station => $arr, - lookbehind => 20, - lookahead => 220 - ); - if ( $status->{errstr} ) { - die("get_departures($arr): $status->{errstr}\n"); - } + eval { + if ( + $arr + and ( not $entry->{real_arr_ts} + or $now->epoch - $entry->{real_arr_ts} < 600 ) + ) + { + my $status = $self->app->iris->get_departures( + station => $arr, + lookbehind => 20, + lookahead => 220 + ); + if ( $status->{errstr} ) { + die("get_departures($arr): $status->{errstr}\n"); + } - # 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 > $entry->{sched_dep_ts} - } - @{ $status->{results} }; + # 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 > $entry->{sched_dep_ts} + } + @{ $status->{results} }; - $train //= List::Util::first { $_->train_id eq $train_id } - @{ $status->{results} }; + $train //= List::Util::first { $_->train_id eq $train_id } + @{ $status->{results} }; - if ( not $train ) { + if ( not $train ) { - # If we haven't seen the train yet, its arrival is probably - # too far in the future. This is not critical. - return; - } + # If we haven't seen the train yet, its arrival is probably + # too far in the future. This is not critical. + return; + } - my $checked_in = $self->app->in_transit->update_arrival( - uid => $uid, - train => $train, - route => [ $self->app->iris->route_diff($train) ], - dep_eva => $dep, - arr_eva => $arr, - ); + my $checked_in = $self->app->in_transit->update_arrival( + uid => $uid, + train => $train, + route => [ $self->app->iris->route_diff($train) ], + dep_eva => $dep, + arr_eva => $arr, + ); - if ( $checked_in and $train->arrival_is_cancelled ) { + if ( $checked_in and $train->arrival_is_cancelled ) { - # check out (adds a cancelled journey and resets journey state - # to destination selection) - $self->app->checkout_p( + # check out (adds a cancelled journey and resets journey state + # to destination selection) + $self->app->checkout_p( + station => $arr, + force => 0, + dep_eva => $dep, + arr_eva => $arr, + uid => $uid + )->wait; + } + else { + $self->app->add_route_timestamps( + $uid, $train, 0, + ( + defined $entry->{real_arr_ts} + and $now->epoch > $entry->{real_arr_ts} + ) ? 1 : 0 + ); + $self->app->add_wagonorder( + uid => $uid, + train_id => $train->train_id, + is_arrival => 1, + eva => $arr, + datetime => $train->sched_departure, + train_type => $train->type, + train_no => $train->train_no + ); + $self->app->add_stationinfo( $uid, 0, $train->train_id, + $dep, $arr ); + } + } + elsif ( $entry->{real_arr_ts} ) { + my ( undef, $error ) = $self->app->checkout_p( station => $arr, - force => 0, + force => 2, dep_eva => $dep, arr_eva => $arr, uid => $uid + )->catch( + sub { + my ($error) = @_; + $backend_issues += 1; + $self->app->log->error( + "work($uid) @ IRIS: arrival: $error"); + $errors += 1; + } )->wait; } - else { - $self->app->add_route_timestamps( - $uid, $train, 0, - ( - defined $entry->{real_arr_ts} - and $now->epoch > $entry->{real_arr_ts} - ) ? 1 : 0 - ); - $self->app->add_wagonorder( - uid => $uid, - train_id => $train->train_id, - is_arrival => 1, - eva => $arr, - datetime => $train->sched_departure, - train_type => $train->type, - train_no => $train->train_no - ); - $self->app->add_stationinfo( $uid, 0, $train->train_id, - $dep, $arr ); - } - } - elsif ( $entry->{real_arr_ts} ) { - my ( undef, $error ) = $self->app->checkout_p( - station => $arr, - force => 2, - dep_eva => $dep, - arr_eva => $arr, - uid => $uid - )->catch( - sub { - my ($error) = @_; - $backend_issues += 1; - $self->app->log->error( - "work($uid) @ IRIS: arrival: $error"); - $errors += 1; - } - )->wait; + }; + if ($@) { + $self->app->log->error("work($uid) @ IRIS: arrival: $@"); + $errors += 1; } - }; - if ($@) { - $self->app->log->error("work($uid) @ IRIS: arrival: $@"); - $errors += 1; + + eval { }; } - eval { }; } my $started_at = $now; @@ -711,15 +736,19 @@ sub run { my $worker_duration = $main_finished_at->epoch - $started_at->epoch; if ( $self->app->config->{influxdb}->{url} ) { + my $tags = q{}; + if ($backend) { + $tags .= ",backend=${backend}"; + } if ( $self->app->mode eq 'development' ) { $self->app->log->debug( 'POST ' . $self->app->config->{influxdb}->{url} - . " worker runtime_seconds=${worker_duration},errors=${errors},backend_errors=${backend_issues},ratelimit_count=${rate_limit_counts}" + . " worker${tags} runtime_seconds=${worker_duration},errors=${errors},backend_errors=${backend_issues},ratelimit_count=${rate_limit_counts}" ); } else { $self->app->ua->post_p( $self->app->config->{influxdb}->{url}, -"worker runtime_seconds=${worker_duration},errors=${errors},backend_errors=${backend_issues},ratelimit_count=${rate_limit_counts}" +"worker${tags} runtime_seconds=${worker_duration},errors=${errors},backend_errors=${backend_issues},ratelimit_count=${rate_limit_counts}" )->wait; } } |
