Loading README.md +2 −0 Original line number Diff line number Diff line Loading @@ -13,6 +13,8 @@ Dependencies * perl >= 5.10 * Cache::File (part of the Cache module) * Crypt::Eksblowfish * DateTime * DateTime::Format::Strptime * DBI * DBD::Pg * Email::Sender Loading lib/Travelynx.pm +125 −5 Original line number Diff line number Diff line Loading @@ -266,6 +266,37 @@ 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 = 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 = 1 where id = ? and action_id = ? } ); } ); $self->attr( action_query => sub { my ($self) = @_; Loading Loading @@ -312,7 +343,7 @@ sub startup { stations.ds100, stations.name, train_type, train_line, train_no, train_id, extract(epoch from sched_time), extract(epoch from real_time), route, messages route, messages, edited from user_actions left outer join stations on station_id = stations.id where user_id = ? Loading @@ -331,7 +362,7 @@ sub startup { stations.ds100, stations.name, train_type, train_line, train_no, train_id, extract(epoch from sched_time), extract(epoch from real_time), route, messages route, messages, edited from user_actions left outer join stations on station_id = stations.id where user_id = ? Loading @@ -354,7 +385,7 @@ sub startup { stations.ds100, stations.name, train_type, train_line, train_no, train_id, extract(epoch from sched_time), extract(epoch from real_time), route, messages route, messages, edited from user_actions left outer join stations on station_id = stations.id where user_id = ? Loading @@ -375,7 +406,7 @@ sub startup { stations.ds100, stations.name, train_type, train_line, train_no, train_id, extract(epoch from sched_time), extract(epoch from real_time), route, messages route, messages, edited from user_actions left outer join stations on station_id = stations.id where user_id = ? Loading Loading @@ -808,6 +839,77 @@ qq{select * from pending_mails where email = ? and num_tries > 1;} } ); $self->helper( 'update_journey_part' => sub { my ( $self, $checkin_id, $checkout_id, $key, $value ) = @_; my ( $query, $id, $action_type ); if ( $key eq 'sched_departure' ) { $query = $self->app->action_set_sched_time_query; $id = $checkin_id; $action_type = $self->app->action_type->{checkin}; } elsif ( $key eq 'rt_departure' ) { $query = $self->app->action_set_real_time_query; $id = $checkin_id; $action_type = $self->app->action_type->{checkin}; } elsif ( $key eq 'sched_arrival' ) { $query = $self->app->action_set_sched_time_query; $id = $checkout_id; $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'; } my $err = $query->errstr; $self->app->log->error( "update_journey_part($id): UPDATE failed: $err"); return 'UPDATE failed: ' . $err; } ); $self->helper( 'journey_sanity_check' => sub { my ( $self, $journey ) = @_; if ( $journey->{sched_duration} and $journey->{sched_duration} < 0 ) { return 'Die geplante Dauer dieser Zugfahrt ist negativ'; } if ( $journey->{rt_duration} and $journey->{rt_duration} < 0 ) { return 'Die Dauer dieser Zugfahrt ist negativ'; } 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'; } return undef; } ); $self->helper( 'get_station_id' => sub { my ( $self, %opt ) = @_; Loading Loading @@ -1161,7 +1263,7 @@ qq{select * from pending_mails where email = ? and num_tries > 1;} $ds100, $name, $train_type, $train_line, $train_no, $train_id, $raw_sched_ts, $raw_real_ts, $raw_route, $raw_messages $raw_messages, $edited ) = @row; if ( $action == $match_actions[0] Loading @@ -1185,6 +1287,7 @@ qq{select * from pending_mails where email = ? and num_tries > 1;} ? [ split( qr{[|]}, $raw_route ) ] : undef, completed => 0, edited => $edited // 0, } ); } Loading @@ -1208,6 +1311,7 @@ qq{select * from pending_mails where email = ? and num_tries > 1;} $ref->{no} //= $train_no; $ref->{messages} //= [ split( qr{[|]}, $raw_messages ) ]; $ref->{route} //= [ split( qr{[|]}, $raw_route ) ]; $ref->{edited} += $edited; if ( $opt{verbose} ) { my @parsed_messages; Loading Loading @@ -1272,6 +1376,22 @@ qq{select * from pending_mails where email = ? and num_tries > 1;} } ); $self->helper( 'get_journey' => sub { my ( $self, %opt ) = @_; my @journeys = $self->get_user_travels(%opt); if ( @journeys == 0 or not $journeys[0]{completed} or $journeys[0]{ids}[1] != $opt{checkout_id} ) { return undef; } return $journeys[0]; } ); $self->helper( 'get_user_status' => sub { my ( $self, $uid ) = @_; Loading lib/Travelynx/Controller/Traveling.pm +59 −8 Original line number Diff line number Diff line Loading @@ -2,6 +2,7 @@ package Travelynx::Controller::Traveling; use Mojo::Base 'Mojolicious::Controller'; use DateTime; use DateTime::Format::Strptime; use Travel::Status::DE::IRIS::Stations; sub homepage { Loading Loading @@ -427,14 +428,12 @@ sub edit_journey { return; } my @journeys = $self->get_user_travels( my $journey = $self->get_journey( uid => $uid, checkout_id => $checkout_id, checkout_id => $checkout_id ); if ( @journeys == 0 or not $journeys[0]{completed} or $journeys[0]{ids}[1] != $checkout_id ) { if ( not $journey ) { $self->render( 'edit_journey', error => 'notfound', Loading @@ -443,7 +442,59 @@ sub edit_journey { return; } my $journey = $journeys[0]; my $error = undef; if ( $self->param('action') and $self->param('action') eq 'cancel' ) { $self->redirect_to("/journey/${uid}-${checkout_id}"); return; } if ( $self->param('action') and $self->param('action') eq 'save' ) { my $parser = DateTime::Format::Strptime->new( pattern => '%d.%m.%Y %H:%M', locale => 'de_DE', time_zone => 'Europe/Berlin' ); $self->app->dbh->begin_work; for my $key (qw(sched_departure rt_departure sched_arrival rt_arrival)) { my $datetime = $parser->parse_datetime( $self->param($key) ); if ( $datetime and $datetime->epoch ne $journey->{$key}->epoch ) { $error = $self->update_journey_part( $journey->{ids}[0], $journey->{ids}[1], $key, $datetime->epoch ); if ($error) { last; } } } if ($error) { $self->app->dbh->rollback; } else { $journey = $self->get_journey( uid => $uid, checkout_id => $checkout_id, verbose => 1 ); $error = $self->journey_sanity_check($journey); if ($error) { $self->app->dbh->rollback; } else { $self->invalidate_stats_cache( $journey->{checkout} ); $self->app->dbh->commit; $self->redirect_to("/journey/${uid}-${checkout_id}"); return; } } } for my $key (qw(sched_departure rt_departure sched_arrival rt_arrival)) { if ( $journey->{$key} and $journey->{$key}->epoch ) { Loading @@ -458,7 +509,7 @@ sub edit_journey { $self->render( 'edit_journey', error => undef, error => $error, journey => $journey ); } Loading templates/edit_journey.html.ep +10 −9 Original line number Diff line number Diff line <h1>Zugfahrt bearbeiten</h1> % if ($error and $error eq 'notfound') { <div class="row"> <div class="col s12"> Loading Loading @@ -25,6 +26,7 @@ % } %= form_for '/journey/edit' => (method => 'POST') => begin %= csrf_field %= hidden_field 'journey_id' => param('journey_id') <div class="row"> <div class="col s12"> <p> Loading @@ -35,6 +37,10 @@ am <b><%= $journey->{sched_departure}->strftime('%d.%m.%Y') %></b> </p> <p> Nach einer Änderung können die ursprünglich eingetragenen Zeiten nicht mehr wiederhergestellt werden. </p> <table class="striped"> <tr> <th scope="row">Zug</th> Loading Loading @@ -69,17 +75,14 @@ %= text_field 'rt_arrival', id => 'real_arrival', class => 'validate', pattern => '[0-9][0-9]?[.][0-9][0-9]?[.][0-9][0-9][0-9][0-9] +[0-9][0-9]:[0-9][0-9]' </td> </tr> <tr> <th scope="row">Route</th> <td> %= text_area 'route', id => 'route', cols => 40, rows => 20 </td> </tr> </table> </div> </div> <div class="row"> <div class="col s3 m3 l3"> <div class="col s6 m6 l6 center-align"> <button class="btn waves-effect waves-light" type="submit" name="action" value="cancel"> Abbrechen </button> </div> <div class="col s6 m6 l6 center-align"> <button class="btn waves-effect waves-light" type="submit" name="action" value="save"> Loading @@ -87,8 +90,6 @@ <i class="material-icons right">send</i> </button> </div> <div class="col s3 m3 l3"> </div> </div> %= end % } templates/journey.html.ep +4 −1 Original line number Diff line number Diff line Loading @@ -26,6 +26,9 @@ am <b><%= $journey->{sched_departure}->strftime('%d.%m.%Y') %></b> </p> % if ($journey->{edited}) { <p>Die Ankunfts- und Abfahrtsdaten wurden nachträglich bearbeitet.</p> % } <table class="striped"> <tr> <th scope="row">Zug</th> Loading Loading @@ -141,7 +144,7 @@ <div class="col s6 m6 l6 center-align"> %= form_for '/journey/edit' => (method => 'POST') => begin %= hidden_field 'journey_id' => param('journey_id') <button class="btn waves-effect waves-light" type="submit" name="action" value="edit" disabled="disabled"> <button class="btn waves-effect waves-light" type="submit" name="action" value="edit"> Bearbeiten </button> %= end Loading Loading
README.md +2 −0 Original line number Diff line number Diff line Loading @@ -13,6 +13,8 @@ Dependencies * perl >= 5.10 * Cache::File (part of the Cache module) * Crypt::Eksblowfish * DateTime * DateTime::Format::Strptime * DBI * DBD::Pg * Email::Sender Loading
lib/Travelynx.pm +125 −5 Original line number Diff line number Diff line Loading @@ -266,6 +266,37 @@ 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 = 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 = 1 where id = ? and action_id = ? } ); } ); $self->attr( action_query => sub { my ($self) = @_; Loading Loading @@ -312,7 +343,7 @@ sub startup { stations.ds100, stations.name, train_type, train_line, train_no, train_id, extract(epoch from sched_time), extract(epoch from real_time), route, messages route, messages, edited from user_actions left outer join stations on station_id = stations.id where user_id = ? Loading @@ -331,7 +362,7 @@ sub startup { stations.ds100, stations.name, train_type, train_line, train_no, train_id, extract(epoch from sched_time), extract(epoch from real_time), route, messages route, messages, edited from user_actions left outer join stations on station_id = stations.id where user_id = ? Loading @@ -354,7 +385,7 @@ sub startup { stations.ds100, stations.name, train_type, train_line, train_no, train_id, extract(epoch from sched_time), extract(epoch from real_time), route, messages route, messages, edited from user_actions left outer join stations on station_id = stations.id where user_id = ? Loading @@ -375,7 +406,7 @@ sub startup { stations.ds100, stations.name, train_type, train_line, train_no, train_id, extract(epoch from sched_time), extract(epoch from real_time), route, messages route, messages, edited from user_actions left outer join stations on station_id = stations.id where user_id = ? Loading Loading @@ -808,6 +839,77 @@ qq{select * from pending_mails where email = ? and num_tries > 1;} } ); $self->helper( 'update_journey_part' => sub { my ( $self, $checkin_id, $checkout_id, $key, $value ) = @_; my ( $query, $id, $action_type ); if ( $key eq 'sched_departure' ) { $query = $self->app->action_set_sched_time_query; $id = $checkin_id; $action_type = $self->app->action_type->{checkin}; } elsif ( $key eq 'rt_departure' ) { $query = $self->app->action_set_real_time_query; $id = $checkin_id; $action_type = $self->app->action_type->{checkin}; } elsif ( $key eq 'sched_arrival' ) { $query = $self->app->action_set_sched_time_query; $id = $checkout_id; $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'; } my $err = $query->errstr; $self->app->log->error( "update_journey_part($id): UPDATE failed: $err"); return 'UPDATE failed: ' . $err; } ); $self->helper( 'journey_sanity_check' => sub { my ( $self, $journey ) = @_; if ( $journey->{sched_duration} and $journey->{sched_duration} < 0 ) { return 'Die geplante Dauer dieser Zugfahrt ist negativ'; } if ( $journey->{rt_duration} and $journey->{rt_duration} < 0 ) { return 'Die Dauer dieser Zugfahrt ist negativ'; } 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'; } return undef; } ); $self->helper( 'get_station_id' => sub { my ( $self, %opt ) = @_; Loading Loading @@ -1161,7 +1263,7 @@ qq{select * from pending_mails where email = ? and num_tries > 1;} $ds100, $name, $train_type, $train_line, $train_no, $train_id, $raw_sched_ts, $raw_real_ts, $raw_route, $raw_messages $raw_messages, $edited ) = @row; if ( $action == $match_actions[0] Loading @@ -1185,6 +1287,7 @@ qq{select * from pending_mails where email = ? and num_tries > 1;} ? [ split( qr{[|]}, $raw_route ) ] : undef, completed => 0, edited => $edited // 0, } ); } Loading @@ -1208,6 +1311,7 @@ qq{select * from pending_mails where email = ? and num_tries > 1;} $ref->{no} //= $train_no; $ref->{messages} //= [ split( qr{[|]}, $raw_messages ) ]; $ref->{route} //= [ split( qr{[|]}, $raw_route ) ]; $ref->{edited} += $edited; if ( $opt{verbose} ) { my @parsed_messages; Loading Loading @@ -1272,6 +1376,22 @@ qq{select * from pending_mails where email = ? and num_tries > 1;} } ); $self->helper( 'get_journey' => sub { my ( $self, %opt ) = @_; my @journeys = $self->get_user_travels(%opt); if ( @journeys == 0 or not $journeys[0]{completed} or $journeys[0]{ids}[1] != $opt{checkout_id} ) { return undef; } return $journeys[0]; } ); $self->helper( 'get_user_status' => sub { my ( $self, $uid ) = @_; Loading
lib/Travelynx/Controller/Traveling.pm +59 −8 Original line number Diff line number Diff line Loading @@ -2,6 +2,7 @@ package Travelynx::Controller::Traveling; use Mojo::Base 'Mojolicious::Controller'; use DateTime; use DateTime::Format::Strptime; use Travel::Status::DE::IRIS::Stations; sub homepage { Loading Loading @@ -427,14 +428,12 @@ sub edit_journey { return; } my @journeys = $self->get_user_travels( my $journey = $self->get_journey( uid => $uid, checkout_id => $checkout_id, checkout_id => $checkout_id ); if ( @journeys == 0 or not $journeys[0]{completed} or $journeys[0]{ids}[1] != $checkout_id ) { if ( not $journey ) { $self->render( 'edit_journey', error => 'notfound', Loading @@ -443,7 +442,59 @@ sub edit_journey { return; } my $journey = $journeys[0]; my $error = undef; if ( $self->param('action') and $self->param('action') eq 'cancel' ) { $self->redirect_to("/journey/${uid}-${checkout_id}"); return; } if ( $self->param('action') and $self->param('action') eq 'save' ) { my $parser = DateTime::Format::Strptime->new( pattern => '%d.%m.%Y %H:%M', locale => 'de_DE', time_zone => 'Europe/Berlin' ); $self->app->dbh->begin_work; for my $key (qw(sched_departure rt_departure sched_arrival rt_arrival)) { my $datetime = $parser->parse_datetime( $self->param($key) ); if ( $datetime and $datetime->epoch ne $journey->{$key}->epoch ) { $error = $self->update_journey_part( $journey->{ids}[0], $journey->{ids}[1], $key, $datetime->epoch ); if ($error) { last; } } } if ($error) { $self->app->dbh->rollback; } else { $journey = $self->get_journey( uid => $uid, checkout_id => $checkout_id, verbose => 1 ); $error = $self->journey_sanity_check($journey); if ($error) { $self->app->dbh->rollback; } else { $self->invalidate_stats_cache( $journey->{checkout} ); $self->app->dbh->commit; $self->redirect_to("/journey/${uid}-${checkout_id}"); return; } } } for my $key (qw(sched_departure rt_departure sched_arrival rt_arrival)) { if ( $journey->{$key} and $journey->{$key}->epoch ) { Loading @@ -458,7 +509,7 @@ sub edit_journey { $self->render( 'edit_journey', error => undef, error => $error, journey => $journey ); } Loading
templates/edit_journey.html.ep +10 −9 Original line number Diff line number Diff line <h1>Zugfahrt bearbeiten</h1> % if ($error and $error eq 'notfound') { <div class="row"> <div class="col s12"> Loading Loading @@ -25,6 +26,7 @@ % } %= form_for '/journey/edit' => (method => 'POST') => begin %= csrf_field %= hidden_field 'journey_id' => param('journey_id') <div class="row"> <div class="col s12"> <p> Loading @@ -35,6 +37,10 @@ am <b><%= $journey->{sched_departure}->strftime('%d.%m.%Y') %></b> </p> <p> Nach einer Änderung können die ursprünglich eingetragenen Zeiten nicht mehr wiederhergestellt werden. </p> <table class="striped"> <tr> <th scope="row">Zug</th> Loading Loading @@ -69,17 +75,14 @@ %= text_field 'rt_arrival', id => 'real_arrival', class => 'validate', pattern => '[0-9][0-9]?[.][0-9][0-9]?[.][0-9][0-9][0-9][0-9] +[0-9][0-9]:[0-9][0-9]' </td> </tr> <tr> <th scope="row">Route</th> <td> %= text_area 'route', id => 'route', cols => 40, rows => 20 </td> </tr> </table> </div> </div> <div class="row"> <div class="col s3 m3 l3"> <div class="col s6 m6 l6 center-align"> <button class="btn waves-effect waves-light" type="submit" name="action" value="cancel"> Abbrechen </button> </div> <div class="col s6 m6 l6 center-align"> <button class="btn waves-effect waves-light" type="submit" name="action" value="save"> Loading @@ -87,8 +90,6 @@ <i class="material-icons right">send</i> </button> </div> <div class="col s3 m3 l3"> </div> </div> %= end % }
templates/journey.html.ep +4 −1 Original line number Diff line number Diff line Loading @@ -26,6 +26,9 @@ am <b><%= $journey->{sched_departure}->strftime('%d.%m.%Y') %></b> </p> % if ($journey->{edited}) { <p>Die Ankunfts- und Abfahrtsdaten wurden nachträglich bearbeitet.</p> % } <table class="striped"> <tr> <th scope="row">Zug</th> Loading Loading @@ -141,7 +144,7 @@ <div class="col s6 m6 l6 center-align"> %= form_for '/journey/edit' => (method => 'POST') => begin %= hidden_field 'journey_id' => param('journey_id') <button class="btn waves-effect waves-light" type="submit" name="action" value="edit" disabled="disabled"> <button class="btn waves-effect waves-light" type="submit" name="action" value="edit"> Bearbeiten </button> %= end Loading