Use one row per journey instead of split checkin/checkout entries
Whether a user is in transit or not is now determined by an entry in the in_transit table instead of a dangling checkin. All completed journeys are stored in the "journeys" table. This does most of the work needed for automatic checkout. However, note that the corresponding worker process is not implemented yet.
This commit is contained in:
parent
812be4f0cb
commit
e168d9cd39
15 changed files with 782 additions and 588 deletions
801
lib/Travelynx.pm
801
lib/Travelynx.pm
File diff suppressed because it is too large
Load diff
|
@ -12,9 +12,11 @@ sub get_schema_version {
|
|||
my $version;
|
||||
|
||||
eval {
|
||||
$version = $db->select( 'schema_version', ['version'] )->hash->{version};
|
||||
$version
|
||||
= $db->select( 'schema_version', ['version'] )->hash->{version};
|
||||
};
|
||||
if ($@) {
|
||||
|
||||
# If it failed, the version table does not exist -> run setup first.
|
||||
return undef;
|
||||
}
|
||||
|
@ -124,6 +126,210 @@ my @migrations = (
|
|||
}
|
||||
);
|
||||
},
|
||||
|
||||
# v3 -> v4
|
||||
# Introduces "journeys", containing one row for each complete
|
||||
# journey, and "in_transit", containing the journey which is currently
|
||||
# in progress (if any). "user_actions" is no longer used, but still kept
|
||||
# as a backup for now.
|
||||
sub {
|
||||
my ($db) = @_;
|
||||
|
||||
$db->query(
|
||||
qq{
|
||||
create table journeys (
|
||||
id serial not null primary key,
|
||||
user_id integer not null references users (id),
|
||||
train_type varchar(16) not null,
|
||||
train_line varchar(16),
|
||||
train_no varchar(16) not null,
|
||||
train_id varchar(128) not null,
|
||||
checkin_station_id integer not null references stations (id),
|
||||
checkin_time timestamptz not null,
|
||||
sched_departure timestamptz not null,
|
||||
real_departure timestamptz not null,
|
||||
checkout_station_id integer not null references stations (id),
|
||||
checkout_time timestamptz not null,
|
||||
sched_arrival timestamptz,
|
||||
real_arrival timestamptz,
|
||||
cancelled boolean not null,
|
||||
edited smallint not null,
|
||||
route text,
|
||||
messages text
|
||||
);
|
||||
create table in_transit (
|
||||
user_id integer not null references users (id) primary key,
|
||||
train_type varchar(16) not null,
|
||||
train_line varchar(16),
|
||||
train_no varchar(16) not null,
|
||||
train_id varchar(128) not null,
|
||||
checkin_station_id integer not null references stations (id),
|
||||
checkin_time timestamptz not null,
|
||||
sched_departure timestamptz not null,
|
||||
real_departure timestamptz not null,
|
||||
checkout_station_id int references stations (id),
|
||||
checkout_time timestamptz,
|
||||
sched_arrival timestamptz,
|
||||
real_arrival timestamptz,
|
||||
cancelled boolean not null,
|
||||
route text,
|
||||
messages text
|
||||
);
|
||||
create view journeys_str as select
|
||||
journeys.id as journey_id, user_id,
|
||||
train_type, train_line, train_no, train_id,
|
||||
extract(epoch from checkin_time) as checkin_ts,
|
||||
extract(epoch from sched_departure) as sched_dep_ts,
|
||||
extract(epoch from real_departure) as real_dep_ts,
|
||||
dep_stations.ds100 as dep_ds100,
|
||||
dep_stations.name as dep_name,
|
||||
extract(epoch from checkout_time) as checkout_ts,
|
||||
extract(epoch from sched_arrival) as sched_arr_ts,
|
||||
extract(epoch from real_arrival) as real_arr_ts,
|
||||
arr_stations.ds100 as arr_ds100,
|
||||
arr_stations.name as arr_name,
|
||||
cancelled, edited, route, messages
|
||||
from journeys
|
||||
join stations as dep_stations on dep_stations.id = checkin_station_id
|
||||
join stations as arr_stations on arr_stations.id = checkout_station_id
|
||||
;
|
||||
create view in_transit_str as select
|
||||
user_id,
|
||||
train_type, train_line, train_no, train_id,
|
||||
extract(epoch from checkin_time) as checkin_ts,
|
||||
extract(epoch from sched_departure) as sched_dep_ts,
|
||||
extract(epoch from real_departure) as real_dep_ts,
|
||||
dep_stations.ds100 as dep_ds100,
|
||||
dep_stations.name as dep_name,
|
||||
extract(epoch from checkout_time) as checkout_ts,
|
||||
extract(epoch from sched_arrival) as sched_arr_ts,
|
||||
extract(epoch from real_arrival) as real_arr_ts,
|
||||
arr_stations.ds100 as arr_ds100,
|
||||
arr_stations.name as arr_name,
|
||||
cancelled, route, messages
|
||||
from in_transit
|
||||
join stations as dep_stations on dep_stations.id = checkin_station_id
|
||||
left join stations as arr_stations on arr_stations.id = checkout_station_id
|
||||
;
|
||||
}
|
||||
);
|
||||
|
||||
my @uids
|
||||
= $db->select( 'users', ['id'] )->hashes->map( sub { shift->{id} } )
|
||||
->each;
|
||||
my $count = 0;
|
||||
|
||||
for my $uid (@uids) {
|
||||
my %cache;
|
||||
my $prev_action_type = 0;
|
||||
my $actions = $db->select(
|
||||
'user_actions', '*',
|
||||
{ user_id => $uid },
|
||||
{ order_by => { -asc => 'id' } }
|
||||
);
|
||||
for my $action ( $actions->hashes->each ) {
|
||||
my $action_type = $action->{action_id};
|
||||
my $id = $action->{id};
|
||||
|
||||
if ( $action_type == 2 and $prev_action_type != 1 ) {
|
||||
die(
|
||||
"Inconsistent data at uid ${uid} action ${id}: Illegal transition $prev_action_type -> $action_type.\n"
|
||||
);
|
||||
}
|
||||
|
||||
if ( $action_type == 5 and $prev_action_type != 4 ) {
|
||||
die(
|
||||
"Inconsistent data at uid ${uid} action ${id}: Illegal transition $prev_action_type -> $action_type.\n"
|
||||
);
|
||||
}
|
||||
|
||||
if ( $action_type == 1 or $action_type == 4 ) {
|
||||
%cache = (
|
||||
train_type => $action->{train_type},
|
||||
train_line => $action->{train_line},
|
||||
train_no => $action->{train_no},
|
||||
train_id => $action->{train_id},
|
||||
checkin_station_id => $action->{station_id},
|
||||
checkin_time => $action->{action_time},
|
||||
sched_departure => $action->{sched_time},
|
||||
real_departure => $action->{real_time},
|
||||
route => $action->{route},
|
||||
messages => $action->{messages},
|
||||
cancelled => $action->{action_id} == 4 ? 1 : 0,
|
||||
edited => $action->{edited},
|
||||
);
|
||||
}
|
||||
elsif ( $action_type == 2 or $action_type == 5 ) {
|
||||
$cache{checkout_station_id} = $action->{station_id};
|
||||
$cache{checkout_time} = $action->{action_time};
|
||||
$cache{sched_arrival} = $action->{sched_time};
|
||||
$cache{real_arrival} = $action->{real_time};
|
||||
$cache{edited} |= $action->{edited} << 8;
|
||||
if ( $action->{route} ) {
|
||||
$cache{route} = $action->{route};
|
||||
}
|
||||
if ( $action->{messages} ) {
|
||||
$cache{messages} = $action->{messages};
|
||||
}
|
||||
|
||||
$db->insert(
|
||||
'journeys',
|
||||
{
|
||||
user_id => $uid,
|
||||
train_type => $cache{train_type},
|
||||
train_line => $cache{train_line},
|
||||
train_no => $cache{train_no},
|
||||
train_id => $cache{train_id},
|
||||
checkin_station_id => $cache{checkin_station_id},
|
||||
checkin_time => $cache{checkin_time},
|
||||
sched_departure => $cache{sched_departure},
|
||||
real_departure => $cache{real_departure},
|
||||
checkout_station_id => $cache{checkout_station_id},
|
||||
checkout_time => $cache{checkout_time},
|
||||
sched_arrival => $cache{sched_arrival},
|
||||
real_arrival => $cache{real_arrival},
|
||||
cancelled => $cache{cancelled},
|
||||
edited => $cache{edited},
|
||||
route => $cache{route},
|
||||
messages => $cache{messages}
|
||||
}
|
||||
);
|
||||
|
||||
%cache = ();
|
||||
|
||||
}
|
||||
|
||||
$prev_action_type = $action_type;
|
||||
}
|
||||
|
||||
if (%cache) {
|
||||
|
||||
# user is currently in transit
|
||||
$db->insert(
|
||||
'in_transit',
|
||||
{
|
||||
user_id => $uid,
|
||||
train_type => $cache{train_type},
|
||||
train_line => $cache{train_line},
|
||||
train_no => $cache{train_no},
|
||||
train_id => $cache{train_id},
|
||||
checkin_station_id => $cache{checkin_station_id},
|
||||
checkin_time => $cache{checkin_time},
|
||||
sched_departure => $cache{sched_departure},
|
||||
real_departure => $cache{real_departure},
|
||||
cancelled => $cache{cancelled},
|
||||
route => $cache{route},
|
||||
messages => $cache{messages}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
$count++;
|
||||
printf( " journey storage migration: %3.0f%% complete\n",
|
||||
$count * 100 / @uids );
|
||||
}
|
||||
$db->update( 'schema_version', { version => 4 } );
|
||||
},
|
||||
);
|
||||
|
||||
sub setup_db {
|
||||
|
|
|
@ -286,45 +286,18 @@ sub account {
|
|||
|
||||
sub json_export {
|
||||
my ($self) = @_;
|
||||
my $uid = $self->current_user->{id};
|
||||
my $query = $self->app->get_all_actions_query;
|
||||
my $uid = $self->current_user->{id};
|
||||
|
||||
$query->execute($uid);
|
||||
|
||||
my @entries;
|
||||
|
||||
while ( my @row = $query->fetchrow_array ) {
|
||||
my (
|
||||
$action_id, $action, $raw_ts, $ds100,
|
||||
$name, $train_type, $train_line, $train_no,
|
||||
$train_id, $raw_sched_ts, $raw_real_ts, $raw_route,
|
||||
$raw_messages
|
||||
) = @row;
|
||||
|
||||
push(
|
||||
@entries,
|
||||
{
|
||||
action => $self->app->action_types->[ $action - 1 ],
|
||||
action_ts => $raw_ts,
|
||||
station_ds100 => $ds100,
|
||||
station_name => $name,
|
||||
train_type => $train_type,
|
||||
train_line => $train_line,
|
||||
train_no => $train_no,
|
||||
train_id => $train_id,
|
||||
scheduled_ts => $raw_sched_ts,
|
||||
realtime_ts => $raw_real_ts,
|
||||
messages => $raw_messages
|
||||
? [ map { [ split(qr{:}) ] } split( qr{[|]}, $raw_messages ) ]
|
||||
: undef,
|
||||
route => $raw_route ? [ split( qr{[|]}, $raw_route ) ]
|
||||
: undef,
|
||||
}
|
||||
);
|
||||
}
|
||||
my $db = $self->pg->db;
|
||||
|
||||
$self->render(
|
||||
json => [@entries],
|
||||
json => {
|
||||
account => $db->select( 'users', '*', { id => $uid } )->hash,
|
||||
journeys => [
|
||||
$db->select( 'journeys', '*', { user_id => $uid } )
|
||||
->hashes->each
|
||||
],
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -48,10 +48,10 @@ sub get_v0 {
|
|||
my $station_lon = undef;
|
||||
my $station_lat = undef;
|
||||
|
||||
if ( $status->{station_ds100} ) {
|
||||
if ( $status->{arr_ds100} // $status->{dep_ds100} ) {
|
||||
@station_descriptions
|
||||
= Travel::Status::DE::IRIS::Stations::get_station(
|
||||
$status->{station_ds100} );
|
||||
$status->{arr_ds100} // $status->{dep_ds100} );
|
||||
}
|
||||
if ( @station_descriptions == 1 ) {
|
||||
( undef, undef, $station_eva, $station_lon, $station_lat )
|
||||
|
@ -59,14 +59,14 @@ sub get_v0 {
|
|||
}
|
||||
$self->render(
|
||||
json => {
|
||||
deprecated => \0,
|
||||
deprecated => \1,
|
||||
checked_in => (
|
||||
$status->{checked_in}
|
||||
or $status->{cancelled}
|
||||
) ? \1 : \0,
|
||||
station => {
|
||||
ds100 => $status->{station_ds100},
|
||||
name => $status->{station_name},
|
||||
ds100 => $status->{arr_ds100} // $status->{dep_ds100},
|
||||
name => $status->{arr_ds100} // $status->{dep_ds100},
|
||||
uic => $station_eva,
|
||||
longitude => $station_lon,
|
||||
latitude => $station_lat,
|
||||
|
@ -77,8 +77,10 @@ sub get_v0 {
|
|||
no => $status->{train_no},
|
||||
},
|
||||
actionTime => $status->{timestamp}->epoch,
|
||||
scheduledTime => $status->{sched_ts}->epoch,
|
||||
realTime => $status->{real_ts}->epoch,
|
||||
scheduledTime => $status->{sched_arrival}->epoch
|
||||
|| $status->{sched_departure}->epoch,
|
||||
realTime => $status->{real_arrival}->epoch
|
||||
|| $status->{real_departure}->epoch,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
@ -6,7 +6,8 @@ my $travelynx_version = qx{git describe --dirty} || 'experimental';
|
|||
sub about {
|
||||
my ($self) = @_;
|
||||
|
||||
$self->render( 'about', version => $self->app->config->{version} // 'UNKNOWN' );
|
||||
$self->render( 'about',
|
||||
version => $self->app->config->{version} // 'UNKNOWN' );
|
||||
}
|
||||
|
||||
sub imprint {
|
||||
|
|
|
@ -97,13 +97,16 @@ sub log_action {
|
|||
else {
|
||||
$self->render(
|
||||
json => {
|
||||
success => 1,
|
||||
success => 1,
|
||||
redirect_to => '/',
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
elsif ( $params->{action} eq 'checkout' ) {
|
||||
my $error = $self->checkout( $params->{station}, $params->{force} );
|
||||
my ( $still_checked_in, $error )
|
||||
= $self->checkout( $params->{station}, $params->{force} );
|
||||
my $station_link = '/s/' . $params->{station};
|
||||
|
||||
if ($error) {
|
||||
$self->render(
|
||||
|
@ -116,7 +119,8 @@ sub log_action {
|
|||
else {
|
||||
$self->render(
|
||||
json => {
|
||||
success => 1,
|
||||
success => 1,
|
||||
redirect_to => $still_checked_in ? '/' : $station_link,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -134,7 +138,8 @@ sub log_action {
|
|||
else {
|
||||
$self->render(
|
||||
json => {
|
||||
success => 1,
|
||||
success => 1,
|
||||
redirect_to => '/',
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -155,13 +160,15 @@ sub log_action {
|
|||
else {
|
||||
$self->render(
|
||||
json => {
|
||||
success => 1,
|
||||
success => 1,
|
||||
redirect_to => '/',
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
elsif ( $params->{action} eq 'cancelled_to' ) {
|
||||
my $error = $self->checkout( $params->{station}, 1,
|
||||
my ( undef, $error )
|
||||
= $self->checkout( $params->{station}, 1,
|
||||
$self->app->action_type->{cancelled_to} );
|
||||
|
||||
if ($error) {
|
||||
|
@ -175,14 +182,14 @@ sub log_action {
|
|||
else {
|
||||
$self->render(
|
||||
json => {
|
||||
success => 1,
|
||||
success => 1,
|
||||
redirect_to => '/',
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
elsif ( $params->{action} eq 'delete' ) {
|
||||
my ( $from, $to ) = split( qr{,}, $params->{ids} );
|
||||
my $error = $self->delete_journey( $from, $to, $params->{checkin},
|
||||
my $error = $self->delete_journey( $params->{id}, $params->{checkin},
|
||||
$params->{checkout} );
|
||||
if ($error) {
|
||||
$self->render(
|
||||
|
@ -195,7 +202,8 @@ sub log_action {
|
|||
else {
|
||||
$self->render(
|
||||
json => {
|
||||
success => 1,
|
||||
success => 1,
|
||||
redirect_to => '/history',
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -215,7 +223,7 @@ sub station {
|
|||
my $station = $self->stash('station');
|
||||
my $train = $self->param('train');
|
||||
|
||||
my $status = $self->get_departures($station);
|
||||
my $status = $self->get_departures( $station, 120, 30 );
|
||||
|
||||
if ( $status->{errstr} ) {
|
||||
$self->render(
|
||||
|
@ -382,11 +390,13 @@ sub monthly_history {
|
|||
|
||||
sub journey_details {
|
||||
my ($self) = @_;
|
||||
my ( $uid, $checkout_id ) = split( qr{-}, $self->stash('id') );
|
||||
my $journey_id = $self->stash('id');
|
||||
|
||||
$self->param( journey_id => $checkout_id );
|
||||
my $uid = $self->current_user->{id};
|
||||
|
||||
if ( not( $uid == $self->current_user->{id} and $checkout_id ) ) {
|
||||
$self->param( journey_id => $journey_id );
|
||||
|
||||
if ( not($journey_id) ) {
|
||||
$self->render(
|
||||
'journey',
|
||||
error => 'notfound',
|
||||
|
@ -395,36 +405,35 @@ sub journey_details {
|
|||
return;
|
||||
}
|
||||
|
||||
my @journeys = $self->get_user_travels(
|
||||
uid => $uid,
|
||||
checkout_id => $checkout_id,
|
||||
verbose => 1,
|
||||
my $journey = $self->get_journey(
|
||||
uid => $uid,
|
||||
journey_id => $journey_id,
|
||||
verbose => 1,
|
||||
);
|
||||
if ( @journeys == 0
|
||||
or not $journeys[0]{completed}
|
||||
or $journeys[0]{ids}[1] != $checkout_id )
|
||||
{
|
||||
|
||||
if ($journey) {
|
||||
$self->render(
|
||||
'journey',
|
||||
error => undef,
|
||||
journey => $journey,
|
||||
);
|
||||
}
|
||||
else {
|
||||
$self->render(
|
||||
'journey',
|
||||
error => 'notfound',
|
||||
journey => {}
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
$self->render(
|
||||
'journey',
|
||||
error => undef,
|
||||
journey => $journeys[0]
|
||||
);
|
||||
}
|
||||
|
||||
sub edit_journey {
|
||||
my ($self) = @_;
|
||||
my $checkout_id = $self->param('journey_id');
|
||||
my $uid = $self->current_user->{id};
|
||||
my ($self) = @_;
|
||||
my $journey_id = $self->param('journey_id');
|
||||
my $uid = $self->current_user->{id};
|
||||
|
||||
if ( not( $uid == $self->current_user->{id} and $checkout_id ) ) {
|
||||
if ( not( $journey_id =~ m{ ^ \d+ $ }x ) ) {
|
||||
$self->render(
|
||||
'edit_journey',
|
||||
error => 'notfound',
|
||||
|
@ -434,8 +443,8 @@ sub edit_journey {
|
|||
}
|
||||
|
||||
my $journey = $self->get_journey(
|
||||
uid => $uid,
|
||||
checkout_id => $checkout_id
|
||||
uid => $uid,
|
||||
journey_id => $journey_id
|
||||
);
|
||||
|
||||
if ( not $journey ) {
|
||||
|
@ -449,11 +458,6 @@ sub edit_journey {
|
|||
|
||||
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',
|
||||
|
@ -468,12 +472,8 @@ sub edit_journey {
|
|||
{
|
||||
my $datetime = $parser->parse_datetime( $self->param($key) );
|
||||
if ( $datetime and $datetime->epoch ne $journey->{$key}->epoch ) {
|
||||
$error = $self->update_journey_part(
|
||||
$db,
|
||||
$journey->{ids}[0],
|
||||
$journey->{ids}[1],
|
||||
$key, $datetime
|
||||
);
|
||||
$error = $self->update_journey_part( $db, $journey->{id},
|
||||
$key, $datetime );
|
||||
if ($error) {
|
||||
last;
|
||||
}
|
||||
|
@ -482,17 +482,16 @@ sub edit_journey {
|
|||
|
||||
if ( not $error ) {
|
||||
$journey = $self->get_journey(
|
||||
uid => $uid,
|
||||
db => $db,
|
||||
checkout_id => $checkout_id,
|
||||
verbose => 1
|
||||
uid => $uid,
|
||||
db => $db,
|
||||
journey_id => $journey_id,
|
||||
verbose => 1
|
||||
);
|
||||
$error = $self->journey_sanity_check($journey);
|
||||
}
|
||||
if ( not $error ) {
|
||||
$tx->commit;
|
||||
$self->redirect_to("/journey/${uid}-${checkout_id}");
|
||||
$self->invalidate_stats_cache( $journey->{checkout} );
|
||||
$self->redirect_to("/journey/${journey_id}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
function tvly_run(link, req, redir, err_callback) {
|
||||
function tvly_run(link, req, err_callback) {
|
||||
var error_icon = '<i class="material-icons">error</i>';
|
||||
var progressbar = $('<div class="progress"><div class="indeterminate"></div></div>');
|
||||
link.hide();
|
||||
link.after(progressbar);
|
||||
$.post('/action', req, function(data) {
|
||||
if (data.success) {
|
||||
$(location).attr('href', redir);
|
||||
$(location).attr('href', data.redirect_to);
|
||||
} else {
|
||||
M.toast({html: error_icon + ' ' + data.error});
|
||||
progressbar.remove();
|
||||
|
@ -25,7 +25,7 @@ $(document).ready(function() {
|
|||
station: link.data('station'),
|
||||
train: link.data('train'),
|
||||
};
|
||||
tvly_run(link, req, '/');
|
||||
tvly_run(link, req);
|
||||
});
|
||||
$('.action-checkout').click(function() {
|
||||
var link = $(this);
|
||||
|
@ -34,7 +34,7 @@ $(document).ready(function() {
|
|||
station: link.data('station'),
|
||||
force: link.data('force'),
|
||||
};
|
||||
tvly_run(link, req, '/s/' + req.station, function() {
|
||||
tvly_run(link, req, function() {
|
||||
link.append(' – Ohne Echtzeitdaten auschecken?')
|
||||
link.data('force', true);
|
||||
});
|
||||
|
@ -45,7 +45,7 @@ $(document).ready(function() {
|
|||
action: 'undo',
|
||||
undo_id: link.data('id'),
|
||||
};
|
||||
tvly_run(link, req, '/');
|
||||
tvly_run(link, req);
|
||||
});
|
||||
$('.action-cancelled-from').click(function() {
|
||||
var link = $(this);
|
||||
|
@ -54,7 +54,7 @@ $(document).ready(function() {
|
|||
station: link.data('station'),
|
||||
train: link.data('train'),
|
||||
};
|
||||
tvly_run(link, req, '/');
|
||||
tvly_run(link, req);
|
||||
});
|
||||
$('.action-cancelled-to').click(function() {
|
||||
var link = $(this);
|
||||
|
@ -63,19 +63,19 @@ $(document).ready(function() {
|
|||
station: link.data('station'),
|
||||
force: true,
|
||||
};
|
||||
tvly_run(link, req, '/');
|
||||
tvly_run(link, req);
|
||||
});
|
||||
$('.action-delete').click(function() {
|
||||
var link = $(this);
|
||||
var req = {
|
||||
action: 'delete',
|
||||
ids: link.data('id'),
|
||||
id: link.data('id'),
|
||||
checkin: link.data('checkin'),
|
||||
checkout: link.data('checkout'),
|
||||
};
|
||||
really_delete = confirm("Diese Zugfahrt wirklich löschen? Der Eintrag wird sofort aus der Datenbank entfernt und kann nicht wiederhergestellt werden.");
|
||||
if (really_delete) {
|
||||
tvly_run(link, req, '/history');
|
||||
tvly_run(link, req);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
2
public/static/js/travelynx-actions.min.js
vendored
2
public/static/js/travelynx-actions.min.js
vendored
|
@ -1 +1 @@
|
|||
function tvly_run(n,t,a,c){var i='<i class="material-icons">error</i>',e=$('<div class="progress"><div class="indeterminate"></div></div>');n.hide(),n.after(e),$.post("/action",t,function(t){t.success?$(location).attr("href",a):(M.toast({html:i+" "+t.error}),e.remove(),c&&c(),n.append(" "+i),n.show())})}$(document).ready(function(){$(".action-checkin").click(function(){var t=$(this);tvly_run(t,{action:"checkin",station:t.data("station"),train:t.data("train")},"/")}),$(".action-checkout").click(function(){var t=$(this),n={action:"checkout",station:t.data("station"),force:t.data("force")};tvly_run(t,n,"/s/"+n.station,function(){t.append(" – Ohne Echtzeitdaten auschecken?"),t.data("force",!0)})}),$(".action-undo").click(function(){var t=$(this);tvly_run(t,{action:"undo",undo_id:t.data("id")},"/")}),$(".action-cancelled-from").click(function(){var t=$(this);tvly_run(t,{action:"cancelled_from",station:t.data("station"),train:t.data("train")},"/")}),$(".action-cancelled-to").click(function(){var t=$(this);tvly_run(t,{action:"cancelled_to",station:t.data("station"),force:!0},"/")}),$(".action-delete").click(function(){var t=$(this),n={action:"delete",ids:t.data("id"),checkin:t.data("checkin"),checkout:t.data("checkout")};really_delete=confirm("Diese Zugfahrt wirklich löschen? Der Eintrag wird sofort aus der Datenbank entfernt und kann nicht wiederhergestellt werden."),really_delete&&tvly_run(t,n,"/history")})});
|
||||
function tvly_run(t,n,a){var c='<i class="material-icons">error</i>',i=$('<div class="progress"><div class="indeterminate"></div></div>');t.hide(),t.after(i),$.post("/action",n,function(n){n.success?$(location).attr("href",n.redirect_to):(M.toast({html:c+" "+n.error}),i.remove(),a&&a(),t.append(" "+c),t.show())})}$(document).ready(function(){$(".action-checkin").click(function(){var t=$(this);tvly_run(t,{action:"checkin",station:t.data("station"),train:t.data("train")})}),$(".action-checkout").click(function(){var t=$(this),n={action:"checkout",station:t.data("station"),force:t.data("force")};tvly_run(t,n,function(){t.append(" – Ohne Echtzeitdaten auschecken?"),t.data("force",!0)})}),$(".action-undo").click(function(){var t=$(this);tvly_run(t,{action:"undo",undo_id:t.data("id")})}),$(".action-cancelled-from").click(function(){var t=$(this);tvly_run(t,{action:"cancelled_from",station:t.data("station"),train:t.data("train")})}),$(".action-cancelled-to").click(function(){var t=$(this);tvly_run(t,{action:"cancelled_to",station:t.data("station"),force:!0})}),$(".action-delete").click(function(){var t=$(this),n={action:"delete",id:t.data("id"),checkin:t.data("checkin"),checkout:t.data("checkout")};really_delete=confirm("Diese Zugfahrt wirklich löschen? Der Eintrag wird sofort aus der Datenbank entfernt und kann nicht wiederhergestellt werden."),really_delete&&tvly_run(t,n)})});
|
||||
|
|
|
@ -11,46 +11,44 @@
|
|||
</thead>
|
||||
<tbody>
|
||||
% for my $travel (@{$journeys}) {
|
||||
% if ($travel->{completed}) {
|
||||
% my $detail_link = '/journey/' . current_user()->{id} . '-' . $travel->{ids}->[1];
|
||||
<tr>
|
||||
<td><%= $travel->{sched_departure}->strftime('%d.%m.') %></td>
|
||||
<td><a href="<%= $detail_link %>"><%= $travel->{type} %> <%= $travel->{line} // $travel->{no} %></a></td>
|
||||
<td>
|
||||
<a href="<%= $detail_link %>" class="unmarked">
|
||||
% if (param('cancelled')) {
|
||||
%= $travel->{sched_departure}->strftime('%H:%M')
|
||||
% my $detail_link = '/journey/' . $travel->{id};
|
||||
<tr>
|
||||
<td><%= $travel->{sched_departure}->strftime('%d.%m.') %></td>
|
||||
<td><a href="<%= $detail_link %>"><%= $travel->{type} %> <%= $travel->{line} // $travel->{no} %></a></td>
|
||||
<td>
|
||||
<a href="<%= $detail_link %>" class="unmarked">
|
||||
% if (param('cancelled')) {
|
||||
%= $travel->{sched_departure}->strftime('%H:%M')
|
||||
% }
|
||||
% else {
|
||||
<%= $travel->{rt_departure}->strftime('%H:%M') %>
|
||||
% if ($travel->{sched_departure} != $travel->{rt_departure}) {
|
||||
(<%= sprintf('%+d', ($travel->{rt_departure}->epoch - $travel->{sched_departure}->epoch) / 60) %>)
|
||||
% }
|
||||
% else {
|
||||
<%= $travel->{rt_departure}->strftime('%H:%M') %>
|
||||
% if ($travel->{sched_departure} != $travel->{rt_departure}) {
|
||||
(<%= sprintf('%+d', ($travel->{rt_departure}->epoch - $travel->{sched_departure}->epoch) / 60) %>)
|
||||
% }
|
||||
<br/>
|
||||
<%= $travel->{from_name} %>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="<%= $detail_link %>" class="unmarked">
|
||||
% if (param('cancelled') and $travel->{sched_arrival}->epoch != 0) {
|
||||
%= $travel->{sched_arrival}->strftime('%H:%M')
|
||||
% }
|
||||
% else {
|
||||
% if ($travel->{rt_arrival}->epoch == 0 and $travel->{sched_arrival}->epoch == 0) {
|
||||
<i class="material-icons">timer_off</i>
|
||||
% } else {
|
||||
%= $travel->{rt_arrival}->strftime('%H:%M');
|
||||
% if ($travel->{sched_arrival} != $travel->{rt_arrival}) {
|
||||
(<%= sprintf('%+d', ($travel->{rt_arrival}->epoch - $travel->{sched_arrival}->epoch) / 60) %>)
|
||||
% }
|
||||
% }
|
||||
<br/>
|
||||
<%= $travel->{from_name} %>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="<%= $detail_link %>" class="unmarked">
|
||||
% if (param('cancelled') and $travel->{sched_arrival}->epoch != 0) {
|
||||
%= $travel->{sched_arrival}->strftime('%H:%M')
|
||||
% }
|
||||
% else {
|
||||
% if ($travel->{rt_arrival}->epoch == 0 and $travel->{sched_arrival}->epoch == 0) {
|
||||
<i class="material-icons">timer_off</i>
|
||||
% } else {
|
||||
%= $travel->{rt_arrival}->strftime('%H:%M');
|
||||
% if ($travel->{sched_arrival} != $travel->{rt_arrival}) {
|
||||
(<%= sprintf('%+d', ($travel->{rt_arrival}->epoch - $travel->{sched_arrival}->epoch) / 60) %>)
|
||||
% }
|
||||
% }
|
||||
% }
|
||||
<br/>
|
||||
<%= $travel->{to_name} %>
|
||||
</a></td>
|
||||
</tr>
|
||||
% }
|
||||
% }
|
||||
<br/>
|
||||
<%= $travel->{to_name} %>
|
||||
</a></td>
|
||||
</tr>
|
||||
% }
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
|
@ -6,10 +6,10 @@
|
|||
<div class="card-content white-text">
|
||||
<span class="card-title">Aktuell eingecheckt</span>
|
||||
<p>In <%= $status->{train_type} %> <%= $status->{train_no} %>
|
||||
ab <%= $status->{station_name} %></p>
|
||||
ab <%= $status->{dep_name} %></p>
|
||||
</div>
|
||||
<div class="card-action">
|
||||
<a class="action-checkout" data-station="<%= $ds100 %>">
|
||||
<a class="action-checkout" data-station="<%= $ds100 %>" data-force="1">
|
||||
Hier auschecken
|
||||
</a>
|
||||
</div>
|
||||
|
@ -20,10 +20,10 @@
|
|||
<div class="card-content white-text">
|
||||
<span class="card-title">Ausgecheckt</span>
|
||||
<p>Aus <%= $status->{train_type} %> <%= $status->{train_no} %>
|
||||
bis <%= $status->{station_name} %></p>
|
||||
bis <%= $status->{arr_name} %></p>
|
||||
</div>
|
||||
<div class="card-action">
|
||||
<a class="action-undo" data-id="<%= $status->{action_id} %>">
|
||||
<a class="action-undo" data-id="<%= $status->{journey_id} %>">
|
||||
<i class="material-icons">undo</i> Rückgängig?
|
||||
</a>
|
||||
</div>
|
||||
|
@ -38,8 +38,8 @@
|
|||
– Zug auswählen zum Einchecken.
|
||||
% }
|
||||
% else {
|
||||
– Keine Abfahrten gefunden. Ein Checkin ist frühestens 10 Minuten vor
|
||||
und maximal 180 Minuten nach Abfahrt möglich.
|
||||
– Keine Abfahrten gefunden. Ein Checkin ist frühestens 30 Minuten vor
|
||||
und maximal 120 Minuten nach Abfahrt möglich.
|
||||
% }
|
||||
<br/>
|
||||
<table class="striped">
|
||||
|
|
|
@ -80,7 +80,7 @@
|
|||
</div>
|
||||
<div class="row">
|
||||
<div class="col s6 m6 l6 center-align">
|
||||
<a href="/journey/<%= current_user()->{id} %>-<%= param('journey_id') %>" class="waves-effect waves-light btn">
|
||||
<a href="/journey/<%= param('journey_id') %>" class="waves-effect waves-light btn">
|
||||
Abbrechen
|
||||
</a>
|
||||
</div>
|
||||
|
|
|
@ -161,7 +161,7 @@
|
|||
<div class="row">
|
||||
<div class="col s6 m6 l6 center-align">
|
||||
<a class="waves-effect waves-light red btn action-delete"
|
||||
data-id="<%= join(q{,}, @{$journey->{ids}}) %>"
|
||||
data-id="<%= $journey->{id} %>"
|
||||
data-checkin="<%= $journey->{checkin}->epoch %>"
|
||||
data-checkout="<%= $journey->{checkout}->epoch %>">
|
||||
<i class="material-icons left">delete_forever</i>
|
||||
|
|
|
@ -14,26 +14,84 @@
|
|||
<div class="row">
|
||||
<div class="col s12">
|
||||
% my $status = get_user_status();
|
||||
% my $now = DateTime->now(time_zone => 'Europe/Berlin');
|
||||
% my $dep_wait = ($status->{real_departure}->epoch - $now->epoch)/60;
|
||||
% my $arr_wait = undef;
|
||||
% if ($status->{real_arrival}->epoch) {
|
||||
% $arr_wait = ($status->{real_arrival}->epoch - $now->epoch)/60;
|
||||
% }
|
||||
% if ($status->{checked_in}) {
|
||||
<div class="card green darken-4">
|
||||
<div class="card-content white-text">
|
||||
<span class="card-title">Hallo, <%= current_user()->{name} %>!</span>
|
||||
<p>Du bist gerade eingecheckt in
|
||||
<%= $status->{train_type} %> <%= $status->{train_no} %>
|
||||
ab <%= $status->{station_name} %>.
|
||||
% if ($status->{timestamp_delta} < 180) {
|
||||
<a class="action-undo" data-id="<%= $status->{action_id} %>"><i class="material-icons">undo</i> Rückgängig</a>
|
||||
<span class="card-title">Eingecheckt</span>
|
||||
<p>
|
||||
In <b><%= $status->{train_type} %> <%= $status->{train_no} %></b>
|
||||
% if ($status->{arr_name}) {
|
||||
von <b><%= $status->{dep_name} %></b> nach <b><%= $status->{arr_name} %></b>.
|
||||
% }
|
||||
</p>
|
||||
<p>Bei Ankunft: Station auswählen zum Auschecken.</p>
|
||||
% else {
|
||||
ab <b><%= $status->{dep_name} %></b>.
|
||||
% }
|
||||
</p>
|
||||
<p>
|
||||
Abfahrt
|
||||
% if ($dep_wait > 0) {
|
||||
in <%= int(($status->{real_departure}->epoch - $now->epoch)/60) %> Minute<%= $dep_wait >= 2 ? 'n' : '' %>
|
||||
% }
|
||||
um <b><%= $status->{real_departure}->strftime('%H:%M') %></b>
|
||||
% if ($status->{real_departure}->epoch != $status->{sched_departure}->epoch) {
|
||||
(+<%= int(($status->{real_departure}->epoch - $status->{sched_departure}->epoch)/60) %>)
|
||||
% }
|
||||
</p>
|
||||
<p>
|
||||
% if ($status->{real_arrival}->epoch) {
|
||||
Voraussichtliche Ankunft um <b><%= $status->{real_arrival}->strftime('%H:%M') %></b>
|
||||
% if ($status->{real_arrival}->epoch != $status->{sched_arrival}->epoch) {
|
||||
(+<%= int(($status->{real_arrival}->epoch - $status->{sched_arrival}->epoch)/60) %>)
|
||||
% }
|
||||
% }
|
||||
% else {
|
||||
Ankunft: noch nicht bekannt
|
||||
% }
|
||||
</p>
|
||||
<p>
|
||||
<b>Achtung:</b> Automatischer Checkout ist noch nicht
|
||||
implementiert. Bitte spätestens eine Stunde nach Ankunft
|
||||
am Ziel manuell auschecken.
|
||||
</p>
|
||||
% if ($status->{arr_name}) {
|
||||
<p>Zielstation ändern?</p>
|
||||
% }
|
||||
% else {
|
||||
<p>Zielstation wählen:</p>
|
||||
% }
|
||||
<table>
|
||||
<tbody>
|
||||
% my $is_after = 0;
|
||||
% for my $station (@{$status->{route_after}}) {
|
||||
<tr><td><a class="action-checkout" data-station="<%= $station %>"><%= $station %></a></td></tr>
|
||||
% if ($status->{arr_name} and $station eq $status->{arr_name}) {
|
||||
<tr><td><b><a class="action-checkout" data-station="<%= $station %>"><%= $station %></a></b></td></tr>
|
||||
% }
|
||||
% else {
|
||||
<tr><td><a class="action-checkout" data-station="<%= $station %>"><%= $station %></a></td></tr>
|
||||
% }
|
||||
% }
|
||||
</tbody>
|
||||
</table>
|
||||
% if ($status->{arr_name}) {
|
||||
<p>
|
||||
Falls das Backend ausgefallen ist oder der Zug aus anderen
|
||||
Gründen verloren ging: <a class="action-checkout"
|
||||
data-force="1" data-station="<%= $status->{arr_name}
|
||||
%>">Ohne Echtzeitdaten in <%= $status->{arr_name} %>
|
||||
auschecken</a>.
|
||||
</p>
|
||||
% }
|
||||
</div>
|
||||
<div class="card-action">
|
||||
<a class="action-undo" data-id="in_transit">
|
||||
<i class="material-icons">undo</i> Checkin Rückgängig?
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
% }
|
||||
|
@ -44,9 +102,6 @@
|
|||
<p>Prinzipiell wärest du nun eingecheckt in
|
||||
<%= $status->{train_type} %> <%= $status->{train_no} %>
|
||||
ab <%= $status->{station_name} %>, doch dieser Zug fällt aus.
|
||||
% if ($status->{timestamp_delta} < 180) {
|
||||
<a class="action-undo" data-id="<%= $status->{action_id} %>"><i class="material-icons">undo</i> Checkinversuch rückgängig</a>
|
||||
% }
|
||||
</p>
|
||||
<p>Falls du den Zugausfall z.B. für ein Fahrgastrechteformular
|
||||
dokumentieren möchtest, wähle bitte jetzt deine geplante
|
||||
|
@ -62,6 +117,11 @@
|
|||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="card-action">
|
||||
<a class="action-undo" data-id="in_transit">
|
||||
<i class="material-icons">undo</i> Checkinversuch Rückgängig?
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
% }
|
||||
% else {
|
||||
|
@ -91,7 +151,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<h1>Letzte Fahrten</h1>
|
||||
%= include '_history_trains', journeys => [get_user_travels(limit => 1)];
|
||||
%= include '_history_trains', journeys => [get_user_travels(limit => 5)];
|
||||
% }
|
||||
% else {
|
||||
<div class="row">
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="theme-color" content="#673ab7">
|
||||
% my $av = 'v5'; # asset version
|
||||
% my $av = 'v6'; # asset version
|
||||
%= stylesheet "/static/${av}/css/materialize.min.css"
|
||||
%= stylesheet "/static/${av}/css/material-icons.css"
|
||||
%= stylesheet "/static/${av}/css/local.css"
|
||||
|
|
Loading…
Reference in a new issue