Refactor user_actions insert/update/delete statements with Mojo::Pg

This commit is contained in:
Daniel Friesel 2019-04-17 13:35:38 -04:00
parent ecedc6a4f2
commit 8ad05720a2
2 changed files with 235 additions and 253 deletions

View file

@ -242,58 +242,6 @@ sub startup {
); );
} }
); );
$self->attr(
action_set_sched_time_query => sub {
my ($self) = @_;
# TODO (re-)initialize all automatically added journeys with edited = 0
# and use it as a bit field to precisely indicate which fields have
# been edited. -> ".. set edited = edited | 1", "... | 2" etc.
# The action_id check is redundant, but better safe than sorry
return $self->app->dbh->prepare(
qq{
update user_actions
set sched_time = to_timestamp(?), edited = edited | 1
where id = ? and action_id = ?
}
);
}
);
$self->attr(
action_set_real_time_query => sub {
my ($self) = @_;
# The action_id check is redundant, but better safe than sorry
return $self->app->dbh->prepare(
qq{
update user_actions
set real_time = to_timestamp(?), edited = edited | 2
where id = ? and action_id = ?
}
);
}
);
$self->attr(
action_query => sub {
my ($self) = @_;
return $self->app->dbh->prepare(
qq{
insert into user_actions (
user_id, action_id, station_id, action_time, edited,
train_type, train_line, train_no, train_id,
sched_time, real_time,
route, messages
) values (
?, ?, ?, to_timestamp(?), ?,
?, ?, ?, ?,
to_timestamp(?), to_timestamp(?),
?, ?
)
}
);
},
);
$self->attr( $self->attr(
dbh => sub { dbh => sub {
my ($self) = @_; my ($self) = @_;
@ -394,19 +342,6 @@ sub startup {
); );
} }
); );
$self->attr(
drop_journey_query => sub {
my ($self) = @_;
return $self->app->dbh->prepare(
qq{
delete from user_actions
where user_id = ?
and (id = ? or id = ?)
}
);
}
);
$self->attr( $self->attr(
get_userid_query => sub { get_userid_query => sub {
my ($self) = @_; my ($self) = @_;
@ -640,60 +575,65 @@ qq{select * from pending_mails where email = ? and num_tries > 1;}
return ( undef, undef, 'Unbekannter Zielbahnhof' ); return ( undef, undef, 'Unbekannter Zielbahnhof' );
} }
say $dep_station->[0]; my $checkin_id;
say $dep_station->[1]; my $checkout_id;
say $arr_station->[0];
say $arr_station->[1];
my $success = $self->app->action_query->execute( eval {
$uid, $checkin_id = $self->pg->db->insert(
$self->app->action_type->{checkin}, 'user_actions',
$self->get_station_id( {
ds100 => $dep_station->[0], user_id => $uid,
name => $dep_station->[1], action_id => $self->app->action_type->{checkin},
), station_id => $self->get_station_id(
DateTime->now( time_zone => 'Europe/Berlin' )->epoch, ds100 => $dep_station->[0],
0x0f, name => $dep_station->[1],
$opt{train_type}, ),
$opt{train_line}, action_time =>
$opt{train_no}, DateTime->now( time_zone => 'Europe/Berlin' ),
undef, edited => 0x0f,
$opt{sched_departure}->epoch, train_type => $opt{train_type},
$opt{rt_departure} ? $opt{rt_departure}->epoch : undef, train_line => $opt{train_line},
undef, undef train_no => $opt{train_no},
); sched_time => $opt{sched_departure},
if ( not $success ) { real_time => $opt{rt_departure},
my $err = $self->app->action_query->errstr; },
$self->app->log->error( { returning => 'id' }
"add_journey($uid, checkin): INSERT failed: $err"); )->hash->{id};
return ( undef, undef, 'INSERT failed: ' . $err ); };
}
my $checkin_id = $self->app->action_query->last_insert_id;
$success = $self->app->action_query->execute( if ($@) {
$uid,
$self->app->action_type->{checkout},
$self->get_station_id(
ds100 => $arr_station->[0],
name => $arr_station->[1],
),
DateTime->now( time_zone => 'Europe/Berlin' )->epoch,
0x0f,
$opt{train_type},
$opt{train_line},
$opt{train_no},
undef,
$opt{sched_arrival}->epoch,
$opt{rt_arrival} ? $opt{rt_arrival}->epoch : undef,
undef, undef
);
if ( not $success ) {
my $err = $self->app->action_query->errstr;
$self->app->log->error( $self->app->log->error(
"add_journey($uid, checkout): INSERT failed: $err"); "add_journey($uid, checkin): INSERT failed: $@");
return ( undef, undef, 'INSERT failed: ' . $err ); return ( undef, undef, 'INSERT failed: ' . $@ );
}
eval {
$checkout_id = $self->pg->db->insert(
'user_actions',
{
user_id => $uid,
action_id => $self->app->action_type->{checkout},
station_id => $self->get_station_id(
ds100 => $arr_station->[0],
name => $arr_station->[1],
),
action_time =>
DateTime->now( time_zone => 'Europe/Berlin' ),
edited => 0x0f,
train_type => $opt{train_type},
train_line => $opt{train_line},
train_no => $opt{train_no},
sched_time => $opt{sched_arrival},
real_time => $opt{rt_arrival},
},
{ returnning => 'id' }
)->hash->{id};
};
if ($@) {
$self->app->log->error(
"add_journey($uid, checkout): INSERT failed: $@");
return ( undef, undef, 'INSERT failed: ' . $@ );
} }
my $checkout_id = $self->app->action_query->last_insert_id;
return ( $checkin_id, $checkout_id, undef ); return ( $checkin_id, $checkout_id, undef );
} }
); );
@ -730,40 +670,43 @@ qq{select * from pending_mails where email = ? and num_tries > 1;}
$self->app->action_type->{cancelled_to} ); $self->app->action_type->{cancelled_to} );
} }
my $success = $self->app->action_query->execute( eval {
$self->current_user->{id}, $self->pg->db->insert(
$action_id, 'user_actions',
$self->get_station_id( {
ds100 => $status->{station_ds100}, user_id => $self->current_user->{id},
name => $status->{station_name} action_id => $action_id,
), station_id => $self->get_station_id(
DateTime->now( time_zone => 'Europe/Berlin' )->epoch, ds100 => $status->{station_ds100},
0, name => $status->{station_name}
$train->type, ),
$train->line_no, action_time =>
$train->train_no, DateTime->now( time_zone => 'Europe/Berlin' ),
$train->train_id, edited => 0,
$train->sched_departure->epoch, train_type => $train->type,
$train->departure->epoch, train_line => $train->line_no,
join( '|', $train->route ), train_no => $train->train_no,
join( train_id => $train->train_id,
'|', sched_time => $train->sched_departure,
map { real_time => $train->departure,
( $_->[0] ? $_->[0]->epoch : q{} ) . ':' route => join( '|', $train->route ),
. $_->[1] messages => join(
} $train->messages '|',
) map {
); ( $_->[0] ? $_->[0]->epoch : q{} ) . ':'
if ( defined $success ) { . $_->[1]
return ( $train, undef ); } $train->messages
} )
else { }
);
};
if ($@) {
my $uid = $self->current_user->{id}; my $uid = $self->current_user->{id};
my $err = $self->app->action_query->errstr;
$self->app->log->error( $self->app->log->error(
"Checkin($uid, $action_id): INSERT failed: $err"); "Checkin($uid, $action_id): INSERT failed: $@");
return ( undef, 'INSERT failed: ' . $err ); return ( undef, 'INSERT failed: ' . $@ );
} }
return ( $train, undef );
} }
} }
} }
@ -876,72 +819,73 @@ qq{select * from pending_mails where email = ? and num_tries > 1;}
= first { $_->train_id eq $train_id } @{ $status->{results} }; = first { $_->train_id eq $train_id } @{ $status->{results} };
if ( not defined $train ) { if ( not defined $train ) {
if ($force) { if ($force) {
my $success = $self->app->action_query->execute( eval {
$self->current_user->{id}, $self->pg->db->insert(
$action_id, 'user_actions',
$self->get_station_id( {
ds100 => $status->{station_ds100}, user_id => $self->current_user->{id},
name => $status->{station_name} action_id => $action_id,
), station_id => $self->get_station_id(
$now->epoch, ds100 => $status->{station_ds100},
0, undef, undef, undef, undef, name => $status->{station_name}
undef, undef, undef, undef ),
); action_time => $now,
if ( defined $success ) { edited => 0
$self->invalidate_stats_cache; }
return;
}
else {
my $uid = $self->current_user->{id};
my $err = $self->app->action_query->errstr;
$self->app->log->error(
"Force checkout($uid, $action_id): INSERT failed: $err"
); );
return 'INSERT failed: ' . $err; };
if ($@) {
my $uid = $self->current_user->{id};
$self->app->log->error(
"Force checkout($uid, $action_id): INSERT failed: $@"
);
return 'INSERT failed: ' . $@;
} }
$self->invalidate_stats_cache;
return;
} }
else { else {
return "Train ${train_id} not found"; return "Train ${train_id} not found";
} }
} }
else { else {
my $success = $self->app->action_query->execute( eval {
$self->current_user->{id}, $self->pg->db->insert(
$action_id, 'user_actions',
$self->get_station_id( {
ds100 => $status->{station_ds100}, user_id => $self->current_user->{id},
name => $status->{station_name} action_id => $action_id,
), station_id => $self->get_station_id(
$now->epoch, ds100 => $status->{station_ds100},
0, name => $status->{station_name}
$train->type, ),
$train->line_no, action_time => $now,
$train->train_no, edited => 0,
$train->train_id, train_type => $train->type,
$train->sched_arrival train_line => $train->line_no,
? $train->sched_arrival->epoch train_no => $train->train_no,
: undef, train_id => $train->train_id,
$train->arrival ? $train->arrival->epoch : undef, sched_time => $train->sched_arrival,
join( '|', $train->route ), real_time => $train->arrival,
join( route => join( '|', $train->route ),
'|', messages => join(
map { '|',
( $_->[0] ? $_->[0]->epoch : q{} ) . ':' map {
. $_->[1] ( $_->[0] ? $_->[0]->epoch : q{} ) . ':'
} $train->messages . $_->[1]
) } $train->messages
); )
if ( defined $success ) { }
$self->invalidate_stats_cache; );
return; };
} if ($@) {
else {
my $uid = $self->current_user->{id}; my $uid = $self->current_user->{id};
my $err = $self->app->action_query->errstr;
$self->app->log->error( $self->app->log->error(
"Checkout($uid, $action_id): INSERT failed: $err"); "Checkout($uid, $action_id): INSERT failed: $@");
return 'INSERT failed: ' . $err; return 'INSERT failed: ' . $@;
} }
$self->invalidate_stats_cache;
return;
} }
} }
); );
@ -949,45 +893,75 @@ qq{select * from pending_mails where email = ? and num_tries > 1;}
$self->helper( $self->helper(
'update_journey_part' => sub { 'update_journey_part' => sub {
my ( $self, $checkin_id, $checkout_id, $key, $value ) = @_; my ( $self, $checkin_id, $checkout_id, $key, $value ) = @_;
my ( $query, $id, $action_type ); my $rows;
if ( $key eq 'sched_departure' ) { eval {
$query = $self->app->action_set_sched_time_query; my $db = $self->pg->db;
$id = $checkin_id; if ( $key eq 'sched_departure' ) {
$action_type = $self->app->action_type->{checkin}; $rows = $db->update(
} 'user_actions',
elsif ( $key eq 'rt_departure' ) { {
$query = $self->app->action_set_real_time_query; sched_time => $value,
$id = $checkin_id; },
$action_type = $self->app->action_type->{checkin}; {
} id => $checkin_id,
elsif ( $key eq 'sched_arrival' ) { action_id => $self->app->action_type->{checkin},
$query = $self->app->action_set_sched_time_query; }
$id = $checkout_id; )->rows;
$action_type = $self->app->action_type->{checkout};
}
elsif ( $key eq 'rt_arrival' ) {
$query = $self->app->action_set_real_time_query;
$id = $checkout_id;
$action_type = $self->app->action_type->{checkout};
}
else {
$self->app->log->error(
"update_journey_part(id = $id): Invalid key $key");
return 'Internal Error';
}
my $success = $query->execute( $value, $id, $action_type );
if ($success) {
if ( $query->rows == 1 ) {
return undef;
} }
return 'UPDATE failed: did not match any journey part'; elsif ( $key eq 'rt_departure' ) {
$rows = $db->update(
'user_actions',
{
real_time => $value,
},
{
id => $checkin_id,
action_id => $self->app->action_type->{checkin},
}
)->rows;
}
elsif ( $key eq 'sched_arrival' ) {
$rows = $db->update(
'user_actions',
{
sched_time => $value,
},
{
id => $checkout_id,
action_id => $self->app->action_type->{checkout},
}
)->rows;
}
elsif ( $key eq 'rt_arrival' ) {
$rows = $db->update(
'user_actions',
{
real_time => $value,
},
{
id => $checkout_id,
action_id => $self->app->action_type->{checkout},
}
)->rows;
}
else {
$self->app->log->error(
"update_journey_part($checkin_id, $checkout_id): Invalid key $key"
);
}
};
if ($@) {
$self->app->log->error(
"update_journey_part($checkin_id, $checkout_id): UPDATE failed: $@"
);
return 'UPDATE failed: ' . $@;
} }
my $err = $query->errstr; if ( $rows == 1 ) {
$self->app->log->error( return undef;
"update_journey_part($id): UPDATE failed: $err"); }
return 'UPDATE failed: ' . $err; return 'UPDATE failed: did not match any journey part';
} }
); );
@ -1221,22 +1195,30 @@ qq{select * from pending_mails where email = ? and num_tries > 1;}
{ {
return 'Invalid journey data'; return 'Invalid journey data';
} }
my $query = $self->app->drop_journey_query;
my $success = $query->execute( $uid, $checkin_id, $checkout_id ); my $rows;
if ($success) { eval {
if ( $query->rows == 2 ) { $rows = $self->pg->db->delete(
$self->invalidate_stats_cache( $journey->{checkout} ); 'user_actions',
return undef; {
} user_id => $uid,
else { id => [ $checkin_id, $checkout_id ]
return }
sprintf( 'Deleted %d rows, expected 2', $query->rows ); )->rows;
} };
if ($@) {
$self->app->log->error(
"Delete($uid, $checkin_id, $checkout_id): DELETE failed: $@"
);
return 'DELETE failed: ' . $@;
} }
my $err = $self->app->drop_journey_query->errstr;
$self->app->log->error( if ( $rows == 2 ) {
"Delete($uid, $checkin_id, $checkout_id): DELETE failed: $err"); $self->invalidate_stats_cache( $journey->{checkout} );
return 'DELETE failed: ' . $err; return undef;
}
return sprintf( 'Deleted %d rows, expected 2', $rows );
} }
); );

View file

@ -470,7 +470,7 @@ sub edit_journey {
$error = $self->update_journey_part( $error = $self->update_journey_part(
$journey->{ids}[0], $journey->{ids}[0],
$journey->{ids}[1], $journey->{ids}[1],
$key, $datetime->epoch $key, $datetime
); );
if ($error) { if ($error) {
last; last;