Commit 47b95736 authored by Birte Kristina Friesel's avatar Birte Kristina Friesel
Browse files

Move most journey code to newly introduced Journey model

parent d9624ee3
Loading
Loading
Loading
Loading
+25 −678
Original line number Diff line number Diff line
@@ -10,16 +10,16 @@ use DateTime;
use DateTime::Format::Strptime;
use Encode qw(decode encode);
use File::Slurp qw(read_file);
use Geo::Distance;
use JSON;
use List::Util;
use List::UtilsBy qw(uniq_by);
use List::MoreUtils qw(after_incl before_incl first_index);
use List::MoreUtils qw(first_index);
use Travel::Status::DE::DBWagenreihung;
use Travel::Status::DE::IRIS::Stations;
use Travelynx::Helper::HAFAS;
use Travelynx::Helper::IRIS;
use Travelynx::Helper::Sendmail;
use Travelynx::Model::Journeys;
use Travelynx::Model::Users;
use XML::LibXML;

@@ -289,6 +289,18 @@ sub startup {
		}
	);

	$self->helper(
		journeys => sub {
			my ($self) = @_;
			state $journeys = Travelynx::Model::Journeys->new(
				log             => $self->app->log,
				pg              => $self->pg,
				renamed_station => $self->app->renamed_station,
				station_by_eva  => $self->app->station_by_eva,
			);
		}
	);

	$self->helper(
		pg => sub {
			my ($self) = @_;
@@ -359,126 +371,6 @@ sub startup {
		}
	);

	# Returns (journey id, error)
	# Must be called during a transaction.
	# Must perform a rollback on error.
	$self->helper(
		'add_journey' => sub {
			my ( $self, %opt ) = @_;

			my $db          = $opt{db};
			my $uid         = $opt{uid} // $self->current_user->{id};
			my $now         = DateTime->now( time_zone => 'Europe/Berlin' );
			my $dep_station = get_station( $opt{dep_station} );
			my $arr_station = get_station( $opt{arr_station} );

			if ( not $dep_station ) {
				return ( undef, 'Unbekannter Startbahnhof' );
			}
			if ( not $arr_station ) {
				return ( undef, 'Unbekannter Zielbahnhof' );
			}

			my $daily_journey_count = $db->select(
				'journeys_str',
				'count(*) as count',
				{
					user_id     => $uid,
					real_dep_ts => {
						-between => [
							$opt{rt_departure}->clone->subtract( days => 1 )
							  ->epoch,
							$opt{rt_departure}->epoch
						],
					},
				}
			)->hash->{count};

			if ( $daily_journey_count >= 100 ) {
				return ( undef,
"In den 24 Stunden vor der angegebenen Abfahrtszeit wurden ${daily_journey_count} weitere Fahrten angetreten. Das kann nicht stimmen."
				);
			}

			my @route = ( [ $dep_station->[1], {}, undef ] );

			if ( $opt{route} ) {
				my @unknown_stations;
				for my $station ( @{ $opt{route} } ) {
					my $station_info = get_station($station);
					if ($station_info) {
						push( @route, [ $station_info->[1], {}, undef ] );
					}
					else {
						push( @route, [ $station, {}, undef ] );
						push( @unknown_stations, $station );
					}
				}

				if ( not $opt{lax} ) {
					if ( @unknown_stations == 1 ) {
						return ( undef,
							"Unbekannter Unterwegshalt: $unknown_stations[0]" );
					}
					elsif (@unknown_stations) {
						return ( undef,
							'Unbekannte Unterwegshalte: '
							  . join( ', ', @unknown_stations ) );
					}
				}
			}

			push( @route, [ $arr_station->[1], {}, undef ] );

			if ( $route[0][0] eq $route[1][0] ) {
				shift(@route);
			}

			if ( $route[-2][0] eq $route[-1][0] ) {
				pop(@route);
			}

			my $entry = {
				user_id             => $uid,
				train_type          => $opt{train_type},
				train_line          => $opt{train_line},
				train_no            => $opt{train_no},
				train_id            => 'manual',
				checkin_station_id  => $dep_station->[2],
				checkin_time        => $now,
				sched_departure     => $opt{sched_departure},
				real_departure      => $opt{rt_departure},
				checkout_station_id => $arr_station->[2],
				sched_arrival       => $opt{sched_arrival},
				real_arrival        => $opt{rt_arrival},
				checkout_time       => $now,
				edited              => 0x3fff,
				cancelled           => $opt{cancelled} ? 1 : 0,
				route               => JSON->new->encode( \@route ),
			};

			if ( $opt{comment} ) {
				$entry->{user_data}
				  = JSON->new->encode( { comment => $opt{comment} } );
			}

			my $journey_id = undef;
			eval {
				$journey_id
				  = $db->insert( 'journeys', $entry, { returning => 'id' } )
				  ->hash->{id};
				$self->invalidate_stats_cache( $opt{rt_departure}, $db, $uid );
			};

			if ($@) {
				$self->app->log->error("add_journey($uid): $@");
				return ( undef, 'add_journey failed: ' . $@ );
			}

			return ( $journey_id, undef );
		}
	);

	$self->helper(
		'checkin' => sub {
			my ( $self, $station, $train_id, $uid ) = @_;
@@ -620,7 +512,11 @@ sub startup {
					);
				}

				$self->invalidate_stats_cache( $cache_ts, $db, $uid );
				$self->journeys->invalidate_stats_cache(
					ts  => $cache_ts,
					db  => $db,
					uid => $uid
				);

				$tx->commit;
			};
@@ -633,36 +529,6 @@ sub startup {
		}
	);

	# Statistics are partitioned by real_departure, which must be provided
	# when calling this function e.g. after journey deletion or editing.
	# If a joureny's real_departure has been edited, this function must be
	# called twice: once with the old and once with the new value.
	$self->helper(
		'invalidate_stats_cache' => sub {
			my ( $self, $ts, $db, $uid ) = @_;

			$uid //= $self->current_user->{id};
			$db  //= $self->pg->db;

			$self->pg->db->delete(
				'journey_stats',
				{
					user_id => $uid,
					year    => $ts->year,
					month   => $ts->month,
				}
			);
			$self->pg->db->delete(
				'journey_stats',
				{
					user_id => $uid,
					year    => $ts->year,
					month   => 0,
				}
			);
		}
	);

	$self->helper(
		'checkout' => sub {
			my ( $self, $station, $force, $uid ) = @_;
@@ -856,7 +722,11 @@ sub startup {
							month => $+{month}
						);
					}
					$self->invalidate_stats_cache( $cache_ts, $db, $uid );
					$self->journeys->invalidate_stats_cache(
						ts  => $cache_ts,
						db  => $db,
						uid => $uid
					);
				}
				elsif ( defined $train and $train->arrival_is_cancelled ) {

@@ -929,208 +799,6 @@ sub startup {
		}
	);

	$self->helper(
		'update_journey_part' => sub {
			my ( $self, $db, $journey_id, $key, $value ) = @_;
			my $rows;

			my $journey = $self->get_journey(
				db            => $db,
				journey_id    => $journey_id,
				with_datetime => 1,
			);

			eval {
				if ( $key eq 'from_name' ) {
					my $from_station = get_station( $value, 1 );
					if ( not $from_station ) {
						die("Unbekannter Startbahnhof\n");
					}
					$rows = $db->update(
						'journeys',
						{
							checkin_station_id => $from_station->[2],
							edited             => $journey->{edited} | 0x0004,
						},
						{
							id => $journey_id,
						}
					)->rows;
				}
				elsif ( $key eq 'to_name' ) {
					my $to_station = get_station( $value, 1 );
					if ( not $to_station ) {
						die("Unbekannter Zielbahnhof\n");
					}
					$rows = $db->update(
						'journeys',
						{
							checkout_station_id => $to_station->[2],
							edited              => $journey->{edited} | 0x0400,
						},
						{
							id => $journey_id,
						}
					)->rows;
				}
				elsif ( $key eq 'sched_departure' ) {
					$rows = $db->update(
						'journeys',
						{
							sched_departure => $value,
							edited          => $journey->{edited} | 0x0001,
						},
						{
							id => $journey_id,
						}
					)->rows;
				}
				elsif ( $key eq 'rt_departure' ) {
					$rows = $db->update(
						'journeys',
						{
							real_departure => $value,
							edited         => $journey->{edited} | 0x0002,
						},
						{
							id => $journey_id,
						}
					)->rows;

                 # stats are partitioned by rt_departure -> both the cache for
                 # the old value (see bottom of this function) and the new value
                 # (here) must be invalidated.
					$self->invalidate_stats_cache( $value, $db );
				}
				elsif ( $key eq 'sched_arrival' ) {
					$rows = $db->update(
						'journeys',
						{
							sched_arrival => $value,
							edited        => $journey->{edited} | 0x0100,
						},
						{
							id => $journey_id,
						}
					)->rows;
				}
				elsif ( $key eq 'rt_arrival' ) {
					$rows = $db->update(
						'journeys',
						{
							real_arrival => $value,
							edited       => $journey->{edited} | 0x0200,
						},
						{
							id => $journey_id,
						}
					)->rows;
				}
				elsif ( $key eq 'route' ) {
					my @new_route = map { [ $_, {}, undef ] } @{$value};
					$rows = $db->update(
						'journeys',
						{
							route  => JSON->new->encode( \@new_route ),
							edited => $journey->{edited} | 0x0010,
						},
						{
							id => $journey_id,
						}
					)->rows;
				}
				elsif ( $key eq 'cancelled' ) {
					$rows = $db->update(
						'journeys',
						{
							cancelled => $value,
							edited    => $journey->{edited} | 0x0020,
						},
						{
							id => $journey_id,
						}
					)->rows;
				}
				elsif ( $key eq 'comment' ) {
					$journey->{user_data}{comment} = $value;
					$rows = $db->update(
						'journeys',
						{
							user_data =>
							  JSON->new->encode( $journey->{user_data} ),
						},
						{
							id => $journey_id,
						}
					)->rows;
				}
				else {
					die("Invalid key $key\n");
				}
			};

			if ($@) {
				$self->app->log->error(
					"update_journey_part($journey_id, $key): $@");
				return "update_journey_part($key): $@";
			}
			if ( $rows == 1 ) {
				$self->invalidate_stats_cache( $journey->{rt_departure}, $db );
				return undef;
			}
			return 'UPDATE failed: did not match any journey part';
		}
	);

	$self->helper(
		'journey_sanity_check' => sub {
			my ( $self, $journey, $lax ) = @_;

			if ( defined $journey->{sched_duration}
				and $journey->{sched_duration} <= 0 )
			{
				return
'Die geplante Dauer dieser Zugfahrt ist ≤ 0. Teleportation und Zeitreisen werden aktuell nicht unterstützt.';
			}
			if ( defined $journey->{rt_duration}
				and $journey->{rt_duration} <= 0 )
			{
				return
'Die Dauer dieser Zugfahrt ist ≤ 0. Teleportation und Zeitreisen werden aktuell nicht unterstützt.';
			}
			if (    $journey->{sched_duration}
				and $journey->{sched_duration} > 60 * 60 * 24 )
			{
				return 'Die Zugfahrt ist länger als 24 Stunden.';
			}
			if (    $journey->{rt_duration}
				and $journey->{rt_duration} > 60 * 60 * 24 )
			{
				return 'Die Zugfahrt ist länger als 24 Stunden.';
			}
			if ( $journey->{kmh_route} > 500 or $journey->{kmh_beeline} > 500 )
			{
				return 'Zugfahrten mit über 500 km/h? Schön wär\'s.';
			}
			if ( $journey->{route} and @{ $journey->{route} } > 99 ) {
				my $stop_count = @{ $journey->{route} };
				return
"Die Zugfahrt hat $stop_count Unterwegshalte. Also ich weiß ja nicht so recht.";
			}
			if ( $journey->{edited} & 0x0010 and not $lax ) {
				my @unknown_stations
				  = $self->grep_unknown_stations( map { $_->[0] }
					  @{ $journey->{route} } );
				if (@unknown_stations) {
					return 'Unbekannte Station(en): '
					  . join( ', ', @unknown_stations );
				}
			}

			return undef;
		}
	);

	# This helper should only be called directly when also providing a user ID.
	# If you don't have one, use current_user() instead (get_user_data will
	# delegate to it anyways).
