Unverified Commit c06d6531 authored by Birte Kristina Friesel's avatar Birte Kristina Friesel
Browse files

Traveling: sort helpers <-> controllers

parent de73024f
Loading
Loading
Loading
Loading
+178 −174
Original line number Diff line number Diff line
@@ -14,6 +14,184 @@ use List::MoreUtils qw(first_index);
use Text::CSV;
use Travel::Status::DE::IRIS::Stations;

# Internal Helpers

sub has_str_in_list {
	my ( $str, @strs ) = @_;
	if ( List::Util::any { $str eq $_ } @strs ) {
		return 1;
	}
	return;
}

sub get_connecting_trains {
	my ( $self, %opt ) = @_;

	my $uid         = $opt{uid} //= $self->current_user->{id};
	my $use_history = $self->users->use_history( uid => $uid );

	my ( $eva, $exclude_via, $exclude_train_id, $exclude_before );
	my $now = $self->now->epoch;
	my ( $stationinfo, $arr_epoch, $arr_platform );

	if ( $opt{eva} ) {
		if ( $use_history & 0x01 ) {
			$eva = $opt{eva};
		}
		elsif ( $opt{destination_name} ) {
			$eva = $opt{eva};
		}
	}
	else {
		if ( $use_history & 0x02 ) {
			my $status = $self->get_user_status;
			$eva              = $status->{arr_eva};
			$exclude_via      = $status->{dep_name};
			$exclude_train_id = $status->{train_id};
			$arr_platform     = $status->{arr_platform};
			$stationinfo      = $status->{extra_data}{stationinfo_arr};
			if ( $status->{real_arrival} ) {
				$exclude_before = $arr_epoch = $status->{real_arrival}->epoch;
			}
		}
	}

	$exclude_before //= $now - 300;

	if ( not $eva ) {
		return;
	}

	my @destinations = $self->journeys->get_connection_targets(%opt);

	if ($exclude_via) {
		@destinations = grep { $_ ne $exclude_via } @destinations;
	}

	if ( not @destinations ) {
		return;
	}

	my $stationboard = $self->iris->get_departures(
		station      => $eva,
		lookbehind   => 10,
		lookahead    => 40,
		with_related => 1
	);
	if ( $stationboard->{errstr} ) {
		return;
	}
	@{ $stationboard->{results} } = map { $_->[0] }
	  sort { $a->[1] <=> $b->[1] }
	  map  { [ $_, $_->departure ? $_->departure->epoch : 0 ] }
	  @{ $stationboard->{results} };
	my @results;
	my @cancellations;
	my %via_count = map { $_ => 0 } @destinations;
	for my $train ( @{ $stationboard->{results} } ) {
		if ( not $train->departure ) {
			next;
		}
		if (    $exclude_before
			and $train->departure
			and $train->departure->epoch < $exclude_before )
		{
			next;
		}
		if (    $exclude_train_id
			and $train->train_id eq $exclude_train_id )
		{
			next;
		}

		# In general, this function is meant to return feasible
		# connections. However, cancelled connections may also be of
		# interest and are also useful for logging cancellations.
		# To satisfy both demands with (hopefully) little confusion and
		# UI clutter, this function returns two concatenated arrays:
		# actual connections (ordered by actual departure time) followed
		# by cancelled connections (ordered by scheduled departure time).
		# This is easiest to achieve in two separate loops.
		#
		# Note that a cancelled train may still have a matching destination
		# in its route_post, e.g. if it leaves out $eva due to
		# unscheduled route changes but continues on schedule afterwards
		# -- so it is only cancelled at $eva, not on the remainder of
		# the route. Also note that this specific case is not yet handled
		# properly by the cancellation logic etc.

		if ( $train->departure_is_cancelled ) {
			my @via = ( $train->sched_route_post, $train->sched_route_end );
			for my $dest (@destinations) {
				if ( List::Util::any { $_ eq $dest } @via ) {
					push( @cancellations, [ $train, $dest ] );
					next;
				}
			}
		}
		else {
			my @via = ( $train->route_post, $train->route_end );
			for my $dest (@destinations) {
				if ( $via_count{$dest} < 2
					and List::Util::any { $_ eq $dest } @via )
				{
					push( @results, [ $train, $dest ] );

                 # Show all past and up to two future departures per destination
					if ( not $train->departure
						or $train->departure->epoch >= $now )
					{
						$via_count{$dest}++;
					}
					next;
				}
			}
		}
	}

	@results = map { $_->[0] }
	  sort { $a->[1] <=> $b->[1] }
	  map {
		[ $_, $_->[0]->departure->epoch // $_->[0]->sched_departure->epoch ]
	  } @results;
	@cancellations = map { $_->[0] }
	  sort { $a->[1] <=> $b->[1] }
	  map { [ $_, $_->[0]->sched_departure->epoch ] } @cancellations;

	for my $result (@results) {
		my $train       = $result->[0];
		my @message_ids = List::Util::uniq map { $_->[1] } $train->raw_messages;
		$train->{message_id} = { map { $_ => 1 } @message_ids };
		my $interchange_duration;
		if ( exists $stationinfo->{i} ) {
			$interchange_duration
			  = $stationinfo->{i}{$arr_platform}{ $train->platform };
			$interchange_duration //= $stationinfo->{i}{"*"};
		}
		if ( defined $interchange_duration ) {
			my $interchange_time
			  = ( $train->departure->epoch - $arr_epoch ) / 60;
			if ( $interchange_time < $interchange_duration ) {
				$train->{interchange_text} = 'Anschluss knapp';
				$train->{interchange_icon} = 'warning';
			}
			elsif ( $interchange_time == $interchange_duration ) {
				$train->{interchange_text} = 'Anschluss könnte knapp werden';
				$train->{interchange_icon} = 'directions_run';
			}

       #else {
       #	$train->{interchange_text} = 'Anschluss wird voraussichtlich erreicht';
       #	$train->{interchange_icon} = 'check';
       #}
		}
	}

	return ( @results, @cancellations );
}

# Controllers

sub homepage {
	my ($self) = @_;
	if ( $self->is_user_authenticated ) {
@@ -852,14 +1030,6 @@ sub commute {
	);
}

sub has_str_in_list {
	my ( $str, @strs ) = @_;
	if ( List::Util::any { $str eq $_ } @strs ) {
		return 1;
	}
	return;
}

sub map_history {
	my ($self) = @_;

@@ -1476,170 +1646,4 @@ sub add_journey_form {
	}
}

sub get_connecting_trains {
	my ( $self, %opt ) = @_;

	my $uid         = $opt{uid} //= $self->current_user->{id};
	my $use_history = $self->users->use_history( uid => $uid );

	my ( $eva, $exclude_via, $exclude_train_id, $exclude_before );
	my $now = $self->now->epoch;
	my ( $stationinfo, $arr_epoch, $arr_platform );

	if ( $opt{eva} ) {
		if ( $use_history & 0x01 ) {
			$eva = $opt{eva};
		}
		elsif ( $opt{destination_name} ) {
			$eva = $opt{eva};
		}
	}
	else {
		if ( $use_history & 0x02 ) {
			my $status = $self->get_user_status;
			$eva              = $status->{arr_eva};
			$exclude_via      = $status->{dep_name};
			$exclude_train_id = $status->{train_id};
			$arr_platform     = $status->{arr_platform};
			$stationinfo      = $status->{extra_data}{stationinfo_arr};
			if ( $status->{real_arrival} ) {
				$exclude_before = $arr_epoch = $status->{real_arrival}->epoch;
			}
		}
	}

	$exclude_before //= $now - 300;

	if ( not $eva ) {
		return;
	}

	my @destinations = $self->journeys->get_connection_targets(%opt);

	if ($exclude_via) {
		@destinations = grep { $_ ne $exclude_via } @destinations;
	}

	if ( not @destinations ) {
		return;
	}

	my $stationboard = $self->iris->get_departures(
		station      => $eva,
		lookbehind   => 10,
		lookahead    => 40,
		with_related => 1
	);
	if ( $stationboard->{errstr} ) {
		return;
	}
	@{ $stationboard->{results} } = map { $_->[0] }
	  sort { $a->[1] <=> $b->[1] }
	  map  { [ $_, $_->departure ? $_->departure->epoch : 0 ] }
	  @{ $stationboard->{results} };
	my @results;
	my @cancellations;
	my %via_count = map { $_ => 0 } @destinations;
	for my $train ( @{ $stationboard->{results} } ) {
		if ( not $train->departure ) {
			next;
		}
		if (    $exclude_before
			and $train->departure
			and $train->departure->epoch < $exclude_before )
		{
			next;
		}
		if (    $exclude_train_id
			and $train->train_id eq $exclude_train_id )
		{
			next;
		}

		# In general, this function is meant to return feasible
		# connections. However, cancelled connections may also be of
		# interest and are also useful for logging cancellations.
		# To satisfy both demands with (hopefully) little confusion and
		# UI clutter, this function returns two concatenated arrays:
		# actual connections (ordered by actual departure time) followed
		# by cancelled connections (ordered by scheduled departure time).
		# This is easiest to achieve in two separate loops.
		#
		# Note that a cancelled train may still have a matching destination
		# in its route_post, e.g. if it leaves out $eva due to
		# unscheduled route changes but continues on schedule afterwards
		# -- so it is only cancelled at $eva, not on the remainder of
		# the route. Also note that this specific case is not yet handled
		# properly by the cancellation logic etc.

		if ( $train->departure_is_cancelled ) {
			my @via = ( $train->sched_route_post, $train->sched_route_end );
			for my $dest (@destinations) {
				if ( List::Util::any { $_ eq $dest } @via ) {
					push( @cancellations, [ $train, $dest ] );
					next;
				}
			}
		}
		else {
			my @via = ( $train->route_post, $train->route_end );
			for my $dest (@destinations) {
				if ( $via_count{$dest} < 2
					and List::Util::any { $_ eq $dest } @via )
				{
					push( @results, [ $train, $dest ] );

                 # Show all past and up to two future departures per destination
					if ( not $train->departure
						or $train->departure->epoch >= $now )
					{
						$via_count{$dest}++;
					}
					next;
				}
			}
		}
	}

	@results = map { $_->[0] }
	  sort { $a->[1] <=> $b->[1] }
	  map {
		[ $_, $_->[0]->departure->epoch // $_->[0]->sched_departure->epoch ]
	  } @results;
	@cancellations = map { $_->[0] }
	  sort { $a->[1] <=> $b->[1] }
	  map { [ $_, $_->[0]->sched_departure->epoch ] } @cancellations;

	for my $result (@results) {
		my $train       = $result->[0];
		my @message_ids = List::Util::uniq map { $_->[1] } $train->raw_messages;
		$train->{message_id} = { map { $_ => 1 } @message_ids };
		my $interchange_duration;
		if ( exists $stationinfo->{i} ) {
			$interchange_duration
			  = $stationinfo->{i}{$arr_platform}{ $train->platform };
			$interchange_duration //= $stationinfo->{i}{"*"};
		}
		if ( defined $interchange_duration ) {
			my $interchange_time
			  = ( $train->departure->epoch - $arr_epoch ) / 60;
			if ( $interchange_time < $interchange_duration ) {
				$train->{interchange_text} = 'Anschluss knapp';
				$train->{interchange_icon} = 'warning';
			}
			elsif ( $interchange_time == $interchange_duration ) {
				$train->{interchange_text} = 'Anschluss könnte knapp werden';
				$train->{interchange_icon} = 'directions_run';
			}

       #else {
       #	$train->{interchange_text} = 'Anschluss wird voraussichtlich erreicht';
       #	$train->{interchange_icon} = 'check';
       #}
		}
	}

	return ( @results, @cancellations );
}

1;