summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md2
-rwxr-xr-xlib/Travelynx.pm130
-rwxr-xr-xlib/Travelynx/Controller/Traveling.pm67
-rw-r--r--templates/edit_journey.html.ep19
-rw-r--r--templates/journey.html.ep5
5 files changed, 200 insertions, 23 deletions
diff --git a/README.md b/README.md
index 281f56b..317ced8 100644
--- a/README.md
+++ b/README.md
@@ -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
diff --git a/lib/Travelynx.pm b/lib/Travelynx.pm
index 7c1c1df..62d70c7 100755
--- a/lib/Travelynx.pm
+++ b/lib/Travelynx.pm
@@ -267,6 +267,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) = @_;
@@ -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 = ?
@@ -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 = ?
@@ -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 = ?
@@ -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 = ?
@@ -809,6 +840,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 ) = @_;
@@ -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]
@@ -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,
}
);
}
@@ -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;
@@ -1273,6 +1377,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 ) = @_;
diff --git a/lib/Travelynx/Controller/Traveling.pm b/lib/Travelynx/Controller/Traveling.pm
index 83036ba..ce7d1d4 100755
--- a/lib/Travelynx/Controller/Traveling.pm
+++ b/lib/Travelynx/Controller/Traveling.pm
@@ -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 {
@@ -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',
@@ -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 ) {
@@ -458,7 +509,7 @@ sub edit_journey {
$self->render(
'edit_journey',
- error => undef,
+ error => $error,
journey => $journey
);
}
diff --git a/templates/edit_journey.html.ep b/templates/edit_journey.html.ep
index 8fdc5b6..d36fc65 100644
--- a/templates/edit_journey.html.ep
+++ b/templates/edit_journey.html.ep
@@ -1,3 +1,4 @@
+<h1>Zugfahrt bearbeiten</h1>
% if ($error and $error eq 'notfound') {
<div class="row">
<div class="col s12">
@@ -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>
@@ -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>
@@ -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">
@@ -87,8 +90,6 @@
<i class="material-icons right">send</i>
</button>
</div>
- <div class="col s3 m3 l3">
- </div>
</div>
%= end
% }
diff --git a/templates/journey.html.ep b/templates/journey.html.ep
index 3b84d67..b5c24a7 100644
--- a/templates/journey.html.ep
+++ b/templates/journey.html.ep
@@ -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>
@@ -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