convert checkout to promises (checkout_p)

This commit is contained in:
Birte Kristina Friesel 2023-07-23 20:18:10 +02:00
parent c0754f9e87
commit 38ad42b42c
No known key found for this signature in database
GPG key ID: 19E6E524EBB177BA
3 changed files with 347 additions and 256 deletions

View file

@ -588,7 +588,7 @@ sub startup {
); );
$self->helper( $self->helper(
'checkout' => sub { 'checkout_p' => sub {
my ( $self, %opt ) = @_; my ( $self, %opt ) = @_;
my $station = $opt{station}; my $station = $opt{station};
@ -596,34 +596,29 @@ sub startup {
my $arr_eva = $opt{arr_eva}; my $arr_eva = $opt{arr_eva};
my $with_related = $opt{with_related} // 0; my $with_related = $opt{with_related} // 0;
my $force = $opt{force}; my $force = $opt{force};
my $uid = $opt{uid}; my $uid = $opt{uid} // $self->current_user->{id};
my $db = $opt{db} // $self->pg->db; 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 $user = $self->get_user_status( $uid, $db );
my $train_id = $user->{train_id}; my $train_id = $user->{train_id};
my $promise = Mojo::Promise->new;
if ( not $station ) { if ( not $station ) {
$self->app->log->error("Checkout($uid): station is empty"); $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} ) { if ( not $user->{checked_in} and not $user->{cancelled} ) {
return ( 0, 'You are not checked into any train' ); return $promise->resolve( 0,
} 'You are not checked into any train' );
if ( $status->{errstr} and not $force ) {
return ( 1, $status->{errstr} );
} }
if ( $dep_eva and $dep_eva != $user->{dep_eva} ) { 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} ) { 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' ); my $now = DateTime->now( time_zone => 'Europe/Berlin' );
@ -632,23 +627,21 @@ sub startup {
with_data => 1 with_data => 1
); );
# Note that a train may pass the same station several times. $self->iris->get_departures_p(
# Notable example: S41 / S42 ("Ringbahn") both starts and station => $station,
# terminates at Berlin Südkreuz lookbehind => 120,
my ($train) = List::Util::first { lookahead => 180,
$_->train_id eq $train_id with_related => $with_related,
and $_->sched_arrival )->then(
and $_->sched_arrival->epoch > $user->{sched_departure}->epoch sub {
} my ($status) = @_;
@{ $status->{results} };
$train //= List::Util::first { $_->train_id eq $train_id }
@{ $status->{results} };
my $new_checkout_station_id = $status->{station_eva}; my $new_checkout_station_id = $status->{station_eva};
# Store the intended checkout station regardless of this operation's # Store the intended checkout station regardless of this operation's
# success. # 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( $self->in_transit->set_arrival_eva(
uid => $uid, uid => $uid,
db => $db, db => $db,
@ -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 ) { if ( not defined $train ) {
# Arrival time via IRIS is unknown, so the train probably has not # Arrival time via IRIS is unknown, so the train probably
# arrived yet. Fall back to HAFAS. # has not arrived yet. Fall back to HAFAS.
# TODO support cases where $station is EVA or DS100 code # TODO support cases where $station is EVA or DS100 code
if ( if (
my $station_data my $station_data
@ -682,14 +689,16 @@ sub startup {
if ( $station_data->{sched_arr} ) { if ( $station_data->{sched_arr} ) {
my $sched_arr my $sched_arr
= epoch_to_dt( $station_data->{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 ) { if ( $rt_arr->epoch == 0 ) {
$rt_arr = $sched_arr->clone; $rt_arr = $sched_arr->clone;
if ( $station_data->{arr_delay} if ( $station_data->{arr_delay}
and $station_data->{arr_delay} =~ m{^\d+$} ) and $station_data->{arr_delay}
=~ m{^\d+$} )
{ {
$rt_arr->add( $rt_arr->add( minutes =>
minutes => $station_data->{arr_delay} ); $station_data->{arr_delay} );
} }
} }
$self->in_transit->set_arrival_times( $self->in_transit->set_arrival_times(
@ -706,10 +715,10 @@ sub startup {
if ( not $opt{in_transaction} ) { if ( not $opt{in_transaction} ) {
$self->run_hook( $uid, 'update' ); $self->run_hook( $uid, 'update' );
} }
return ( 1, undef ); $promise->resolve( 1, undef );
return;
} }
} }
my $has_arrived = 0; my $has_arrived = 0;
eval { eval {
@ -719,7 +728,10 @@ sub startup {
$tx = $db->begin; $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; my $train_no = $train->train_no;
die("Train ${train_no} has no arrival timestamp\n"); die("Train ${train_no} has no arrival timestamp\n");
} }
@ -731,10 +743,12 @@ sub startup {
route => [ $self->iris->route_diff($train) ] 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) { if ($has_arrived) {
my @unknown_stations my @unknown_stations
= $self->stations->grep_unknown( $train->route ); = $self->stations->grep_unknown(
$train->route );
if (@unknown_stations) { if (@unknown_stations) {
$self->app->log->warn( $self->app->log->warn(
sprintf( sprintf(
@ -767,7 +781,8 @@ sub startup {
my $cache_ts = $now->clone; my $cache_ts = $now->clone;
if ( $journey->{real_departure} if ( $journey->{real_departure}
=~ m{ ^ (?<year> \d{4} ) - (?<month> \d{2} ) }x ) =~ m{ ^ (?<year> \d{4} ) - (?<month> \d{2} ) }x
)
{ {
$cache_ts->set( $cache_ts->set(
year => $+{year}, year => $+{year},
@ -780,7 +795,9 @@ sub startup {
uid => $uid 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, # This branch is only taken if the deparure was not cancelled,
# i.e., if the train was supposed to go here but got # i.e., if the train was supposed to go here but got
@ -808,20 +825,34 @@ sub startup {
if ($@) { if ($@) {
$self->app->log->error("Checkout($uid): $@"); $self->app->log->error("Checkout($uid): $@");
return ( 1, 'Checkout error: ' . $@ ); $promise->resolve( 1, 'Checkout error: ' . $@ );
return;
} }
if ( $has_arrived or $force ) { if ( $has_arrived or $force ) {
if ( not $opt{in_transaction} ) { if ( not $opt{in_transaction} ) {
$self->run_hook( $uid, 'checkout' ); $self->run_hook( $uid, 'checkout' );
} }
return ( 0, undef ); $promise->resolve( 0, undef );
return;
} }
if ( not $opt{in_transaction} ) { if ( not $opt{in_transaction} ) {
$self->run_hook( $uid, 'update' ); $self->run_hook( $uid, 'update' );
$self->add_route_timestamps( $uid, $train, 0, 1 ); $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;
} }
); );
@ -1788,13 +1819,17 @@ sub startup {
)->then( )->then(
sub { sub {
$self->log->debug("... handled origin"); $self->log->debug("... handled origin");
my ( undef, $err ) = $self->checkout( return $self->checkout_p(
station => $traewelling->{arr_eva}, station => $traewelling->{arr_eva},
train_id => 0, train_id => 0,
uid => $uid, uid => $uid,
in_transaction => 1, in_transaction => 1,
db => $db db => $db
); );
}
)->then(
sub {
my ( undef, $err ) = @_;
if ($err) { if ($err) {
$self->log->debug("... error: $err"); $self->log->debug("... error: $err");
return Mojo::Promise->reject($err); return Mojo::Promise->reject($err);

View file

@ -88,13 +88,13 @@ sub run {
# check out (adds a cancelled journey and resets journey state # check out (adds a cancelled journey and resets journey state
# to checkin # to checkin
$self->app->checkout( $self->app->checkout_p(
station => $arr, station => $arr,
force => 1, force => 2,
dep_eva => $dep, dep_eva => $dep,
arr_eva => $arr, arr_eva => $arr,
uid => $uid uid => $uid
); )->wait;
} }
} }
else { else {
@ -155,13 +155,13 @@ sub run {
# check out (adds a cancelled journey and resets journey state # check out (adds a cancelled journey and resets journey state
# to destination selection) # to destination selection)
$self->app->checkout( $self->app->checkout_p(
station => $arr, station => $arr,
force => 0, force => 0,
dep_eva => $dep, dep_eva => $dep,
arr_eva => $arr, arr_eva => $arr,
uid => $uid uid => $uid
); )->wait;
} }
else { else {
$self->app->add_route_timestamps( $self->app->add_route_timestamps(
@ -174,21 +174,24 @@ sub run {
} }
} }
elsif ( $entry->{real_arr_ts} ) { elsif ( $entry->{real_arr_ts} ) {
my ( undef, $error ) = $self->app->checkout( my ( undef, $error ) = $self->app->checkout_p(
station => $arr, station => $arr,
force => 1, force => 2,
dep_eva => $dep, dep_eva => $dep,
arr_eva => $arr, arr_eva => $arr,
uid => $uid uid => $uid
); )->catch(
if ($error) { sub {
die("${error}\n"); my ($error) = @_;
$self->app->log->error("work($uid)/arrival: $@");
$errors += 1;
} }
)->wait;
} }
}; };
if ($@) { if ($@) {
$errors += 1;
$self->app->log->error("work($uid)/arrival: $@"); $self->app->log->error("work($uid)/arrival: $@");
$errors += 1;
} }
eval { } eval { }

View file

@ -622,17 +622,26 @@ sub travel_action {
if ( $params->{action} eq 'checkin' ) { if ( $params->{action} eq 'checkin' ) {
my $status = $self->get_user_status; my $status = $self->get_user_status;
my $promise;
if ( $status->{checked_in} if ( $status->{checked_in}
and $status->{arr_eva} and $status->{arr_eva}
and $status->{arrival_countdown} <= 0 ) 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->render_later;
$self->checkin_p( $promise->then(
sub {
return $self->checkin_p(
station => $params->{station}, station => $params->{station},
train_id => $params->{train} train_id => $params->{train}
);
}
)->then( )->then(
sub { sub {
my $destination = $params->{dest}; my $destination = $params->{dest};
@ -648,18 +657,27 @@ sub travel_action {
# Silently ignore errors -- if they are permanent, the user will see # Silently ignore errors -- if they are permanent, the user will see
# them when selecting the destination manually. # them when selecting the destination manually.
my ( $still_checked_in, undef ) = $self->checkout( return $self->checkout_p(
station => $destination, station => $destination,
force => 0 force => 0
); );
}
)->then(
sub {
my ( $still_checked_in, undef ) = @_;
if ( my $destination = $params->{dest} ) {
my $station_link = '/s/' . $destination; my $station_link = '/s/' . $destination;
$self->render( $self->render(
json => { json => {
success => 1, success => 1,
redirect_to => $still_checked_in ? '/' : $station_link, redirect_to => $still_checked_in
? '/'
: $station_link,
}, },
); );
} }
return;
}
)->catch( )->catch(
sub { sub {
my ($error) = @_; my ($error) = @_;
@ -673,10 +691,13 @@ sub travel_action {
)->wait; )->wait;
} }
elsif ( $params->{action} eq 'checkout' ) { elsif ( $params->{action} eq 'checkout' ) {
my ( $still_checked_in, $error ) = $self->checkout( $self->render_later;
$self->checkout_p(
station => $params->{station}, station => $params->{station},
force => $params->{force} force => $params->{force}
); )->then(
sub {
my ( $still_checked_in, $error ) = @_;
my $station_link = '/s/' . $params->{station}; my $station_link = '/s/' . $params->{station};
if ($error) { if ($error) {
@ -691,10 +712,26 @@ sub travel_action {
$self->render( $self->render(
json => { json => {
success => 1, 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' ) { elsif ( $params->{action} eq 'undo' ) {
my $status = $self->get_user_status; my $status = $self->get_user_status;
@ -747,11 +784,13 @@ sub travel_action {
)->wait; )->wait;
} }
elsif ( $params->{action} eq 'cancelled_to' ) { elsif ( $params->{action} eq 'cancelled_to' ) {
my ( undef, $error ) = $self->checkout( $self->render_later;
$self->checkout_p(
station => $params->{station}, station => $params->{station},
force => 1 force => 1
); )->then(
sub {
my ( undef, $error ) = @_;
if ($error) { if ($error) {
$self->render( $self->render(
json => { json => {
@ -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' ) { elsif ( $params->{action} eq 'delete' ) {
my $error = $self->journeys->delete( my $error = $self->journeys->delete(