Loading lib/Travelynx.pm +1 −0 Original line number Diff line number Diff line Loading @@ -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'); Loading lib/Travelynx/Controller/Passengerrights.pm +172 −12 Original line number Diff line number Diff line Loading @@ -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'); Loading Loading @@ -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} ); 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} ) { # 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 ) { # 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'); Loading templates/history.html.ep +22 −14 Original line number Diff line number Diff line Loading @@ -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"> </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"> </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) { Loading @@ -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"> </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"> </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'); % } templates/passengerrights.html.ep 0 → 100644 +69 −0 Original line number Diff line number Diff line <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> Loading
lib/Travelynx.pm +1 −0 Original line number Diff line number Diff line Loading @@ -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'); Loading
lib/Travelynx/Controller/Passengerrights.pm +172 −12 Original line number Diff line number Diff line Loading @@ -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'); Loading Loading @@ -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} ); 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} ) { # 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 ) { # 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'); Loading
templates/history.html.ep +22 −14 Original line number Diff line number Diff line Loading @@ -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"> </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"> </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) { Loading @@ -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"> </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"> </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'); % }
templates/passengerrights.html.ep 0 → 100644 +69 −0 Original line number Diff line number Diff line <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>