diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | .gitmodules | 3 | ||||
-rw-r--r-- | Changelog | 6 | ||||
-rwxr-xr-x | bin/efa-m | 20 | ||||
m--------- | ext/transport-apis | 0 | ||||
-rw-r--r-- | lib/Travel/Status/DE/EFA.pm | 133 | ||||
-rw-r--r-- | lib/Travel/Status/DE/EFA/Services.pm.PL | 147 |
7 files changed, 187 insertions, 123 deletions
@@ -9,3 +9,4 @@ /META.json /MYMETA.yml /MYMETA.json +/lib/Travel/Status/DE/EFA/Services.pm diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..e1af5dd --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "ext/transport-apis"] + path = ext/transport-apis + url = https://github.com/public-transport/transport-apis.git @@ -4,11 +4,13 @@ git HEAD * Switch API language from XML to JSON * Rename Travel::Status::DE::EFA::Result to Travel::Status::DE::EFA::Departure * EFA: Remove "identified_data" accessor (not supported by JSON backends) - * Result/Departure: Remove "info" accessor * Result/Departure: Add "hints" accessor - * Stop: Remove "name_suf" accessor + * Result/Departure: Remove "info" accessor; use "hints" instead * Stop: Add "place", "full_name" and "occupancy" accessors + * Stop: Remove "name_suf" accessor; use "name" instead * Line: Add "number" accessor + * EFA: Remove "get_efa_urls" and "get_service" functions; use the newly + introduced Travel::Status::DE::EFA::Services package instead. * efa-m: -Oa, -Ob, -Of: show per-stop occupancy, if available Travel::Status::DE::VRR 2.02 - Sun May 19 2024 @@ -12,6 +12,7 @@ use Encode qw(decode); use Getopt::Long qw(:config no_ignore_case bundling); use List::Util qw(first max none); use Travel::Status::DE::EFA; +use Travel::Status::DE::EFA::Services; my $service = 'VRR'; my $efa_url; @@ -112,7 +113,7 @@ if ($efa_url) { $service = undef; } elsif ($service) { - my $service_ref = Travel::Status::DE::EFA::get_service($service); + my $service_ref = Travel::Status::DE::EFA::Services::get_service($service); if ( not $service_ref ) { printf STDERR ( "Error: Unknown service '%s'. See 'efa-m --list' for a " @@ -156,8 +157,11 @@ sub show_help { sub show_services { printf( "%-45s %-14s %s\n\n", 'service', 'abbr. (-s)', 'url (-u)' ); - for my $service ( Travel::Status::DE::EFA::get_efa_urls() ) { - printf( "%-45s %-14s %s\n", @{$service}{qw(name shortname url)} ); + for my $shortname ( Travel::Status::DE::EFA::Services::get_service_ids() ) { + my $service + = Travel::Status::DE::EFA::Services::get_service($shortname); + printf( "%-45s %-14s %s\n", + $service->{name}, $shortname, $service->{url} ); } exit 0; @@ -437,16 +441,18 @@ sub show_results { } if ( $discover or $discover_and_print ) { - for my $service_ref ( Travel::Status::DE::EFA::get_efa_urls() ) { - $efa = new_efa( $service_ref->{shortname} ); + for my $shortname ( Travel::Status::DE::EFA::Services::get_service_ids() ) { + $efa = new_efa($shortname); if ( $efa and not $efa->errstr ) { if ($discover_and_print) { last; } + my $service_ref + = Travel::Status::DE::EFA::Services::get_service($shortname); printf( "%s / %s (%s)\n -> efa-m -s %s %s\n\n", - @{$service_ref}{qw(name shortname url shortname)}, - join( q{ }, map { "'$_'" } @ARGV ), + $service_ref->{name}, $shortname, $service_ref->{url}, + $shortname, join( q{ }, map { "'$_'" } @ARGV ), ); } } diff --git a/ext/transport-apis b/ext/transport-apis new file mode 160000 +Subproject 16ded4b86256cba98c19fc0967c54ad7554f77e diff --git a/lib/Travel/Status/DE/EFA.pm b/lib/Travel/Status/DE/EFA.pm index bd1fe48..b95ff41 100644 --- a/lib/Travel/Status/DE/EFA.pm +++ b/lib/Travel/Status/DE/EFA.pm @@ -12,79 +12,13 @@ use DateTime; use DateTime::Format::Strptime; use Encode qw(encode); use JSON; -use Travel::Status::DE::EFA::Line; use Travel::Status::DE::EFA::Departure; +use Travel::Status::DE::EFA::Line; +use Travel::Status::DE::EFA::Services; use Travel::Status::DE::EFA::Stop; use Travel::Status::DE::EFA::Trip; use LWP::UserAgent; -my %efa_instance = ( - BSVG => { - url => 'https://bsvg.efa.de/bsvagstd', - name => 'Braunschweiger Verkehrs-GmbH', - }, - DING => { - url => 'https://www.ding.eu/ding3', - name => 'Donau-Iller Nahverkehrsverbund', - }, - KVV => { - url => 'https://projekte.kvv-efa.de/sl3-alone', - name => 'Karlsruher Verkehrsverbund', - }, - LinzAG => { - url => 'https://www.linzag.at/static', - name => 'Linz AG', - encoding => 'iso-8859-15', - }, - MVV => { - url => 'https://efa.mvv-muenchen.de/mobile', - name => 'Münchner Verkehrs- und Tarifverbund', - }, - NVBW => { - url => 'https://www.efa-bw.de/nvbw', - name => 'Nahverkehrsgesellschaft Baden-Württemberg', - }, - VAG => { - url => 'https://efa.vagfr.de/vagfr3', - name => 'Freiburger Verkehrs AG', - }, - VGN => { - url => 'https://efa.vgn.de/vgnExt_oeffi', - name => 'Verkehrsverbund Grossraum Nuernberg', - }, - - # HTTPS: certificate verification fails - VMV => { - url => 'http://efa.vmv-mbh.de/vmv', - name => 'Verkehrsgesellschaft Mecklenburg-Vorpommern', - }, - VRN => { - url => 'https://www.vrn.de/mngvrn/', - name => 'Verkehrsverbund Rhein-Neckar', - }, - VRR => { - url => 'https://efa.vrr.de/vrr', - name => 'Verkehrsverbund Rhein-Ruhr', - }, - VRR2 => { - url => 'https://app.vrr.de/standard', - name => 'Verkehrsverbund Rhein-Ruhr (alternative)', - }, - VRR3 => { - url => 'https://efa.vrr.de/rbgstd3', - name => 'Verkehrsverbund Rhein-Ruhr (alternative alternative)', - }, - VVO => { - url => 'https://efa.vvo-online.de/VMSSL3', - name => 'Verkehrsverbund Oberelbe', - }, - VVS => { - url => 'https://www2.vvs.de/vvs', - name => 'Verkehrsverbund Stuttgart', - }, - -); - sub new_p { my ( $class, %opt ) = @_; my $promise = $opt{promise}->new; @@ -153,22 +87,27 @@ sub new { confess('type must be stop, stopID, address, or poi'); } - if ( $opt{service} and exists $efa_instance{ $opt{service} } ) { - $opt{efa_url} = $efa_instance{ $opt{service} }{url}; - if ( $opt{stopseq} ) { - $opt{efa_url} .= '/XML_STOPSEQCOORD_REQUEST'; - } - else { - $opt{efa_url} .= '/XML_DM_REQUEST'; + if ( $opt{service} ) { + if ( my $service + = Travel::Status::DE::EFA::Services::get_service( $opt{service} ) ) + { + $opt{efa_url} = $service->{url}; + if ( $opt{stopseq} ) { + $opt{efa_url} .= '/XML_STOPSEQCOORD_REQUEST'; + } + else { + $opt{efa_url} .= '/XML_DM_REQUEST'; + } + $opt{time_zone} //= $service->{time_zone}; } - $opt{time_zone} //= $efa_instance{ $opt{service} }{time_zone}; } + $opt{time_zone} //= 'Europe/Berlin'; + if ( not $opt{efa_url} ) { confess('service or efa_url must be specified'); } - my $dt = $opt{datetime} - // DateTime->now( time_zone => $opt{time_zone} // 'Europe/Berlin' ); + my $dt = $opt{datetime} // DateTime->now( time_zone => $opt{time_zone} ); ## no critic (RegularExpressions::ProhibitUnusedCapture) ## no critic (Variables::ProhibitPunctuationVars) @@ -216,11 +155,11 @@ sub new { service => $opt{service}, strp_stopseq => DateTime::Format::Strptime->new( pattern => '%Y%m%d %H:%M', - time_zone => 'Europe/Berlin', + time_zone => $opt{time_zone}, ), strp_stopseq_s => DateTime::Format::Strptime->new( pattern => '%Y%m%d %H:%M:%S', - time_zone => 'Europe/Berlin', + time_zone => $opt{time_zone}, ), json => JSON->new->utf8, @@ -465,18 +404,6 @@ sub result { return Travel::Status::DE::EFA::Trip->new( json => $self->{response} ); } -# static -sub get_efa_urls { - return map { - { %{ $efa_instance{$_} }, shortname => $_ } - } sort keys %efa_instance; -} - -sub get_service { - my ($service) = @_; - return $efa_instance{$service}; -} - 1; __END__ @@ -620,28 +547,6 @@ nothing (undef / empty list) otherwise. Returns a list of Travel::Status::DE::EFA::Departure(3pm) objects, each one describing one departure. -=item Travel::Status::DE::EFA::get_efa_urls() - -Returns a list of known EFA entry points. Each list element is a hashref with -the following elements. - -=over - -=item B<url>: service URL as passed to B<efa_url> - -=item B<name>: Name of the entity operating this service - -=item B<shortname>: Short name of the entity - -=item B<encoding>: Server-side encoding override for B<efa_encoding> (optional) - -=back - -=item Travel::Status::DE::EFA::service(I<$service>) - -Returns a hashref describing the service I<$service>, or undef if it is not -known. See B<get_efa_urls> for the hashref layout. - =back =head1 DIAGNOSTICS diff --git a/lib/Travel/Status/DE/EFA/Services.pm.PL b/lib/Travel/Status/DE/EFA/Services.pm.PL new file mode 100644 index 0000000..be3bffb --- /dev/null +++ b/lib/Travel/Status/DE/EFA/Services.pm.PL @@ -0,0 +1,147 @@ +#!/usr/bin/env perl + +use strict; +use warnings; +use 5.014; +use utf8; +use Data::Dumper; +use Encode qw(encode); +use File::Slurp qw(read_file write_file); +use JSON; + +my $json = JSON->new->utf8; + +sub load_instance { + my ( $path, %opt ) = @_; + + my $data = $json->decode( + scalar read_file("ext/transport-apis/data/${path}-efa.json") ); + my %ret = ( + name => $opt{name} // $data->{name} =~ s{ *[(][^)]+[)]}{}r, + homepage => $data->{attribution}{homepage}, + url => $opt{url} // $data->{options}{endpoint} =~ s{ / $ }{}rx, + time_zone => $data->{timezone}, + languages => $data->{supportedLanguages}, + coverage => { + area => $data->{coverage}{realtimeCoverage}{area}, + regions => $data->{coverage}{realtimeCoverage}{region} // [] + }, + ); + + return %ret; +} + +my %efa_instance = ( + BSVG => { + url => 'https://bsvg.efa.de/bsvagstd', + name => 'Braunschweiger Verkehrs-GmbH', + }, + DING => { + url => 'https://www.ding.eu/ding3', + name => 'Donau-Iller Nahverkehrsverbund', + }, + KVV => { load_instance('de/kvv') }, + LinzAG => { + url => 'https://www.linzag.at/static', + name => 'Linz AG', + encoding => 'iso-8859-15', + }, + MVV => { + url => 'https://efa.mvv-muenchen.de/mobile', + name => 'Münchner Verkehrs- und Tarifverbund', + }, + NVBW => { + url => 'https://www.efa-bw.de/nvbw', + name => 'Nahverkehrsgesellschaft Baden-Württemberg', + }, + VAG => { + url => 'https://efa.vagfr.de/vagfr3', + name => 'Freiburger Verkehrs AG', + }, + VGN => { + url => 'https://efa.vgn.de/vgnExt_oeffi', + name => 'Verkehrsverbund Grossraum Nuernberg', + }, + + # HTTPS: certificate verification fails + VMV => { + url => 'http://efa.vmv-mbh.de/vmv', + name => 'Verkehrsgesellschaft Mecklenburg-Vorpommern', + }, + VRN => { + url => 'https://www.vrn.de/mngvrn/', + name => 'Verkehrsverbund Rhein-Neckar', + }, + VRR => { + url => 'https://efa.vrr.de/vrr', + name => 'Verkehrsverbund Rhein-Ruhr', + }, + VRR2 => { + url => 'https://app.vrr.de/standard', + name => 'Verkehrsverbund Rhein-Ruhr (alternative)', + }, + VRR3 => { + url => 'https://efa.vrr.de/rbgstd3', + name => 'Verkehrsverbund Rhein-Ruhr (alternative alternative)', + }, + VVO => { + url => 'https://efa.vvo-online.de/VMSSL3', + name => 'Verkehrsverbund Oberelbe', + }, + VVS => { + url => 'https://www2.vvs.de/vvs', + name => 'Verkehrsverbund Stuttgart', + }, + +); + +my $buf = <<'__EOF__'; +package Travel::Status::DE::EFA::Services; + +# vim:readonly +# This package has been automatically generated +# by lib/Travel/Status/DE/EFA/Services.pm.PL. +# Do not edit, changes will be lost. + +use strict; +use warnings; +use 5.014; +use utf8; + +our $VERSION = '3.00'; + +# Most of these have been adapted from +# <https://github.com/public-transport/transport-apis> and +# <https://github.com/public-transport/hafas-client/tree/main/p>. +# Many thanks to Jannis R / @derhuerst and all contributors for maintaining +# these resources. + +__EOF__ + +my $perlobj = Data::Dumper->new( [ \%efa_instance ], ['efa_instance'] ); + +$buf .= 'my ' . $perlobj->Sortkeys(1)->Indent(0)->Dump; + +$buf .= <<'__EOF__'; + +sub get_service_ids { + return sort keys %{$efa_instance}; +} + +sub get_service { + my ($service) = @_; + return $efa_instance->{$service}; +} + +sub get_service_ref { + return $efa_instance; +} + +sub get_service_map { + return %{$efa_instance}; +} + +1; +__EOF__ + +write_file( $ARGV[0], { binmode => ':utf8' }, $buf ); |