Add travel (checkin/checkout/undo) API
This commit is contained in:
parent
5fe4174feb
commit
46fc64de48
4 changed files with 246 additions and 29 deletions
|
@ -11,7 +11,7 @@ use DateTime::Format::Strptime;
|
|||
use Encode qw(decode encode);
|
||||
use Geo::Distance;
|
||||
use JSON;
|
||||
use List::Util qw(first);
|
||||
use List::Util;
|
||||
use List::MoreUtils qw(after_incl before_incl);
|
||||
use Travel::Status::DE::DBWagenreihung;
|
||||
use Travel::Status::DE::IRIS;
|
||||
|
@ -158,14 +158,14 @@ sub startup {
|
|||
return {
|
||||
status => 1,
|
||||
history => 2,
|
||||
action => 3,
|
||||
travel => 3,
|
||||
import => 4,
|
||||
};
|
||||
}
|
||||
);
|
||||
$self->attr(
|
||||
token_types => sub {
|
||||
return [qw(status history action import)];
|
||||
return [qw(status history travel import)];
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -425,21 +425,23 @@ sub startup {
|
|||
|
||||
$self->helper(
|
||||
'checkin' => sub {
|
||||
my ( $self, $station, $train_id ) = @_;
|
||||
my ( $self, $station, $train_id, $uid ) = @_;
|
||||
|
||||
$uid //= $self->current_user->{id};
|
||||
|
||||
my $status = $self->get_departures( $station, 140, 40, 0 );
|
||||
if ( $status->{errstr} ) {
|
||||
return ( undef, $status->{errstr} );
|
||||
}
|
||||
else {
|
||||
my ($train)
|
||||
= first { $_->train_id eq $train_id } @{ $status->{results} };
|
||||
my ($train) = List::Util::first { $_->train_id eq $train_id }
|
||||
@{ $status->{results} };
|
||||
if ( not defined $train ) {
|
||||
return ( undef, "Train ${train_id} not found" );
|
||||
}
|
||||
else {
|
||||
|
||||
my $user = $self->get_user_status;
|
||||
my $user = $self->get_user_status($uid);
|
||||
if ( $user->{checked_in} or $user->{cancelled} ) {
|
||||
|
||||
if ( $user->{train_id} eq $train_id
|
||||
|
@ -450,7 +452,7 @@ sub startup {
|
|||
}
|
||||
|
||||
# Otherwise, someone forgot to check out first
|
||||
$self->checkout( $station, 1 );
|
||||
$self->checkout( $station, 1, $uid );
|
||||
}
|
||||
|
||||
eval {
|
||||
|
@ -458,7 +460,7 @@ sub startup {
|
|||
$self->pg->db->insert(
|
||||
'in_transit',
|
||||
{
|
||||
user_id => $self->current_user->{id},
|
||||
user_id => $uid,
|
||||
cancelled => $train->departure_is_cancelled
|
||||
? 1
|
||||
: 0,
|
||||
|
@ -488,14 +490,12 @@ sub startup {
|
|||
);
|
||||
};
|
||||
if ($@) {
|
||||
my $uid = $self->current_user->{id};
|
||||
$self->app->log->error(
|
||||
"Checkin($uid): INSERT failed: $@");
|
||||
return ( undef, 'INSERT failed: ' . $@ );
|
||||
}
|
||||
$self->add_route_timestamps( $self->current_user->{id},
|
||||
$train, 1 );
|
||||
$self->run_hook( $self->current_user->{id}, 'checkin' );
|
||||
$self->add_route_timestamps( $uid, $train, 1 );
|
||||
$self->run_hook( $uid, 'checkin' );
|
||||
return ( $train, undef );
|
||||
}
|
||||
}
|
||||
|
@ -504,8 +504,8 @@ sub startup {
|
|||
|
||||
$self->helper(
|
||||
'undo' => sub {
|
||||
my ( $self, $journey_id ) = @_;
|
||||
my $uid = $self->current_user->{id};
|
||||
my ( $self, $journey_id, $uid ) = @_;
|
||||
$uid //= $self->current_user->{id};
|
||||
|
||||
if ( $journey_id eq 'in_transit' ) {
|
||||
eval {
|
||||
|
@ -627,8 +627,8 @@ sub startup {
|
|||
my $journey
|
||||
= $db->select( 'in_transit', '*', { user_id => $uid } )
|
||||
->expand->hash;
|
||||
my ($train)
|
||||
= first { $_->train_id eq $train_id } @{ $status->{results} };
|
||||
my ($train) = List::Util::first { $_->train_id eq $train_id }
|
||||
@{ $status->{results} };
|
||||
|
||||
# When a checkout is triggered by a checkin, there is an edge case
|
||||
# with related stations.
|
||||
|
@ -641,8 +641,8 @@ sub startup {
|
|||
# well.
|
||||
if ( not $train ) {
|
||||
$status = $self->get_departures( $station, 120, 180, 1 );
|
||||
($train)
|
||||
= first { $_->train_id eq $train_id } @{ $status->{results} };
|
||||
($train) = List::Util::first { $_->train_id eq $train_id }
|
||||
@{ $status->{results} };
|
||||
}
|
||||
|
||||
# Store the intended checkout station regardless of this operation's
|
||||
|
@ -681,8 +681,11 @@ sub startup {
|
|||
|
||||
# Arrival time via IRIS is unknown, so the train probably has not
|
||||
# arrived yet. Fall back to HAFAS.
|
||||
if ( my $station_data
|
||||
= first { $_->[0] eq $station } @{ $journey->{route} } )
|
||||
if (
|
||||
my $station_data
|
||||
= List::Util::first { $_->[0] eq $station }
|
||||
@{ $journey->{route} }
|
||||
)
|
||||
{
|
||||
$station_data = $station_data->[1];
|
||||
if ( $station_data->{sched_arr} ) {
|
||||
|
@ -784,7 +787,7 @@ sub startup {
|
|||
return ( 0, undef );
|
||||
}
|
||||
$self->run_hook( $uid, 'update' );
|
||||
$self->add_route_timestamps( $self->current_user->{id}, $train, 0 );
|
||||
$self->add_route_timestamps( $uid, $train, 0 );
|
||||
return ( 1, undef );
|
||||
}
|
||||
);
|
||||
|
@ -3234,6 +3237,7 @@ sub startup {
|
|||
$r->get('/ajax/status/:name')->to('traveling#public_status_card');
|
||||
$r->get('/ajax/status/:name/:ts')->to('traveling#public_status_card');
|
||||
$r->post('/api/v1/import')->to('api#import_v1');
|
||||
$r->post('/api/v1/travel')->to('api#travel_v1');
|
||||
$r->post('/action')->to('traveling#log_action');
|
||||
$r->post('/geolocation')->to('traveling#geolocation');
|
||||
$r->post('/list_departures')->to('traveling#redirect_to_station');
|
||||
|
|
|
@ -2,6 +2,7 @@ package Travelynx::Controller::Api;
|
|||
use Mojo::Base 'Mojolicious::Controller';
|
||||
|
||||
use DateTime;
|
||||
use List::Util;
|
||||
use Travel::Status::DE::IRIS::Stations;
|
||||
use UUID::Tiny qw(:std);
|
||||
|
||||
|
@ -165,6 +166,166 @@ sub get_v1 {
|
|||
}
|
||||
}
|
||||
|
||||
sub travel_v1 {
|
||||
my ($self) = @_;
|
||||
|
||||
my $payload = $self->req->json;
|
||||
my $api_token = $payload->{token} // '';
|
||||
|
||||
if ( $api_token !~ qr{ ^ (?<id> \d+ ) - (?<token> .* ) $ }x ) {
|
||||
$self->render(
|
||||
json => {
|
||||
success => \0,
|
||||
error => 'Malformed JSON or malformed token',
|
||||
},
|
||||
);
|
||||
return;
|
||||
}
|
||||
my $uid = $+{id};
|
||||
$api_token = $+{token};
|
||||
|
||||
if ( $uid > 2147483647 ) {
|
||||
$self->render(
|
||||
json => {
|
||||
success => \0,
|
||||
error => 'Malformed token',
|
||||
},
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
my $token = $self->get_api_token($uid);
|
||||
if ( $api_token ne $token->{'travel'} ) {
|
||||
$self->render(
|
||||
json => {
|
||||
success => \0,
|
||||
error => 'Invalid token',
|
||||
},
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if ( not exists $payload->{action}
|
||||
or $payload->{action} !~ m{^(checkin|checkout|undo)$} )
|
||||
{
|
||||
$self->render(
|
||||
json => {
|
||||
success => \0,
|
||||
error => 'Missing or invalid action',
|
||||
},
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if ( $payload->{action} eq 'checkin' ) {
|
||||
my $from_station = sanitize( q{}, $payload->{fromStation} );
|
||||
my $to_station = sanitize( q{}, $payload->{toStation} );
|
||||
my $train_id;
|
||||
|
||||
if ( exists $payload->{train}{id} ) {
|
||||
$train_id = sanitize( 0, $payload->{train}{id} );
|
||||
}
|
||||
else {
|
||||
my $train_type = sanitize( q{}, $payload->{train}{type} );
|
||||
my $train_no = sanitize( q{}, $payload->{train}{no} );
|
||||
my $status = $self->get_departures( $from_station, 140, 40, 0 );
|
||||
if ( $status->{errstr} ) {
|
||||
$self->render(
|
||||
json => {
|
||||
success => \0,
|
||||
error => 'Fehler am Abfahrtsbahnhof: '
|
||||
. $status->{errstr},
|
||||
status => $self->get_user_status_json_v1($uid)
|
||||
}
|
||||
);
|
||||
return;
|
||||
}
|
||||
my ($train) = List::Util::first {
|
||||
$_->type eq $train_type and $_->train_no eq $train_no
|
||||
}
|
||||
@{ $status->{results} };
|
||||
if ( not defined $train ) {
|
||||
$self->render(
|
||||
json => {
|
||||
success => \0,
|
||||
error => 'Fehler am Abfahrtsbahnhof: '
|
||||
. $status->{errstr},
|
||||
status => $self->get_user_status_json_v1($uid)
|
||||
}
|
||||
);
|
||||
return;
|
||||
}
|
||||
$train_id = $train->train_id;
|
||||
}
|
||||
|
||||
my ( $train, $error )
|
||||
= $self->checkin( $from_station, $train_id, $uid );
|
||||
if ( $to_station and not $error ) {
|
||||
( $train, $error ) = $self->checkout( $to_station, 0, $uid );
|
||||
}
|
||||
if ($error) {
|
||||
$self->render(
|
||||
json => {
|
||||
success => \0,
|
||||
error => $error,
|
||||
status => $self->get_user_status_json_v1($uid)
|
||||
}
|
||||
);
|
||||
}
|
||||
else {
|
||||
$self->render(
|
||||
json => {
|
||||
success => \1,
|
||||
status => $self->get_user_status_json_v1($uid)
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
elsif ( $payload->{action} eq 'checkout' ) {
|
||||
my $to_station = sanitize( q{}, $payload->{toStation} );
|
||||
|
||||
my ( $train, $error )
|
||||
= $self->checkout( $to_station, $payload->{force} ? 1 : 0, $uid );
|
||||
if ($error) {
|
||||
$self->render(
|
||||
json => {
|
||||
success => \0,
|
||||
error => $error,
|
||||
status => $self->get_user_status_json_v1($uid)
|
||||
}
|
||||
);
|
||||
}
|
||||
else {
|
||||
$self->render(
|
||||
json => {
|
||||
success => \1,
|
||||
status => $self->get_user_status_json_v1($uid)
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
elsif ( $payload->{action} eq 'undo' ) {
|
||||
my $error = $self->undo( 'in_transit', $uid );
|
||||
if ($error) {
|
||||
$self->render(
|
||||
json => {
|
||||
success => \0,
|
||||
error => $error,
|
||||
status => $self->get_user_status_json_v1($uid)
|
||||
}
|
||||
);
|
||||
}
|
||||
else {
|
||||
$self->render(
|
||||
json => {
|
||||
success => \1,
|
||||
status => $self->get_user_status_json_v1($uid)
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub import_v1 {
|
||||
my ($self) = @_;
|
||||
|
||||
|
|
|
@ -183,7 +183,7 @@
|
|||
<td>
|
||||
%= form_for 'set_token' => begin
|
||||
%= csrf_field
|
||||
%= hidden_field 'token' => 'action'
|
||||
%= hidden_field 'token' => 'travel'
|
||||
<button class="btn waves-effect waves-light" type="submit" name="action" value="generate">
|
||||
Generieren
|
||||
</button>
|
||||
|
|
|
@ -64,17 +64,69 @@
|
|||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<!--
|
||||
<h3>History</h3>
|
||||
|
||||
<h2>Travel</h2>
|
||||
<div class="row">
|
||||
<div class="col s12">
|
||||
<p>
|
||||
Coming soon.
|
||||
Checkin per API. Sobald eine Zielstation bekannt ist, erfolgt der
|
||||
Checkout wie beim Webinterface automatisch zehn Minuten nach Ankunft.
|
||||
</p>
|
||||
<p style="font-family: Monospace;">
|
||||
curl -X POST -H "Content-Type: application/json" -d '{"token":"<%= $uid %>-<%= $token->{travel} // 'TOKEN' %>"}' <%= $api_root %>/travel
|
||||
</p>
|
||||
<p>Payload zum Einchecken, optional mit Zielwahl:</p>
|
||||
<p style="font-family: Monospace;">
|
||||
{<br/>
|
||||
"token" : "<%= $uid %>-<%= $token->{import} // 'TOKEN' %>",<br/>
|
||||
"action" : "checkin",<br/>
|
||||
"train" : {<br/>
|
||||
"type" : "ICE",<br/>
|
||||
"no" : "1234",<br/>
|
||||
}<br/>
|
||||
"fromStation" : "Essen Hbf", (DS100 oder EVA-Nummer sind ebenfalls möglich)<br/>
|
||||
"toStation" : "Berlin Hbf" (optional, DS100 oder EVA-Nummer sind ebenfalls möglich)<br/>
|
||||
}
|
||||
</p>
|
||||
<p>Payload zur Wahl eines neuen Ziels, wenn bereits eingecheckt:</p>
|
||||
<p style="font-family: Monospace;">
|
||||
{<br/>
|
||||
"token" : "<%= $uid %>-<%= $token->{import} // 'TOKEN' %>",<br/>
|
||||
"action" : "checkout",<br/>
|
||||
"force" : True/False, (wenn True: Checkout jetzt durchführen und auftretende Fehler ignorieren. Kann zu Logeinträgen ohne Ankunftsdaten führen.)<br/>
|
||||
"toStation" : "Berlin Hbf" (DS100 oder EVA-Nummer sind ebenfalls möglich)<br/>
|
||||
}
|
||||
</p>
|
||||
<p>Payload zum Rückgängigmachen eines Checkins (nur während der Fahrt möglich):</p>
|
||||
<p style="font-family: Monospace;">
|
||||
{<br/>
|
||||
"token" : "<%= $uid %>-<%= $token->{import} // 'TOKEN' %>",<br/>
|
||||
"action" : "undo"<br/>
|
||||
}
|
||||
</p>
|
||||
<p>
|
||||
Antwort bei Erfolg:
|
||||
</p>
|
||||
<p style="font-family: Monospace;">
|
||||
{<br/>
|
||||
"success" : True,<br/>
|
||||
"status" : { aktueller Nutzerstatus gemäß Status-API }<br/>
|
||||
}
|
||||
</p>
|
||||
<p>
|
||||
Antwort bei Fehler:
|
||||
</p>
|
||||
<p style="font-family: Monospace;">
|
||||
{<br/>
|
||||
"success" : False,<br/>
|
||||
"error" : "Begründung",<br/>
|
||||
"status" : { aktueller Nutzerstatus gemäß Status-API }<br/>
|
||||
}
|
||||
</p>
|
||||
</div>
|
||||
</div>-->
|
||||
</div>
|
||||
|
||||
<h3>Import</h3>
|
||||
<h2>Import</h2>
|
||||
<div class="row">
|
||||
<div class="col s12">
|
||||
<p>
|
||||
|
@ -86,7 +138,7 @@
|
|||
<p>Payload (alle nicht als optional markierten Felder sind Pflicht):</p>
|
||||
<p style="font-family: Monospace;">
|
||||
{<br/>
|
||||
"token" : "<%= $token->{import} // 'TOKEN' %>",<br/>
|
||||
"token" : "<%= $uid %>-<%= $token->{import} // 'TOKEN' %>",<br/>
|
||||
"dryRun" : True/False, (optional: wenn True, wird die Eingabe validiert, aber keine Zugfahrt angelegt)<br/>
|
||||
"cancelled" : True/False, (Zugausfall?)<br/>
|
||||
"train" : {<br/>
|
||||
|
|
Loading…
Reference in a new issue