Commit 35cc18df authored by Birte Kristina Friesel's avatar Birte Kristina Friesel
Browse files

add passenger rights heuristic for missed connections

parent 29d99fe1
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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');
+172 −12
Original line number Diff line number Diff line
@@ -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} );

	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');
+22 −14
Original line number Diff line number Diff line
@@ -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');
% }
+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>