diff options
Diffstat (limited to 'lib/DBInfoscreen.pm')
-rw-r--r-- | lib/DBInfoscreen.pm | 434 |
1 files changed, 214 insertions, 220 deletions
diff --git a/lib/DBInfoscreen.pm b/lib/DBInfoscreen.pm index 3acb969..18a2c87 100644 --- a/lib/DBInfoscreen.pm +++ b/lib/DBInfoscreen.pm @@ -1,66 +1,68 @@ package DBInfoscreen; -use Mojo::Base 'Mojolicious'; -# Copyright (C) 2011-2020 Daniel Friesel <derf+dbf@finalrewind.org> -# License: 2-Clause BSD +# Copyright (C) 2011-2020 Birte Kristina Friesel +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +use Mojo::Base 'Mojolicious'; use Cache::File; +use DBInfoscreen::Helper::DBRIS; +use DBInfoscreen::Helper::EFA; use DBInfoscreen::Helper::HAFAS; +use DBInfoscreen::Helper::MOTIS; use DBInfoscreen::Helper::Wagonorder; use File::Slurp qw(read_file); use JSON; -use Travel::Status::DE::HAFAS; -use Travel::Status::DE::HAFAS::StopFinder; -use Travel::Status::DE::IRIS::Stations; use utf8; -no if $] >= 5.018, warnings => 'experimental::smartmatch'; +sub startup { + my ($self) = @_; -our $VERSION = qx{git describe --dirty} || '0.05'; + $self->config( + hypnotoad => { + accepts => $ENV{DBFAKEDISPLAY_ACCEPTS} // 100, + clients => $ENV{DBFAKEDISPLAY_CLIENTS} // 10, + listen => [ $ENV{DBFAKEDISPLAY_LISTEN} // 'http://*:8092' ], + pid_file => $ENV{DBFAKEDISPLAY_PID_FILE} + // '/tmp/db-fakedisplay.pid', + spare => $ENV{DBFAKEDISPLAY_SPARE} // 2, + workers => $ENV{DBFAKEDISPLAY_WORKERS} // 2, + }, + lookahead => $ENV{DBFAKEDISPLAY_LOOKAHEAD} // 180, + source_url => 'https://github.com/derf/db-fakedisplay', + issue_url => 'https://github.com/derf/db-fakedisplay/issues', + version => $ENV{DBFAKEDISPLAY_VERSION} // qx{git describe --dirty} + // '???', + ); -chomp $VERSION; + chomp $self->config->{version}; + $self->defaults( version => $self->config->{version} // 'UNKNOWN' ); -my %default = ( - backend => 'iris', - mode => 'app', - admode => 'deparr', -); - -sub startup { - my ($self) = @_; + # Generally, the reverse proxy handles compression. + # Also, Mojolicious compression breaks legacy callback-based JSON endpoints + # for some clients. + $self->renderer->compress(0); $self->hook( before_dispatch => sub { my ($self) = @_; - # The "theme" cookie is set client-side if the theme we delivered was - # changed by dark mode detection or by using the theme switcher. It's - # not part of Mojolicious' session data (and can't be, due to - # signing and HTTPOnly), so we need to add it here. + # The "theme" cookie is set client-side if the theme we delivered was + # changed by dark mode detection or by using the theme switcher. It's + # not part of Mojolicious' session data (and can't be, due to + # signing and HTTPOnly), so we need to add it here. for my $cookie ( @{ $self->req->cookies } ) { if ( $cookie->name eq 'theme' ) { $self->session( theme => $cookie->value ); - return; } } } ); $self->attr( - cache_hafas => sub { - my ($self) = @_; - return Cache::File->new( - cache_root => $ENV{DBFAKEDISPLAY_HAFAS_CACHE} - // '/tmp/dbf-hafas', - default_expires => '180 seconds', - lock_level => Cache::File::LOCK_LOCAL(), - ); - } - ); - - $self->attr( cache_iris_main => sub { my ($self) = @_; return Cache::File->new( @@ -85,16 +87,51 @@ sub startup { ); $self->attr( - ice_type_map => sub { - my $ice_type_map = JSON->new->utf8->decode( - scalar read_file('share/ice_type.json') ); - my $ret; - while ( my ( $k, $v ) = each %{$ice_type_map} ) { - if ( $v->{type} ) { - $ret->{$k} = [ $v->{type}, $v->{short} ]; - } - } - return $ret; + dbdb_wagon => sub { + return JSON->new->utf8->decode( + scalar read_file('share/dbdb_wagen.json') ); + } + ); + + $self->helper( + dbris => sub { + my ($self) = @_; + state $efa = DBInfoscreen::Helper::DBRIS->new( + log => $self->app->log, + main_cache => $self->app->cache_iris_main, + realtime_cache => $self->app->cache_iris_rt, + root_url => $self->url_for('/')->to_abs, + user_agent => $self->ua, + version => $self->config->{version}, + ); + } + ); + + $self->helper( + motis => sub { + my ($self) = @_; + state $motis = DBInfoscreen::Helper::MOTIS->new( + log => $self->app->log, + main_cache => $self->app->cache_iris_main, + realtime_cache => $self->app->cache_iris_rt, + root_url => $self->url_for('/')->to_abs, + user_agent => $self->ua, + version => $self->config->{version}, + ); + } + ); + + $self->helper( + efa => sub { + my ($self) = @_; + state $efa = DBInfoscreen::Helper::EFA->new( + log => $self->app->log, + main_cache => $self->app->cache_iris_main, + realtime_cache => $self->app->cache_iris_rt, + root_url => $self->url_for('/')->to_abs, + user_agent => $self->ua, + version => $self->config->{version}, + ); } ); @@ -107,7 +144,7 @@ sub startup { realtime_cache => $self->app->cache_iris_rt, root_url => $self->url_for('/')->to_abs, user_agent => $self->ua, - version => $VERSION, + version => $self->config->{version}, ); } ); @@ -121,112 +158,40 @@ sub startup { realtime_cache => $self->app->cache_iris_rt, root_url => $self->url_for('/')->to_abs, user_agent => $self->ua, - version => $VERSION, + version => $self->config->{version}, ); } ); $self->helper( - 'handle_no_results' => sub { - my ( $self, $backend, $station, $errstr ) = @_; - - if ( $backend eq 'ris' ) { - my $db_service = Travel::Status::DE::HAFAS::get_service('DB'); - my $sf = Travel::Status::DE::HAFAS::StopFinder->new( - url => $db_service->{stopfinder}, - input => $station, - ); - my @candidates - = map { [ $_->{name}, $_->{id} ] } $sf->results; - if ( @candidates > 1 - or ( @candidates == 1 and $candidates[0][1] ne $station ) ) - { - $self->render( - 'landingpage', - stationlist => \@candidates, - hide_opts => 0 - ); - return; - } + wagon_image => sub { + my ( $self, $train_type, $wagon_type, $uic ) = @_; + my $ret; + if ( $train_type =~ m{IC(?!E)} + and $wagon_type + =~ m{ ^ [AB] R? k? [ipv] m m? b? d? s? z f? $ }x ) + { + $ret = $wagon_type; } - if ( $backend eq 'iris' ) { - my @candidates = map { [ $_->[1], $_->[0] ] } - Travel::Status::DE::IRIS::Stations::get_station($station); - if ( @candidates > 1 - or ( @candidates == 1 and $candidates[0][1] ne $station ) ) - { - $self->render( - 'landingpage', - stationlist => \@candidates, - hide_opts => 0 - ); - return; - } + elsif ( $train_type =~ m{IC2.TWIN} ) { + $ret = $wagon_type; } - $self->render( - 'landingpage', - error => ( $errstr // "Got no results for '$station'" ), - hide_opts => 0 - ); - return; - } - ); - - $self->helper( - 'handle_no_results_json' => sub { - my ( $self, $backend, $station, $errstr, $api_version ) = @_; - - my $callback = $self->param('callback'); - - $self->res->headers->access_control_allow_origin(q{*}); - my $json; - if ($errstr) { - $json = $self->render_to_string( - json => { - api_version => $api_version, - version => $VERSION, - error => $errstr, - } - ); + elsif ( not $uic ) { + return; } else { - my @candidates = map { { code => $_->[0], name => $_->[1] } } - Travel::Status::DE::IRIS::Stations::get_station($station); - if ( @candidates > 1 - or - ( @candidates == 1 and $candidates[0]{code} ne $station ) ) - { - $json = $self->render_to_string( - json => { - api_version => $api_version, - version => $VERSION, - error => 'ambiguous station code/name', - candidates => \@candidates, - } - ); - } - else { - $json = $self->render_to_string( - json => { - api_version => $api_version, - version => $VERSION, - error => - ( $errstr // "Got no results for '$station'" ) - } - ); - } + $ret = substr( $uic, 4, 5 ); } - if ($callback) { - $self->render( - data => "$callback($json);", - format => 'json' - ); + + if ( $train_type =~ m{[.]S(\d)$} ) { + $ret .= ".$1"; } - else { - $self->render( - data => $json, - format => 'json' - ); + elsif ( $train_type =~ m{[.]R$} ) { + $ret .= '.r'; + } + + if ( $ret and $self->app->dbdb_wagon->{$ret} ) { + return $ret; } return; } @@ -243,72 +208,83 @@ sub startup { { return 1; } + if ( ( $self->param('hafas') or $self->param('efa') ) + and $stop =~ m{ [Bb]ahnhof | Bf }x ) + { + return 1; + } return; } ); $self->helper( - 'json_route_diff' => sub { - my ( $self, $route, $sched_route ) = @_; - my @json_route; - my @route = @{$route}; - my @sched_route = @{$sched_route}; - - my $route_idx = 0; - my $sched_idx = 0; - - while ( $route_idx <= $#route and $sched_idx <= $#sched_route ) { - if ( $route[$route_idx] eq $sched_route[$sched_idx] ) { - push( @json_route, { name => $route[$route_idx] } ); - $route_idx++; - $sched_idx++; - } + 'occupancy_icon' => sub { + my ( $self, $occupancy ) = @_; - # this branch is inefficient, but won't be taken frequently - elsif ( not( $route[$route_idx] ~~ \@sched_route ) ) { - push( - @json_route, - { - name => $route[$route_idx], - isAdditional => 1 - } - ); - $route_idx++; - } - else { - push( - @json_route, - { - name => $sched_route[$sched_idx], - isCancelled => 1 - } - ); - $sched_idx++; - } + my @symbols + = ( + qw(help_outline person_outline people priority_high not_interested) + ); + my $text = 'Auslastung unbekannt'; + + if ( $occupancy eq 'MANY_SEATS' ) { + $occupancy = 1; + } + elsif ( $occupancy eq 'FEW_SEATS' ) { + $occupancy = 2; } - while ( $route_idx <= $#route ) { - push( - @json_route, - { - name => $route[$route_idx], - isAdditional => 1, - isCancelled => 0 - } - ); - $route_idx++; + elsif ( $occupancy eq 'STANDING_ONLY' ) { + $occupancy = 3; } - while ( $sched_idx <= $#sched_route ) { - push( - @json_route, - { - name => $sched_route[$sched_idx], - isAdditional => 0, - isCancelled => 1 - } - ); - $sched_idx++; + elsif ( $occupancy eq 'FULL' ) { + $occupancy = 4; } - return @json_route; + + if ( $occupancy > 3 ) { + $text = 'Voraussichtlich überfüllt'; + } + if ( $occupancy > 2 ) { + $text = 'Sehr hohe Auslastung erwartet'; + } + elsif ( $occupancy > 1 ) { + $text = 'Hohe Auslastung erwartet'; + } + elsif ( $occupancy > 0 ) { + $text = 'Geringe Auslastung erwartet'; + } + + return ( $text, $symbols[$occupancy] ); + } + ); + + $self->helper( + 'utilization_icon' => sub { + my ( $self, $utilization ) = @_; + my ( $first, $second ) = @{ $utilization // [] }; + $first //= 0; + $second //= 0; + my $sum = ( $first + $second ) / 2; + + my @symbols + = ( + qw(help_outline person_outline people priority_high not_interested) + ); + my $text = 'Auslastung unbekannt'; + + if ( $sum > 3.5 ) { + $text = 'Zug ist ausgebucht'; + } + elsif ( $sum >= 2.5 ) { + $text = 'Sehr hohe Auslastung'; + } + elsif ( $sum >= 1.5 ) { + $text = 'Hohe Auslastung'; + } + elsif ( $sum >= 1 ) { + $text = 'Geringe Auslastung'; + } + + return ( $text, $symbols[$first], $symbols[$second] ); } ); @@ -332,11 +308,35 @@ sub startup { } ); + $self->helper( + 'get_rt_time_class' => sub { + my ( $self, $train ) = @_; + if ( $train->{has_realtime} + and not $train->{is_bit_delayed} + and not $train->{is_delayed} ) + { + return 'on-time'; + } + if ( $train->{is_bit_delayed} ) { + return 'a-bit-delayed'; + } + if ( $train->{is_delayed} ) { + return 'delayed'; + } + return q{}; + } + ); + my $r = $self->routes; - $r->get('/_redirect')->to('static#redirect'); + $r->get('/_redirect')->to('stationboard#redirect_to_station'); + + # legacy entry point + $r->get('/_auto')->to('static#geostop'); - $r->get('/_auto')->to('static#geolocation'); + $r->get('/_autostop')->to('static#geostop'); + + $r->get('/_backend')->to('stationboard#backend_list'); $r->get('/_datenschutz')->to('static#privacy'); @@ -346,33 +346,27 @@ sub startup { $r->get('/_impressum')->to('static#imprint'); - $r->get('/_wr/:train/:departure')->to('wagenreihung#wagenreihung'); + $r->get('/dyn/:av/autocomplete.js')->to('stationboard#autocomplete'); + + $r->get('/carriage-formation')->to('wagenreihung#wagenreihung'); + $r->get('/w/*wagon')->to('wagenreihung#wagen'); $r->get('/_ajax_mapinfo/:tripid/:lineno')->to('map#ajax_route'); $r->get('/map/:tripid/:lineno')->to('map#route'); - $r->get('/intersection/:trips')->to('map#intersection'); - $r->get('/z/:train/:station')->to('stationboard#train_details'); - - $r->get('/map')->to('map#search_form'); - $r->get('/_trainsearch')->to('map#search'); + $r->get('/coverage/:backend/:service')->to('map#coverage'); + $r->get( '/z/:train/*station' => [ format => [ 'html', 'json' ] ] ) + ->to( 'stationboard#station_train_details', format => undef ) + ->name('train_at_station'); + $r->get( '/z/:train' => [ format => [ 'html', 'json' ] ] ) + ->to( 'stationboard#train_details', format => undef ) + ->name('train'); $self->defaults( layout => 'app' ); - $r->get('/')->to('stationboard#handle_request'); - $r->get('/multi/*station')->to('stationboard#handle_request'); - $r->get('/*station')->to('stationboard#handle_request'); - - $self->config( - hypnotoad => { - accepts => $ENV{DBFAKEDISPLAY_ACCEPTS} // 100, - clients => $ENV{DBFAKEDISPLAY_CLIENTS} // 10, - listen => [ $ENV{DBFAKEDISPLAY_LISTEN} // 'http://*:8092' ], - pid_file => $ENV{DBFAKEDISPLAY_PID_FILE} - // '/tmp/db-fakedisplay.pid', - spare => $ENV{DBFAKEDISPLAY_SPARE} // 2, - workers => $ENV{DBFAKEDISPLAY_WORKERS} // 2, - }, - ); + $r->get('/')->to('stationboard#handle_board_request'); + $r->get('/multi/*station')->to('stationboard#handle_board_request'); + $r->get( '/*station' => [ format => [ 'html', 'json' ] ] ) + ->to( 'stationboard#handle_board_request', format => undef ); $self->types->type( json => 'application/json; charset=utf-8' ); |