Loading lib/Travelynx.pm +221 −186 Original line number Diff line number Diff line Loading @@ -588,7 +588,7 @@ sub startup { ); $self->helper( 'checkout' => sub { 'checkout_p' => sub { my ( $self, %opt ) = @_; my $station = $opt{station}; Loading @@ -596,34 +596,29 @@ sub startup { my $arr_eva = $opt{arr_eva}; my $with_related = $opt{with_related} // 0; my $force = $opt{force}; my $uid = $opt{uid}; my $uid = $opt{uid} // $self->current_user->{id}; my $db = $opt{db} // $self->pg->db; my $status = $self->iris->get_departures( station => $station, lookbehind => 120, lookahead => 180, with_related => $with_related, ); $uid //= $self->current_user->{id}; my $user = $self->get_user_status( $uid, $db ); my $train_id = $user->{train_id}; my $promise = Mojo::Promise->new; if ( not $station ) { $self->app->log->error("Checkout($uid): station is empty"); return ( 1, 'BUG: Checkout station is empty.' ); return $promise->resolve( 1, 'BUG: Checkout station is empty.' ); } if ( not $user->{checked_in} and not $user->{cancelled} ) { return ( 0, 'You are not checked into any train' ); } if ( $status->{errstr} and not $force ) { return ( 1, $status->{errstr} ); return $promise->resolve( 0, 'You are not checked into any train' ); } if ( $dep_eva and $dep_eva != $user->{dep_eva} ) { return ( 0, 'race condition' ); return $promise->resolve( 0, 'race condition' ); } if ( $arr_eva and $arr_eva != $user->{arr_eva} ) { return ( 0, 'race condition' ); return $promise->resolve( 0, 'race condition' ); } my $now = DateTime->now( time_zone => 'Europe/Berlin' ); Loading @@ -632,23 +627,21 @@ sub startup { with_data => 1 ); # Note that a train may pass the same station several times. # Notable example: S41 / S42 ("Ringbahn") both starts and # terminates at Berlin Südkreuz my ($train) = List::Util::first { $_->train_id eq $train_id and $_->sched_arrival and $_->sched_arrival->epoch > $user->{sched_departure}->epoch } @{ $status->{results} }; $train //= List::Util::first { $_->train_id eq $train_id } @{ $status->{results} }; $self->iris->get_departures_p( station => $station, lookbehind => 120, lookahead => 180, with_related => $with_related, )->then( sub { my ($status) = @_; my $new_checkout_station_id = $status->{station_eva}; # Store the intended checkout station regardless of this operation's # success. # TODO for with_related == 1, the correct EVA may be different # and should be fetched from $train later on $self->in_transit->set_arrival_eva( uid => $uid, db => $db, Loading @@ -667,10 +660,24 @@ sub startup { ); } # Note that a train may pass the same station several times. # Notable example: S41 / S42 ("Ringbahn") both starts and # terminates at Berlin Südkreuz my $train = List::Util::first { $_->train_id eq $train_id and $_->sched_arrival and $_->sched_arrival->epoch > $user->{sched_departure}->epoch } @{ $status->{results} }; $train //= List::Util::first { $_->train_id eq $train_id } @{ $status->{results} }; if ( not defined $train ) { # Arrival time via IRIS is unknown, so the train probably has not # arrived yet. Fall back to HAFAS. # Arrival time via IRIS is unknown, so the train probably # has not arrived yet. Fall back to HAFAS. # TODO support cases where $station is EVA or DS100 code if ( my $station_data Loading @@ -682,14 +689,16 @@ sub startup { if ( $station_data->{sched_arr} ) { my $sched_arr = epoch_to_dt( $station_data->{sched_arr} ); my $rt_arr = epoch_to_dt( $station_data->{rt_arr} ); my $rt_arr = epoch_to_dt( $station_data->{rt_arr} ); if ( $rt_arr->epoch == 0 ) { $rt_arr = $sched_arr->clone; if ( $station_data->{arr_delay} and $station_data->{arr_delay} =~ m{^\d+$} ) and $station_data->{arr_delay} =~ m{^\d+$} ) { $rt_arr->add( minutes => $station_data->{arr_delay} ); $rt_arr->add( minutes => $station_data->{arr_delay} ); } } $self->in_transit->set_arrival_times( Loading @@ -706,10 +715,10 @@ sub startup { if ( not $opt{in_transaction} ) { $self->run_hook( $uid, 'update' ); } return ( 1, undef ); $promise->resolve( 1, undef ); return; } } my $has_arrived = 0; eval { Loading @@ -719,7 +728,10 @@ sub startup { $tx = $db->begin; } if ( defined $train and not $train->arrival and not $force ) { if ( defined $train and not $train->arrival and not $force ) { my $train_no = $train->train_no; die("Train ${train_no} has no arrival timestamp\n"); } Loading @@ -731,10 +743,12 @@ sub startup { route => [ $self->iris->route_diff($train) ] ); $has_arrived = $train->arrival->epoch < $now->epoch ? 1 : 0; $has_arrived = $train->arrival->epoch < $now->epoch ? 1 : 0; if ($has_arrived) { my @unknown_stations = $self->stations->grep_unknown( $train->route ); = $self->stations->grep_unknown( $train->route ); if (@unknown_stations) { $self->app->log->warn( sprintf( Loading Loading @@ -767,7 +781,8 @@ sub startup { my $cache_ts = $now->clone; if ( $journey->{real_departure} =~ m{ ^ (?<year> \d{4} ) - (?<month> \d{2} ) }x ) =~ m{ ^ (?<year> \d{4} ) - (?<month> \d{2} ) }x ) { $cache_ts->set( year => $+{year}, Loading @@ -780,7 +795,9 @@ sub startup { uid => $uid ); } elsif ( defined $train and $train->arrival_is_cancelled ) { elsif ( defined $train and $train->arrival_is_cancelled ) { # This branch is only taken if the deparure was not cancelled, # i.e., if the train was supposed to go here but got Loading Loading @@ -808,20 +825,34 @@ sub startup { if ($@) { $self->app->log->error("Checkout($uid): $@"); return ( 1, 'Checkout error: ' . $@ ); $promise->resolve( 1, 'Checkout error: ' . $@ ); return; } if ( $has_arrived or $force ) { if ( not $opt{in_transaction} ) { $self->run_hook( $uid, 'checkout' ); } return ( 0, undef ); $promise->resolve( 0, undef ); return; } if ( not $opt{in_transaction} ) { $self->run_hook( $uid, 'update' ); $self->add_route_timestamps( $uid, $train, 0, 1 ); } return ( 1, undef ); $promise->resolve( 1, undef ); return; } )->catch( sub { my ($err) = @_; $promise->resolve( 1, $err ); return; } )->wait; return $promise; } ); Loading Loading @@ -1788,13 +1819,17 @@ sub startup { )->then( sub { $self->log->debug("... handled origin"); my ( undef, $err ) = $self->checkout( return $self->checkout_p( station => $traewelling->{arr_eva}, train_id => 0, uid => $uid, in_transaction => 1, db => $db ); } )->then( sub { my ( undef, $err ) = @_; if ($err) { $self->log->debug("... error: $err"); return Mojo::Promise->reject($err); Loading lib/Travelynx/Command/work.pm +19 −16 Original line number Diff line number Diff line Loading @@ -88,13 +88,13 @@ sub run { # check out (adds a cancelled journey and resets journey state # to checkin $self->app->checkout( $self->app->checkout_p( station => $arr, force => 1, force => 2, dep_eva => $dep, arr_eva => $arr, uid => $uid ); )->wait; } } else { Loading Loading @@ -155,13 +155,13 @@ sub run { # check out (adds a cancelled journey and resets journey state # to destination selection) $self->app->checkout( $self->app->checkout_p( station => $arr, force => 0, dep_eva => $dep, arr_eva => $arr, uid => $uid ); )->wait; } else { $self->app->add_route_timestamps( Loading @@ -174,21 +174,24 @@ sub run { } } elsif ( $entry->{real_arr_ts} ) { my ( undef, $error ) = $self->app->checkout( my ( undef, $error ) = $self->app->checkout_p( station => $arr, force => 1, force => 2, dep_eva => $dep, arr_eva => $arr, uid => $uid ); if ($error) { die("${error}\n"); )->catch( sub { my ($error) = @_; $self->app->log->error("work($uid)/arrival: $@"); $errors += 1; } )->wait; } }; if ($@) { $errors += 1; $self->app->log->error("work($uid)/arrival: $@"); $errors += 1; } eval { } Loading lib/Travelynx/Controller/Traveling.pm +103 −50 Original line number Diff line number Diff line Loading @@ -622,17 +622,26 @@ sub travel_action { if ( $params->{action} eq 'checkin' ) { my $status = $self->get_user_status; my $promise; if ( $status->{checked_in} and $status->{arr_eva} and $status->{arrival_countdown} <= 0 ) { $self->checkout( station => $status->{arr_eva} ); $promise = $self->checkout_p( station => $status->{arr_eva} ); } else { $promise = Mojo::Promise->resolve; } $self->render_later; $self->checkin_p( $promise->then( sub { return $self->checkin_p( station => $params->{station}, train_id => $params->{train} ); } )->then( sub { my $destination = $params->{dest}; Loading @@ -648,18 +657,27 @@ sub travel_action { # Silently ignore errors -- if they are permanent, the user will see # them when selecting the destination manually. my ( $still_checked_in, undef ) = $self->checkout( return $self->checkout_p( station => $destination, force => 0 ); } )->then( sub { my ( $still_checked_in, undef ) = @_; if ( my $destination = $params->{dest} ) { my $station_link = '/s/' . $destination; $self->render( json => { success => 1, redirect_to => $still_checked_in ? '/' : $station_link, redirect_to => $still_checked_in ? '/' : $station_link, }, ); } return; } )->catch( sub { my ($error) = @_; Loading @@ -673,10 +691,13 @@ sub travel_action { )->wait; } elsif ( $params->{action} eq 'checkout' ) { my ( $still_checked_in, $error ) = $self->checkout( $self->render_later; $self->checkout_p( station => $params->{station}, force => $params->{force} ); )->then( sub { my ( $still_checked_in, $error ) = @_; my $station_link = '/s/' . $params->{station}; if ($error) { Loading @@ -691,10 +712,26 @@ sub travel_action { $self->render( json => { success => 1, redirect_to => $still_checked_in ? '/' : $station_link, redirect_to => $still_checked_in ? '/' : $station_link, }, ); } return; } )->catch( sub { my ($error) = @_; $self->render( json => { success => 0, error => $error, }, ); return; } )->wait; } elsif ( $params->{action} eq 'undo' ) { my $status = $self->get_user_status; Loading Loading @@ -747,11 +784,13 @@ sub travel_action { )->wait; } elsif ( $params->{action} eq 'cancelled_to' ) { my ( undef, $error ) = $self->checkout( $self->render_later; $self->checkout_p( station => $params->{station}, force => 1 ); )->then( sub { my ( undef, $error ) = @_; if ($error) { $self->render( json => { Loading @@ -768,6 +807,20 @@ sub travel_action { }, ); } return; } )->catch( sub { my ($error) = @_; $self->render( json => { success => 0, error => $error, }, ); return; } )->wait; } elsif ( $params->{action} eq 'delete' ) { my $error = $self->journeys->delete( Loading Loading
lib/Travelynx.pm +221 −186 Original line number Diff line number Diff line Loading @@ -588,7 +588,7 @@ sub startup { ); $self->helper( 'checkout' => sub { 'checkout_p' => sub { my ( $self, %opt ) = @_; my $station = $opt{station}; Loading @@ -596,34 +596,29 @@ sub startup { my $arr_eva = $opt{arr_eva}; my $with_related = $opt{with_related} // 0; my $force = $opt{force}; my $uid = $opt{uid}; my $uid = $opt{uid} // $self->current_user->{id}; my $db = $opt{db} // $self->pg->db; my $status = $self->iris->get_departures( station => $station, lookbehind => 120, lookahead => 180, with_related => $with_related, ); $uid //= $self->current_user->{id}; my $user = $self->get_user_status( $uid, $db ); my $train_id = $user->{train_id}; my $promise = Mojo::Promise->new; if ( not $station ) { $self->app->log->error("Checkout($uid): station is empty"); return ( 1, 'BUG: Checkout station is empty.' ); return $promise->resolve( 1, 'BUG: Checkout station is empty.' ); } if ( not $user->{checked_in} and not $user->{cancelled} ) { return ( 0, 'You are not checked into any train' ); } if ( $status->{errstr} and not $force ) { return ( 1, $status->{errstr} ); return $promise->resolve( 0, 'You are not checked into any train' ); } if ( $dep_eva and $dep_eva != $user->{dep_eva} ) { return ( 0, 'race condition' ); return $promise->resolve( 0, 'race condition' ); } if ( $arr_eva and $arr_eva != $user->{arr_eva} ) { return ( 0, 'race condition' ); return $promise->resolve( 0, 'race condition' ); } my $now = DateTime->now( time_zone => 'Europe/Berlin' ); Loading @@ -632,23 +627,21 @@ sub startup { with_data => 1 ); # Note that a train may pass the same station several times. # Notable example: S41 / S42 ("Ringbahn") both starts and # terminates at Berlin Südkreuz my ($train) = List::Util::first { $_->train_id eq $train_id and $_->sched_arrival and $_->sched_arrival->epoch > $user->{sched_departure}->epoch } @{ $status->{results} }; $train //= List::Util::first { $_->train_id eq $train_id } @{ $status->{results} }; $self->iris->get_departures_p( station => $station, lookbehind => 120, lookahead => 180, with_related => $with_related, )->then( sub { my ($status) = @_; my $new_checkout_station_id = $status->{station_eva}; # Store the intended checkout station regardless of this operation's # success. # TODO for with_related == 1, the correct EVA may be different # and should be fetched from $train later on $self->in_transit->set_arrival_eva( uid => $uid, db => $db, Loading @@ -667,10 +660,24 @@ sub startup { ); } # Note that a train may pass the same station several times. # Notable example: S41 / S42 ("Ringbahn") both starts and # terminates at Berlin Südkreuz my $train = List::Util::first { $_->train_id eq $train_id and $_->sched_arrival and $_->sched_arrival->epoch > $user->{sched_departure}->epoch } @{ $status->{results} }; $train //= List::Util::first { $_->train_id eq $train_id } @{ $status->{results} }; if ( not defined $train ) { # Arrival time via IRIS is unknown, so the train probably has not # arrived yet. Fall back to HAFAS. # Arrival time via IRIS is unknown, so the train probably # has not arrived yet. Fall back to HAFAS. # TODO support cases where $station is EVA or DS100 code if ( my $station_data Loading @@ -682,14 +689,16 @@ sub startup { if ( $station_data->{sched_arr} ) { my $sched_arr = epoch_to_dt( $station_data->{sched_arr} ); my $rt_arr = epoch_to_dt( $station_data->{rt_arr} ); my $rt_arr = epoch_to_dt( $station_data->{rt_arr} ); if ( $rt_arr->epoch == 0 ) { $rt_arr = $sched_arr->clone; if ( $station_data->{arr_delay} and $station_data->{arr_delay} =~ m{^\d+$} ) and $station_data->{arr_delay} =~ m{^\d+$} ) { $rt_arr->add( minutes => $station_data->{arr_delay} ); $rt_arr->add( minutes => $station_data->{arr_delay} ); } } $self->in_transit->set_arrival_times( Loading @@ -706,10 +715,10 @@ sub startup { if ( not $opt{in_transaction} ) { $self->run_hook( $uid, 'update' ); } return ( 1, undef ); $promise->resolve( 1, undef ); return; } } my $has_arrived = 0; eval { Loading @@ -719,7 +728,10 @@ sub startup { $tx = $db->begin; } if ( defined $train and not $train->arrival and not $force ) { if ( defined $train and not $train->arrival and not $force ) { my $train_no = $train->train_no; die("Train ${train_no} has no arrival timestamp\n"); } Loading @@ -731,10 +743,12 @@ sub startup { route => [ $self->iris->route_diff($train) ] ); $has_arrived = $train->arrival->epoch < $now->epoch ? 1 : 0; $has_arrived = $train->arrival->epoch < $now->epoch ? 1 : 0; if ($has_arrived) { my @unknown_stations = $self->stations->grep_unknown( $train->route ); = $self->stations->grep_unknown( $train->route ); if (@unknown_stations) { $self->app->log->warn( sprintf( Loading Loading @@ -767,7 +781,8 @@ sub startup { my $cache_ts = $now->clone; if ( $journey->{real_departure} =~ m{ ^ (?<year> \d{4} ) - (?<month> \d{2} ) }x ) =~ m{ ^ (?<year> \d{4} ) - (?<month> \d{2} ) }x ) { $cache_ts->set( year => $+{year}, Loading @@ -780,7 +795,9 @@ sub startup { uid => $uid ); } elsif ( defined $train and $train->arrival_is_cancelled ) { elsif ( defined $train and $train->arrival_is_cancelled ) { # This branch is only taken if the deparure was not cancelled, # i.e., if the train was supposed to go here but got Loading Loading @@ -808,20 +825,34 @@ sub startup { if ($@) { $self->app->log->error("Checkout($uid): $@"); return ( 1, 'Checkout error: ' . $@ ); $promise->resolve( 1, 'Checkout error: ' . $@ ); return; } if ( $has_arrived or $force ) { if ( not $opt{in_transaction} ) { $self->run_hook( $uid, 'checkout' ); } return ( 0, undef ); $promise->resolve( 0, undef ); return; } if ( not $opt{in_transaction} ) { $self->run_hook( $uid, 'update' ); $self->add_route_timestamps( $uid, $train, 0, 1 ); } return ( 1, undef ); $promise->resolve( 1, undef ); return; } )->catch( sub { my ($err) = @_; $promise->resolve( 1, $err ); return; } )->wait; return $promise; } ); Loading Loading @@ -1788,13 +1819,17 @@ sub startup { )->then( sub { $self->log->debug("... handled origin"); my ( undef, $err ) = $self->checkout( return $self->checkout_p( station => $traewelling->{arr_eva}, train_id => 0, uid => $uid, in_transaction => 1, db => $db ); } )->then( sub { my ( undef, $err ) = @_; if ($err) { $self->log->debug("... error: $err"); return Mojo::Promise->reject($err); Loading
lib/Travelynx/Command/work.pm +19 −16 Original line number Diff line number Diff line Loading @@ -88,13 +88,13 @@ sub run { # check out (adds a cancelled journey and resets journey state # to checkin $self->app->checkout( $self->app->checkout_p( station => $arr, force => 1, force => 2, dep_eva => $dep, arr_eva => $arr, uid => $uid ); )->wait; } } else { Loading Loading @@ -155,13 +155,13 @@ sub run { # check out (adds a cancelled journey and resets journey state # to destination selection) $self->app->checkout( $self->app->checkout_p( station => $arr, force => 0, dep_eva => $dep, arr_eva => $arr, uid => $uid ); )->wait; } else { $self->app->add_route_timestamps( Loading @@ -174,21 +174,24 @@ sub run { } } elsif ( $entry->{real_arr_ts} ) { my ( undef, $error ) = $self->app->checkout( my ( undef, $error ) = $self->app->checkout_p( station => $arr, force => 1, force => 2, dep_eva => $dep, arr_eva => $arr, uid => $uid ); if ($error) { die("${error}\n"); )->catch( sub { my ($error) = @_; $self->app->log->error("work($uid)/arrival: $@"); $errors += 1; } )->wait; } }; if ($@) { $errors += 1; $self->app->log->error("work($uid)/arrival: $@"); $errors += 1; } eval { } Loading
lib/Travelynx/Controller/Traveling.pm +103 −50 Original line number Diff line number Diff line Loading @@ -622,17 +622,26 @@ sub travel_action { if ( $params->{action} eq 'checkin' ) { my $status = $self->get_user_status; my $promise; if ( $status->{checked_in} and $status->{arr_eva} and $status->{arrival_countdown} <= 0 ) { $self->checkout( station => $status->{arr_eva} ); $promise = $self->checkout_p( station => $status->{arr_eva} ); } else { $promise = Mojo::Promise->resolve; } $self->render_later; $self->checkin_p( $promise->then( sub { return $self->checkin_p( station => $params->{station}, train_id => $params->{train} ); } )->then( sub { my $destination = $params->{dest}; Loading @@ -648,18 +657,27 @@ sub travel_action { # Silently ignore errors -- if they are permanent, the user will see # them when selecting the destination manually. my ( $still_checked_in, undef ) = $self->checkout( return $self->checkout_p( station => $destination, force => 0 ); } )->then( sub { my ( $still_checked_in, undef ) = @_; if ( my $destination = $params->{dest} ) { my $station_link = '/s/' . $destination; $self->render( json => { success => 1, redirect_to => $still_checked_in ? '/' : $station_link, redirect_to => $still_checked_in ? '/' : $station_link, }, ); } return; } )->catch( sub { my ($error) = @_; Loading @@ -673,10 +691,13 @@ sub travel_action { )->wait; } elsif ( $params->{action} eq 'checkout' ) { my ( $still_checked_in, $error ) = $self->checkout( $self->render_later; $self->checkout_p( station => $params->{station}, force => $params->{force} ); )->then( sub { my ( $still_checked_in, $error ) = @_; my $station_link = '/s/' . $params->{station}; if ($error) { Loading @@ -691,10 +712,26 @@ sub travel_action { $self->render( json => { success => 1, redirect_to => $still_checked_in ? '/' : $station_link, redirect_to => $still_checked_in ? '/' : $station_link, }, ); } return; } )->catch( sub { my ($error) = @_; $self->render( json => { success => 0, error => $error, }, ); return; } )->wait; } elsif ( $params->{action} eq 'undo' ) { my $status = $self->get_user_status; Loading Loading @@ -747,11 +784,13 @@ sub travel_action { )->wait; } elsif ( $params->{action} eq 'cancelled_to' ) { my ( undef, $error ) = $self->checkout( $self->render_later; $self->checkout_p( station => $params->{station}, force => 1 ); )->then( sub { my ( undef, $error ) = @_; if ($error) { $self->render( json => { Loading @@ -768,6 +807,20 @@ sub travel_action { }, ); } return; } )->catch( sub { my ($error) = @_; $self->render( json => { success => 0, error => $error, }, ); return; } )->wait; } elsif ( $params->{action} eq 'delete' ) { my $error = $self->journeys->delete( Loading