Work-in-progress journey editor. Not ready for deployment.
This commit is contained in:
parent
beb17acb84
commit
c0c7b1a589
5 changed files with 200 additions and 23 deletions
|
@ -13,6 +13,8 @@ Dependencies
|
||||||
* perl >= 5.10
|
* perl >= 5.10
|
||||||
* Cache::File (part of the Cache module)
|
* Cache::File (part of the Cache module)
|
||||||
* Crypt::Eksblowfish
|
* Crypt::Eksblowfish
|
||||||
|
* DateTime
|
||||||
|
* DateTime::Format::Strptime
|
||||||
* DBI
|
* DBI
|
||||||
* DBD::Pg
|
* DBD::Pg
|
||||||
* Email::Sender
|
* Email::Sender
|
||||||
|
|
130
lib/Travelynx.pm
130
lib/Travelynx.pm
|
@ -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(
|
$self->attr(
|
||||||
action_query => sub {
|
action_query => sub {
|
||||||
my ($self) = @_;
|
my ($self) = @_;
|
||||||
|
@ -312,7 +343,7 @@ sub startup {
|
||||||
stations.ds100, stations.name,
|
stations.ds100, stations.name,
|
||||||
train_type, train_line, train_no, train_id,
|
train_type, train_line, train_no, train_id,
|
||||||
extract(epoch from sched_time), extract(epoch from real_time),
|
extract(epoch from sched_time), extract(epoch from real_time),
|
||||||
route, messages
|
route, messages, edited
|
||||||
from user_actions
|
from user_actions
|
||||||
left outer join stations on station_id = stations.id
|
left outer join stations on station_id = stations.id
|
||||||
where user_id = ?
|
where user_id = ?
|
||||||
|
@ -331,7 +362,7 @@ sub startup {
|
||||||
stations.ds100, stations.name,
|
stations.ds100, stations.name,
|
||||||
train_type, train_line, train_no, train_id,
|
train_type, train_line, train_no, train_id,
|
||||||
extract(epoch from sched_time), extract(epoch from real_time),
|
extract(epoch from sched_time), extract(epoch from real_time),
|
||||||
route, messages
|
route, messages, edited
|
||||||
from user_actions
|
from user_actions
|
||||||
left outer join stations on station_id = stations.id
|
left outer join stations on station_id = stations.id
|
||||||
where user_id = ?
|
where user_id = ?
|
||||||
|
@ -354,7 +385,7 @@ sub startup {
|
||||||
stations.ds100, stations.name,
|
stations.ds100, stations.name,
|
||||||
train_type, train_line, train_no, train_id,
|
train_type, train_line, train_no, train_id,
|
||||||
extract(epoch from sched_time), extract(epoch from real_time),
|
extract(epoch from sched_time), extract(epoch from real_time),
|
||||||
route, messages
|
route, messages, edited
|
||||||
from user_actions
|
from user_actions
|
||||||
left outer join stations on station_id = stations.id
|
left outer join stations on station_id = stations.id
|
||||||
where user_id = ?
|
where user_id = ?
|
||||||
|
@ -375,7 +406,7 @@ sub startup {
|
||||||
stations.ds100, stations.name,
|
stations.ds100, stations.name,
|
||||||
train_type, train_line, train_no, train_id,
|
train_type, train_line, train_no, train_id,
|
||||||
extract(epoch from sched_time), extract(epoch from real_time),
|
extract(epoch from sched_time), extract(epoch from real_time),
|
||||||
route, messages
|
route, messages, edited
|
||||||
from user_actions
|
from user_actions
|
||||||
left outer join stations on station_id = stations.id
|
left outer join stations on station_id = stations.id
|
||||||
where user_id = ?
|
where user_id = ?
|
||||||
|
@ -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(
|
$self->helper(
|
||||||
'get_station_id' => sub {
|
'get_station_id' => sub {
|
||||||
my ( $self, %opt ) = @_;
|
my ( $self, %opt ) = @_;
|
||||||
|
@ -1161,7 +1263,7 @@ qq{select * from pending_mails where email = ? and num_tries > 1;}
|
||||||
$ds100, $name, $train_type,
|
$ds100, $name, $train_type,
|
||||||
$train_line, $train_no, $train_id,
|
$train_line, $train_no, $train_id,
|
||||||
$raw_sched_ts, $raw_real_ts, $raw_route,
|
$raw_sched_ts, $raw_real_ts, $raw_route,
|
||||||
$raw_messages
|
$raw_messages, $edited
|
||||||
) = @row;
|
) = @row;
|
||||||
|
|
||||||
if ( $action == $match_actions[0]
|
if ( $action == $match_actions[0]
|
||||||
|
@ -1185,6 +1287,7 @@ qq{select * from pending_mails where email = ? and num_tries > 1;}
|
||||||
? [ split( qr{[|]}, $raw_route ) ]
|
? [ split( qr{[|]}, $raw_route ) ]
|
||||||
: undef,
|
: undef,
|
||||||
completed => 0,
|
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->{no} //= $train_no;
|
||||||
$ref->{messages} //= [ split( qr{[|]}, $raw_messages ) ];
|
$ref->{messages} //= [ split( qr{[|]}, $raw_messages ) ];
|
||||||
$ref->{route} //= [ split( qr{[|]}, $raw_route ) ];
|
$ref->{route} //= [ split( qr{[|]}, $raw_route ) ];
|
||||||
|
$ref->{edited} += $edited;
|
||||||
|
|
||||||
if ( $opt{verbose} ) {
|
if ( $opt{verbose} ) {
|
||||||
my @parsed_messages;
|
my @parsed_messages;
|
||||||
|
@ -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(
|
$self->helper(
|
||||||
'get_user_status' => sub {
|
'get_user_status' => sub {
|
||||||
my ( $self, $uid ) = @_;
|
my ( $self, $uid ) = @_;
|
||||||
|
|
|
@ -2,6 +2,7 @@ package Travelynx::Controller::Traveling;
|
||||||
use Mojo::Base 'Mojolicious::Controller';
|
use Mojo::Base 'Mojolicious::Controller';
|
||||||
|
|
||||||
use DateTime;
|
use DateTime;
|
||||||
|
use DateTime::Format::Strptime;
|
||||||
use Travel::Status::DE::IRIS::Stations;
|
use Travel::Status::DE::IRIS::Stations;
|
||||||
|
|
||||||
sub homepage {
|
sub homepage {
|
||||||
|
@ -427,14 +428,12 @@ sub edit_journey {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
my @journeys = $self->get_user_travels(
|
my $journey = $self->get_journey(
|
||||||
uid => $uid,
|
uid => $uid,
|
||||||
checkout_id => $checkout_id,
|
checkout_id => $checkout_id
|
||||||
);
|
);
|
||||||
if ( @journeys == 0
|
|
||||||
or not $journeys[0]{completed}
|
if ( not $journey ) {
|
||||||
or $journeys[0]{ids}[1] != $checkout_id )
|
|
||||||
{
|
|
||||||
$self->render(
|
$self->render(
|
||||||
'edit_journey',
|
'edit_journey',
|
||||||
error => 'notfound',
|
error => 'notfound',
|
||||||
|
@ -443,7 +442,59 @@ sub edit_journey {
|
||||||
return;
|
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)) {
|
for my $key (qw(sched_departure rt_departure sched_arrival rt_arrival)) {
|
||||||
if ( $journey->{$key} and $journey->{$key}->epoch ) {
|
if ( $journey->{$key} and $journey->{$key}->epoch ) {
|
||||||
|
@ -458,7 +509,7 @@ sub edit_journey {
|
||||||
|
|
||||||
$self->render(
|
$self->render(
|
||||||
'edit_journey',
|
'edit_journey',
|
||||||
error => undef,
|
error => $error,
|
||||||
journey => $journey
|
journey => $journey
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
<h1>Zugfahrt bearbeiten</h1>
|
||||||
% if ($error and $error eq 'notfound') {
|
% if ($error and $error eq 'notfound') {
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col s12">
|
<div class="col s12">
|
||||||
|
@ -25,6 +26,7 @@
|
||||||
% }
|
% }
|
||||||
%= form_for '/journey/edit' => (method => 'POST') => begin
|
%= form_for '/journey/edit' => (method => 'POST') => begin
|
||||||
%= csrf_field
|
%= csrf_field
|
||||||
|
%= hidden_field 'journey_id' => param('journey_id')
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col s12">
|
<div class="col s12">
|
||||||
<p>
|
<p>
|
||||||
|
@ -35,6 +37,10 @@
|
||||||
am
|
am
|
||||||
<b><%= $journey->{sched_departure}->strftime('%d.%m.%Y') %></b>
|
<b><%= $journey->{sched_departure}->strftime('%d.%m.%Y') %></b>
|
||||||
</p>
|
</p>
|
||||||
|
<p>
|
||||||
|
Nach einer Änderung können die ursprünglich eingetragenen
|
||||||
|
Zeiten nicht mehr wiederhergestellt werden.
|
||||||
|
</p>
|
||||||
<table class="striped">
|
<table class="striped">
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">Zug</th>
|
<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]'
|
%= 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>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
|
||||||
<th scope="row">Route</th>
|
|
||||||
<td>
|
|
||||||
%= text_area 'route', id => 'route', cols => 40, rows => 20
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<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>
|
||||||
<div class="col s6 m6 l6 center-align">
|
<div class="col s6 m6 l6 center-align">
|
||||||
<button class="btn waves-effect waves-light" type="submit" name="action" value="save">
|
<button class="btn waves-effect waves-light" type="submit" name="action" value="save">
|
||||||
|
@ -87,8 +90,6 @@
|
||||||
<i class="material-icons right">send</i>
|
<i class="material-icons right">send</i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="col s3 m3 l3">
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
%= end
|
%= end
|
||||||
% }
|
% }
|
||||||
|
|
|
@ -26,6 +26,9 @@
|
||||||
am
|
am
|
||||||
<b><%= $journey->{sched_departure}->strftime('%d.%m.%Y') %></b>
|
<b><%= $journey->{sched_departure}->strftime('%d.%m.%Y') %></b>
|
||||||
</p>
|
</p>
|
||||||
|
% if ($journey->{edited}) {
|
||||||
|
<p>Die Ankunfts- und Abfahrtsdaten wurden nachträglich bearbeitet.</p>
|
||||||
|
% }
|
||||||
<table class="striped">
|
<table class="striped">
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">Zug</th>
|
<th scope="row">Zug</th>
|
||||||
|
@ -141,7 +144,7 @@
|
||||||
<div class="col s6 m6 l6 center-align">
|
<div class="col s6 m6 l6 center-align">
|
||||||
%= form_for '/journey/edit' => (method => 'POST') => begin
|
%= form_for '/journey/edit' => (method => 'POST') => begin
|
||||||
%= hidden_field 'journey_id' => param('journey_id')
|
%= 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
|
Bearbeiten
|
||||||
</button>
|
</button>
|
||||||
%= end
|
%= end
|
||||||
|
|
Loading…
Reference in a new issue