summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Friesel <derf@finalrewind.org>2019-09-13 23:39:28 +0200
committerDaniel Friesel <derf@finalrewind.org>2019-09-13 23:39:28 +0200
commit35cc18dfae9ba3436809d1b6e823362f2aa725ad (patch)
tree721dbebd0cee8d6838142af7d3dd00bf267129a4
parent29d99fe140ad1ec9670b89cf33155c989024adfb (diff)
add passenger rights heuristic for missed connections
-rwxr-xr-xlib/Travelynx.pm1
-rw-r--r--lib/Travelynx/Controller/Passengerrights.pm184
-rw-r--r--templates/history.html.ep36
-rw-r--r--templates/passengerrights.html.ep69
4 files changed, 264 insertions, 26 deletions
diff --git a/lib/Travelynx.pm b/lib/Travelynx.pm
index 44273e5..7d3548e 100755
--- a/lib/Travelynx.pm
+++ b/lib/Travelynx.pm
@@ -2890,6 +2890,7 @@ sub startup {
$authed_r->get('/account/insight')->to('account#insight');
$authed_r->get('/ajax/status_card.html')->to('traveling#status_card');
$authed_r->get('/cancelled')->to('traveling#cancelled');
+ $authed_r->get('/fgr')->to('passengerrights#list_candidates');
$authed_r->get('/account/password')->to('account#password_form');
$authed_r->get('/account/mail')->to('account#change_mail');
$authed_r->get('/export.json')->to('account#json_export');
diff --git a/lib/Travelynx/Controller/Passengerrights.pm b/lib/Travelynx/Controller/Passengerrights.pm
index aab8d76..1188e19 100644
--- a/lib/Travelynx/Controller/Passengerrights.pm
+++ b/lib/Travelynx/Controller/Passengerrights.pm
@@ -4,6 +4,82 @@ use Mojo::Base 'Mojolicious::Controller';
use DateTime;
use CAM::PDF;
+sub mark_if_missed_connection {
+ my ( $self, $journey, $next_journey ) = @_;
+
+ my $possible_delay
+ = ( $next_journey->{rt_departure}->epoch
+ - $journey->{sched_arrival}->epoch ) / 60;
+ my $wait_time
+ = ( $next_journey->{rt_departure}->epoch - $journey->{rt_arrival}->epoch )
+ / 60;
+ if (
+ $wait_time < 120
+ and ( $possible_delay >= 120
+ or ( $journey->{delay} < 60 and $possible_delay >= 60 ) )
+ )
+ {
+ $journey->{connection_missed} = 1;
+ $journey->{connection} = $next_journey;
+ $journey->{possible_delay} = $possible_delay;
+ $journey->{wait_time} = $wait_time;
+ return 1;
+ }
+ return 0;
+}
+
+sub list_candidates {
+ my ($self) = @_;
+
+ my $now = DateTime->now( time_zone => 'Europe/Berlin' );
+ my $range_start = $now->clone->subtract( months => 12 );
+
+ my @journeys = $self->get_user_travels(
+ after => $range_start,
+ before => $now
+ );
+ @journeys = grep { $_->{sched_arrival}->epoch and $_->{rt_arrival}->epoch }
+ @journeys;
+
+ for my $i ( 0 .. $#journeys ) {
+ my $journey = $journeys[$i];
+
+ $journey->{delay}
+ = ( $journey->{rt_arrival}->epoch - $journey->{sched_arrival}->epoch )
+ / 60;
+
+ if ( $journey->{delay} < 3 or $journey->{delay} >= 120 ) {
+ next;
+ }
+ if ( $i > 0 ) {
+ $self->mark_if_missed_connection( $journey, $journeys[ $i - 1 ] );
+ }
+ }
+
+ @journeys = grep { $_->{delay} >= 60 or $_->{connection_missed} } @journeys;
+
+ push(
+ @journeys,
+ map { $_->{cancelled} = 1; $_ } $self->get_user_travels(
+ after => $range_start,
+ before => $now,
+ cancelled => 1
+ )
+ );
+
+ @journeys
+ = sort { $b->{sched_departure}->epoch <=> $a->{sched_departure}->epoch }
+ @journeys;
+
+ $self->respond_to(
+ json => { json => [@journeys] },
+ any => {
+ template => 'passengerrights',
+ journeys => [@journeys]
+ }
+ );
+}
+
sub generate {
my ($self) = @_;
my $journey_id = $self->param('id');
@@ -34,44 +110,128 @@ sub generate {
return;
}
+ $journey->{delay}
+ = ( $journey->{rt_arrival}->epoch - $journey->{sched_arrival}->epoch )
+ / 60;
+
+ if ( $journey->{delay} < 120 ) {
+ my @connections = $self->get_user_travels(
+ uid => $uid,
+ after => $journey->{rt_arrival},
+ before => $journey->{rt_arrival}->clone->add( hours => 2 )
+ );
+ if (@connections) {
+ $self->mark_if_missed_connection( $journey, $connections[-1] );
+ }
+ }
+
my $pdf = CAM::PDF->new('public/static/pdf/fahrgastrechteformular.pdf');
+ # from station
$pdf->fillFormFields( 'S1F4', $journey->{from_name} );
- $pdf->fillFormFields( 'S1F7', $journey->{to_name} );
+
+ if ( $journey->{connection} ) {
+
+ # to station
+ $pdf->fillFormFields( 'S1F7', $journey->{connection}{to_name} );
+
+ # missed connection in:
+ $pdf->fillFormFields( 'S1F22', $journey->{to_name} );
+
+ # last change in:
+ $pdf->fillFormFields( 'S1F24', $journey->{to_name} );
+ }
+ else {
+ # to station
+ $pdf->fillFormFields( 'S1F7', $journey->{to_name} );
+ }
+
if ( not $journey->{cancelled} ) {
- $pdf->fillFormFields( 'S1F13', $journey->{type} );
- $pdf->fillFormFields( 'S1F14', $journey->{no} );
+
+ # arived with: TRAIN NO
+ if ( $journey->{connection} ) {
+ $pdf->fillFormFields( 'S1F13', $journey->{connection}{type} );
+ $pdf->fillFormFields( 'S1F14', $journey->{connection}{no} );
+ }
+ else {
+ $pdf->fillFormFields( 'S1F13', $journey->{type} );
+ $pdf->fillFormFields( 'S1F14', $journey->{no} );
+ }
}
+
+ # first delayed train: TRAIN NO
$pdf->fillFormFields( 'S1F17', $journey->{type} );
$pdf->fillFormFields( 'S1F18', $journey->{no} );
+
if ( $journey->{sched_departure}->epoch ) {
+
+ # journey YYMMDD
$pdf->fillFormFields( 'S1F1',
$journey->{sched_departure}->strftime('%d') );
$pdf->fillFormFields( 'S1F2',
$journey->{sched_departure}->strftime('%m') );
$pdf->fillFormFields( 'S1F3',
$journey->{sched_departure}->strftime('%y') );
+
+ # sched departure HHMM
$pdf->fillFormFields( 'S1F5',
$journey->{sched_departure}->strftime('%H') );
$pdf->fillFormFields( 'S1F6',
$journey->{sched_departure}->strftime('%M') );
+
+ # first delayed train: sched departure HHMM
$pdf->fillFormFields( 'S1F19',
$journey->{sched_departure}->strftime('%H') );
$pdf->fillFormFields( 'S1F20',
$journey->{sched_departure}->strftime('%M') );
}
if ( $journey->{sched_arrival}->epoch ) {
- $pdf->fillFormFields( 'S1F8',
- $journey->{sched_arrival}->strftime('%H') );
- $pdf->fillFormFields( 'S1F9',
- $journey->{sched_arrival}->strftime('%M') );
+
+ # sched arrival HHMM
+ if ( $journey->{connection} ) {
+
+ # TODO (needs plan data for non-journey trains)
+ }
+ else {
+ $pdf->fillFormFields( 'S1F8',
+ $journey->{sched_arrival}->strftime('%H') );
+ $pdf->fillFormFields( 'S1F9',
+ $journey->{sched_arrival}->strftime('%M') );
+ }
}
if ( $journey->{rt_arrival}->epoch and not $journey->{cancelled} ) {
- $pdf->fillFormFields( 'S1F10', $journey->{rt_arrival}->strftime('%d') );
- $pdf->fillFormFields( 'S1F11', $journey->{rt_arrival}->strftime('%m') );
- $pdf->fillFormFields( 'S1F12', $journey->{rt_arrival}->strftime('%y') );
- $pdf->fillFormFields( 'S1F15', $journey->{rt_arrival}->strftime('%H') );
- $pdf->fillFormFields( 'S1F16', $journey->{rt_arrival}->strftime('%M') );
+
+ if ( $journey->{connection} ) {
+
+ # arrival YYMMDD
+ $pdf->fillFormFields( 'S1F10',
+ $journey->{connection}{rt_arrival}->strftime('%d') );
+ $pdf->fillFormFields( 'S1F11',
+ $journey->{connection}{rt_arrival}->strftime('%m') );
+ $pdf->fillFormFields( 'S1F12',
+ $journey->{connection}{rt_arrival}->strftime('%y') );
+
+ # arrival HHMM
+ $pdf->fillFormFields( 'S1F15',
+ $journey->{connection}{rt_arrival}->strftime('%H') );
+ $pdf->fillFormFields( 'S1F16',
+ $journey->{connection}{rt_arrival}->strftime('%M') );
+ }
+ else {
+ # arrival YYMMDD
+ $pdf->fillFormFields( 'S1F10',
+ $journey->{rt_arrival}->strftime('%d') );
+ $pdf->fillFormFields( 'S1F11',
+ $journey->{rt_arrival}->strftime('%m') );
+ $pdf->fillFormFields( 'S1F12',
+ $journey->{rt_arrival}->strftime('%y') );
+
+ # arrival HHMM
+ $pdf->fillFormFields( 'S1F15',
+ $journey->{rt_arrival}->strftime('%H') );
+ $pdf->fillFormFields( 'S1F16',
+ $journey->{rt_arrival}->strftime('%M') );
+ }
}
$self->res->headers->content_type('application/pdf');
diff --git a/templates/history.html.ep b/templates/history.html.ep
index a2f607a..607a356 100644
--- a/templates/history.html.ep
+++ b/templates/history.html.ep
@@ -7,20 +7,6 @@
</div>
</div>
-<div class="row">
- <div class="col s12 m12 l3 center-align">
- <a href="/cancelled" class="waves-effect waves-light btn"><i class="material-icons left">cancel</i> Zugausfälle</a>
- </div>
- <div class="col s12 m12 l1">&nbsp;</div>
- <div class="col s12 m12 l4 center-align">
- <a href="/journey/add" class="waves-effect waves-light btn"><i class="material-icons left">add</i> Neue Fahrt</a>
- </div>
- <div class="col s12 m12 l1">&nbsp;</div>
- <div class="col s12 m12 l3 center-align">
- <a href="/history.json" class="waves-effect waves-light btn"><i class="material-icons left">cloud</i> JSON-Export</a>
- </div>
-</div>
-
<h2>Nach Jahr</h2>
%= include '_history_years', current => '';
% if(0) {
@@ -41,6 +27,28 @@
</div>
% }
+<h2>Ausfälle und Verspätungen</h2>
+<div class="row">
+ <div class="col s12 m12 l5 center-align">
+ <a href="/cancelled" class="waves-effect waves-light btn"><i class="material-icons left">cancel</i> Zugausfälle</a>
+ </div>
+ <div class="col s12 m12 l2">&nbsp;</div>
+ <div class="col s12 m12 l5 center-align">
+ <a href="/fgr" class="waves-effect waves-light btn"><i class="material-icons left">feedback</i> Fahrgastrechte</a>
+ </div>
+</div>
+
+<h2>Rohdaten</h2>
+<div class="row">
+ <div class="col s12 m12 l5 center-align">
+ <a href="/history.json" class="waves-effect waves-light btn"><i class="material-icons left">cloud</i> JSON-Export</a>
+ </div>
+ <div class="col s12 m12 l2">&nbsp;</div>
+ <div class="col s12 m12 l5 center-align">
+ <a href="/journey/add" class="waves-effect waves-light btn"><i class="material-icons left">add</i> Neue Fahrt</a>
+ </div>
+</div>
+
% if (stash('statistics')) {
%= include '_history_stats', stats => stash('statistics');
% }
diff --git a/templates/passengerrights.html.ep b/templates/passengerrights.html.ep
new file mode 100644
index 0000000..09c50d9
--- /dev/null
+++ b/templates/passengerrights.html.ep
@@ -0,0 +1,69 @@
+<h1>Fahrgastrechte</h1>
+<div class="row">
+ <div class="col s12">
+ <p>
+ Gemäß der Fahrgastrechte im Eisenbahnverkehr besteht ab 60 Minuten
+ Verspätung am Ziel ein Entschädigungsanspruch gegenüber dem
+ Eisenbahnverkehrsunternehmen. Dieser kann mit dem
+ Fahrgastrechteformular geltend gemacht werden.
+ </p>
+ <p>
+ Die folgenden Zugfahrten sind wahrscheinliche Kandidaten dafür.
+ Details zur jeweiligen Zugfahrt sind bereits im Formular eingetragen.
+ </p>
+ </div>
+</div>
+
+<div class="row">
+ <div class="col s12">
+ <table class="striped">
+ <thead>
+ <tr>
+ <th>Datum</th>
+ <th>Zug</th>
+ <th>Grund</th>
+ <th>Formular</th>
+ </tr>
+ </thead>
+ <tbody>
+ % for my $journey (@{$journeys}) {
+ % my $detail_link = '/journey/' . $journey->{id};
+ <tr>
+ <td><%= $journey->{sched_departure}->strftime('%d.%m.%Y') %></td>
+ <td><a href="<%= $detail_link %>">
+ <%= $journey->{type} %> <%= $journey->{line} // $journey->{no} %>
+ → <%= $journey->{to_name} %>
+ % if ($journey->{connection}) {
+ <br/>
+ <%= $journey->{connection}{type} %> <%= $journey->{connection}{line} // $journey->{connection}{no} %>
+ → <%= $journey->{connection}{to_name} %>
+ % }
+ </a></td>
+ <td>
+ % if ($journey->{cancelled}) {
+ Ausfall
+ % }
+ % else {
+ %= sprintf('%+d', $journey->{delay})
+ % if ($journey->{connection}) {
+ <br/>
+ %= sprintf('Mit Anschluss: %+d?', $journey->{possible_delay})
+ % }
+ % }
+ </td>
+ <td>
+ % my $form_target = sprintf('/journey/passenger_rights/FGR %s %s %s.pdf', $journey->{sched_departure}->ymd, $journey->{type}, $journey->{no});
+ %= form_for $form_target => (method => 'POST') => begin
+ %= csrf_field
+ %= hidden_field id => $journey->{id}
+ <button class="btn waves-effect waves-light grey darken-3" type="submit" name="action" value="generate">
+ <i class="material-icons">file_download</i>
+ </button>
+ %= end
+ </td>
+ </tr>
+ % }
+ </tbody>
+ </table>
+ </div>
+</div>