diff options
Diffstat (limited to 'lib/Travelynx')
-rw-r--r-- | lib/Travelynx/Command/database.pm | 208 | ||||
-rw-r--r-- | lib/Travelynx/Controller/Account.pm | 47 | ||||
-rwxr-xr-x | lib/Travelynx/Controller/Api.pm | 16 | ||||
-rw-r--r-- | lib/Travelynx/Controller/Static.pm | 3 | ||||
-rwxr-xr-x | lib/Travelynx/Controller/Traveling.pm | 101 |
5 files changed, 278 insertions, 97 deletions
diff --git a/lib/Travelynx/Command/database.pm b/lib/Travelynx/Command/database.pm index b270262..b5e8cf5 100644 --- a/lib/Travelynx/Command/database.pm +++ b/lib/Travelynx/Command/database.pm @@ -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 { diff --git a/lib/Travelynx/Controller/Account.pm b/lib/Travelynx/Controller/Account.pm index 0037e16..e60f1d3 100644 --- a/lib/Travelynx/Controller/Account.pm +++ b/lib/Travelynx/Controller/Account.pm @@ -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; - - $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 $uid = $self->current_user->{id}; + + 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 + ], + } ); } diff --git a/lib/Travelynx/Controller/Api.pm b/lib/Travelynx/Controller/Api.pm index a9500f1..8e72374 100755 --- a/lib/Travelynx/Controller/Api.pm +++ b/lib/Travelynx/Controller/Api.pm @@ -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, }, ); } diff --git a/lib/Travelynx/Controller/Static.pm b/lib/Travelynx/Controller/Static.pm index 0144d83..09d7f51 100644 --- a/lib/Travelynx/Controller/Static.pm +++ b/lib/Travelynx/Controller/Static.pm @@ -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 { diff --git a/lib/Travelynx/Controller/Traveling.pm b/lib/Travelynx/Controller/Traveling.pm index b43c891..d8e5e03 100755 --- a/lib/Travelynx/Controller/Traveling.pm +++ b/lib/Travelynx/Controller/Traveling.pm @@ -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'); + + my $uid = $self->current_user->{id}; - $self->param( journey_id => $checkout_id ); + $self->param( journey_id => $journey_id ); - if ( not( $uid == $self->current_user->{id} and $checkout_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; } } |