@@ -1291,55 +959,6 @@ sub startup {
		}
	);

	$self->helper(
		'delete_journey' => sub {
			my ( $self, $journey_id, $checkin_epoch, $checkout_epoch ) = @_;
			my $uid = $self->current_user->{id};

			my @journeys = $self->get_user_travels(
				uid        => $uid,
				journey_id => $journey_id
			);
			if ( @journeys == 0 ) {
				return 'Journey not found';
			}
			my $journey = $journeys[0];

			# Double-check (comparing both ID and action epoch) to make sure we
			# are really deleting the right journey and the user isn't just
			# playing around with POST requests.
			if (   $journey->{id} != $journey_id
				or $journey->{checkin_ts} != $checkin_epoch
				or $journey->{checkout_ts} != $checkout_epoch )
			{
				return 'Invalid journey data';
			}

			my $rows;
			eval {
				$rows = $self->pg->db->delete(
					'journeys',
					{
						user_id => $uid,
						id      => $journey_id,
					}
				)->rows;
			};

			if ($@) {
				$self->app->log->error("Delete($uid, $journey_id): $@");
				return 'DELETE failed: ' . $@;
			}

			if ( $rows == 1 ) {
				$self->invalidate_stats_cache(
					epoch_to_dt( $journey->{rt_dep_ts} ) );
				return undef;
			}
			return sprintf( 'Deleted %d rows, expected 1', $rows );
		}
	);

	$self->helper(
		'get_journey_stats' => sub {
			my ( $self, %opt ) = @_;
@@ -1401,7 +1020,7 @@ sub startup {
				$interval_end = $interval_start->clone->add( years => 1 );
			}

			my @journeys = $self->get_user_travels(
			my @journeys = $self->journeys->get(
				uid           => $uid,
				cancelled     => $opt{cancelled} ? 1 : 0,
				verbose       => 1,
@@ -2001,31 +1620,6 @@ sub startup {
		}
	);

	$self->helper(
		'get_oldest_journey_ts' => sub {
			my ($self) = @_;

			my $res_h = $self->pg->db->select(
				'journeys_str',
				['sched_dep_ts'],
				{
					user_id => $self->current_user->{id},
				},
				{
					limit    => 1,
					order_by => {
						-asc => 'real_dep_ts',
					},
				}
			)->hash;

			if ($res_h) {
				return epoch_to_dt( $res_h->{sched_dep_ts} );
			}
			return undef;
		}
	);

	$self->helper(
		'get_latest_dest_id' => sub {
			my ( $self, %opt ) = @_;
@@ -2253,164 +1847,6 @@ sub startup {
		}
	);

	$self->helper(
		'get_user_travels' => sub {
			my ( $self, %opt ) = @_;

			my $uid = $opt{uid} || $self->current_user->{id};

			# If get_user_travels is called from inside a transaction, db
			# specifies the database handle performing the transaction.
			# Otherwise, we grab a fresh one.
			my $db = $opt{db} // $self->pg->db;

			my @select
			  = (
				qw(journey_id train_type train_line train_no checkin_ts sched_dep_ts real_dep_ts dep_eva checkout_ts sched_arr_ts real_arr_ts arr_eva cancelled edited route messages user_data)
			  );
			my %where = (
				user_id   => $uid,
				cancelled => 0
			);
			my %order = (
				order_by => {
					-desc => 'real_dep_ts',
				}
			);

			if ( $opt{cancelled} ) {
				$where{cancelled} = 1;
			}

			if ( $opt{limit} ) {
				$order{limit} = $opt{limit};
			}

			if ( $opt{journey_id} ) {
				$where{journey_id} = $opt{journey_id};
				delete $where{cancelled};
			}
			elsif ( $opt{after} and $opt{before} ) {
				$where{real_dep_ts} = {
					-between => [ $opt{after}->epoch, $opt{before}->epoch, ] };
			}

			if ( $opt{with_polyline} ) {
				push( @select, 'polyline' );
			}

			my @travels;

			my $res = $db->select( 'journeys_str', \@select, \%where, \%order );

			for my $entry ( $res->expand->hashes->each ) {

				my $ref = {
					id           => $entry->{journey_id},
					type         => $entry->{train_type},
					line         => $entry->{train_line},
					no           => $entry->{train_no},
					from_eva     => $entry->{dep_eva},
					checkin_ts   => $entry->{checkin_ts},
					sched_dep_ts => $entry->{sched_dep_ts},
					rt_dep_ts    => $entry->{real_dep_ts},
					to_eva       => $entry->{arr_eva},
					checkout_ts  => $entry->{checkout_ts},
					sched_arr_ts => $entry->{sched_arr_ts},
					rt_arr_ts    => $entry->{real_arr_ts},
					messages     => $entry->{messages},
					route        => $entry->{route},
					edited       => $entry->{edited},
					user_data    => $entry->{user_data},
				};

				if ( $opt{with_polyline} ) {
					$ref->{polyline} = $entry->{polyline};
				}

				if ( my $station
					= $self->app->station_by_eva->{ $ref->{from_eva} } )
				{
					$ref->{from_ds100} = $station->[0];
					$ref->{from_name}  = $station->[1];
				}
				if ( my $station
					= $self->app->station_by_eva->{ $ref->{to_eva} } )
				{
					$ref->{to_ds100} = $station->[0];
					$ref->{to_name}  = $station->[1];
				}

				if ( $opt{with_datetime} ) {
					$ref->{checkin} = epoch_to_dt( $ref->{checkin_ts} );
					$ref->{sched_departure}
					  = epoch_to_dt( $ref->{sched_dep_ts} );
					$ref->{rt_departure}  = epoch_to_dt( $ref->{rt_dep_ts} );
					$ref->{checkout}      = epoch_to_dt( $ref->{checkout_ts} );
					$ref->{sched_arrival} = epoch_to_dt( $ref->{sched_arr_ts} );
					$ref->{rt_arrival}    = epoch_to_dt( $ref->{rt_arr_ts} );
				}

				if ( $opt{verbose} ) {
					my $rename = $self->app->renamed_station;
					for my $stop ( @{ $ref->{route} } ) {
						if ( $rename->{ $stop->[0] } ) {
							$stop->[0] = $rename->{ $stop->[0] };
						}
					}
					$ref->{cancelled} = $entry->{cancelled};
					my @parsed_messages;
					for my $message ( @{ $ref->{messages} // [] } ) {
						my ( $ts, $msg ) = @{$message};
						push( @parsed_messages, [ epoch_to_dt($ts), $msg ] );
					}
					$ref->{messages} = [ reverse @parsed_messages ];
					$ref->{sched_duration}
					  = defined $ref->{sched_arr_ts}
					  ? $ref->{sched_arr_ts} - $ref->{sched_dep_ts}
					  : undef;
					$ref->{rt_duration}
					  = defined $ref->{rt_arr_ts}
					  ? $ref->{rt_arr_ts} - $ref->{rt_dep_ts}
					  : undef;
					my ( $km_polyline, $km_route, $km_beeline, $skip )
					  = $self->get_travel_distance($ref);
					$ref->{km_route}     = $km_polyline || $km_route;
					$ref->{skip_route}   = $km_polyline ? 0 : $skip;
					$ref->{km_beeline}   = $km_beeline;
					$ref->{skip_beeline} = $skip;
					my $kmh_divisor
					  = ( $ref->{rt_duration} // $ref->{sched_duration}
						  // 999999 ) / 3600;
					$ref->{kmh_route}
					  = $kmh_divisor ? $ref->{km_route} / $kmh_divisor : -1;
					$ref->{kmh_beeline}
					  = $kmh_divisor
					  ? $ref->{km_beeline} / $kmh_divisor
					  : -1;
				}

				push( @travels, $ref );
			}

			return @travels;
		}
	);

	$self->helper(
		'get_journey' => sub {
			my ( $self, %opt ) = @_;

			$opt{cancelled} = 'any';
			my @journeys = $self->get_user_travels(%opt);
			if ( @journeys == 0 ) {
				return undef;
			}

			return $journeys[0];
		}
	);

	$self->helper(
		'stationinfo_to_direction' => sub {
			my ( $self, $platform_info, $wagonorder, $prev_stop, $next_stop )
@@ -3145,95 +2581,6 @@ sub startup {
		}
	);

	$self->helper(
		'get_travel_distance' => sub {
			my ( $self, $journey ) = @_;

			my $from         = $journey->{from_name};
			my $from_eva     = $journey->{from_eva};
			my $to           = $journey->{to_name};
			my $to_eva       = $journey->{to_eva};
			my $route_ref    = $journey->{route};
			my $polyline_ref = $journey->{polyline};

			my $distance_polyline     = 0;
			my $distance_intermediate = 0;
			my $distance_beeline      = 0;
			my $skipped               = 0;
			my $geo                   = Geo::Distance->new();
			my @stations              = map { $_->[0] } @{$route_ref};
			my @route                 = after_incl { $_ eq $from } @stations;
			@route = before_incl { $_ eq $to } @route;

			if ( @route < 2 ) {

				# I AM ERROR
				return ( 0, 0, 0 );
			}

			my @polyline = after_incl { $_->[2] and $_->[2] == $from_eva }
			@{ $polyline_ref // [] };
			@polyline
			  = before_incl { $_->[2] and $_->[2] == $to_eva } @polyline;

			my $prev_station = shift @polyline;
			for my $station (@polyline) {

				#lonlatlonlat
				$distance_polyline
				  += $geo->distance( 'kilometer', $prev_station->[0],
					$prev_station->[1], $station->[0], $station->[1] );
				$prev_station = $station;
			}

			$prev_station = get_station( shift @route );
			if ( not $prev_station ) {
				return ( $distance_polyline, 0, 0 );
			}

           # Geo-coordinates for stations outside Germany are not available
           # at the moment. When calculating distance with intermediate stops,
           # these are simply left out (as if they were not part of the route).
           # For beeline distance calculation, we use the route's first and last
           # station with known geo-coordinates.
			my $from_station_beeline;
			my $to_station_beeline;

			# $#{$station} >= 4    iff    $station has geocoordinates
			for my $station_name (@route) {
				if ( my $station = get_station($station_name) ) {
					if ( not $from_station_beeline and $#{$prev_station} >= 4 )
					{
						$from_station_beeline = $prev_station;
					}
					if ( $#{$station} >= 4 ) {
						$to_station_beeline = $station;
					}
					if ( $#{$prev_station} >= 4 and $#{$station} >= 4 ) {
						$distance_intermediate
						  += $geo->distance( 'kilometer', $prev_station->[3],
							$prev_station->[4], $station->[3], $station->[4] );
					}
					else {
						$skipped++;
					}
					$prev_station = $station;
				}
			}

			if ( $from_station_beeline and $to_station_beeline ) {
				$distance_beeline = $geo->distance(
					'kilometer',                $from_station_beeline->[3],
					$from_station_beeline->[4], $to_station_beeline->[3],
					$to_station_beeline->[4]
				);
			}

			return ( $distance_polyline, $distance_intermediate,
				$distance_beeline, $skipped );
		}
	);

	$self->helper(
		'compute_journey_stats' => sub {
			my ( $self, @journeys ) = @_;
+8 −4
Original line number Diff line number Diff line
@@ -492,18 +492,18 @@ sub import_v1 {
	my $tx = $db->begin;

	$opt{db} = $db;
	my ( $journey_id, $error ) = $self->add_journey(%opt);
	my ( $journey_id, $error ) = $self->journeys->add(%opt);
	my $journey;

	if ( not $error ) {
		$journey = $self->get_journey(
		$journey = $self->journeys->get_single(
			uid        => $uid,
			db         => $db,
			journey_id => $journey_id,
			verbose    => 1
		);
		$error
		  = $self->journey_sanity_check( $journey, $payload->{lax} ? 1 : 0 );
		  = $self->journeys->sanity_check( $journey, $payload->{lax} ? 1 : 0 );
	}

	if ($error) {
@@ -526,7 +526,11 @@ sub import_v1 {
		);
	}
	else {
		$self->invalidate_stats_cache( $opt{rt_departure}, $db, $uid );
		$self->journeys->invalidate_stats_cache(
			ts  => $opt{rt_departure},
			db  => $db,
			uid => $uid
		);
		$tx->commit;
		$self->render(
			json => {
+8 −5
Original line number Diff line number Diff line
@@ -50,7 +50,8 @@ sub mark_if_missed_connection {
sub mark_substitute_connection {
	my ( $self, $journey ) = @_;

	my @substitute_candidates = reverse $self->get_user_travels(
	my @substitute_candidates = reverse $self->journeys->get(
		uid    => $self->current_user->{id},
		after  => $journey->{sched_departure}->clone->subtract( hours => 1 ),
		before => $journey->{sched_departure}->clone->add( hours => 12 ),
		with_datetime => 1,
@@ -87,7 +88,8 @@ sub list_candidates {
	my $now         = DateTime->now( time_zone => 'Europe/Berlin' );
	my $range_start = $now->clone->subtract( months => 6 );

	my @journeys = $self->get_user_travels(
	my @journeys = $self->journeys->get(
		uid           => $self->current_user->{id},
		after         => $range_start,
		before        => $now,
		with_datetime => 1,
@@ -112,7 +114,8 @@ sub list_candidates {

	@journeys = grep { $_->{delay} >= 60 or $_->{connection_missed} } @journeys;

	my @cancelled = $self->get_user_travels(
	my @cancelled = $self->journeys->get(
		uid           => $self->current_user->{id},
		after         => $range_start,
		before        => $now,
		cancelled     => 1,
@@ -163,7 +166,7 @@ sub generate {
		return;
	}

	my $journey = $self->get_journey(
	my $journey = $self->journeys->get_single(
		uid           => $uid,
		journey_id    => $journey_id,
		verbose       => 1,
@@ -187,7 +190,7 @@ sub generate {
		$self->mark_substitute_connection($journey);
	}
	elsif ( $journey->{delay} < 120 ) {
		my @connections = $self->get_user_travels(
		my @connections = $self->journey->get(
			uid           => $uid,
			after         => $journey->{rt_arrival},
			before        => $journey->{rt_arrival}->clone->add( hours => 2 ),
+76 −33

File changed.

Preview size limit exceeded, changes collapsed.

+751 −0

File added.

Preview size limit exceeded, changes collapsed.

Loading