add PIREP pre-file and ACARS updates; removing caching from ACARS/Pirep/User repositories; adjust PirepState enum values; add additional columns

This commit is contained in:
Nabeel Shahzad 2017-12-25 15:19:34 -06:00
parent 70b1476e93
commit 3bd97b4d37
41 changed files with 688 additions and 192 deletions

View File

@ -0,0 +1,159 @@
<?php
namespace App\Console\Commands;
use GuzzleHttp\Client;
use Illuminate\Console\Command;
use Illuminate\Database\Eloquent\Collection;
use App\Facades\Utils;
class AcarsReplay extends Command
{
protected $signature = 'phpvms:replay {files} {--manual}';
protected $description = 'Replay an ACARS file';
/**
* API Key to post as
* @var string
*/
protected $apiKey = 'testadminapikey';
/**
* For automatic updates, how many seconds to sleep between updates
* @var int
*/
protected $sleepTime = 10;
/**
* @var array key == update[callsign]
* value == PIREP ID
*/
protected $pirepList = [];
/**
* @var Client
*/
protected $httpClient;
/**
* Return an instance of an HTTP client all ready to post
*/
public function __construct()
{
parent::__construct();
$this->httpClient = new Client([
'base_uri' => config('app.url'),
'headers' => [
'Authorization' => $this->apiKey,
]
]);
}
/**
* Make a request to start a PIREP
* @param \stdClass $flight
* @return string
*/
protected function startPirep($flight)
{
# convert the planned flight time to be completely in minutes
$pft = Utils::hoursToMinutes($flight->planned_hrsenroute,
$flight->planned_minenroute);
$response = $this->httpClient->post('/api/pirep/prefile', [
'json' => [
'airline_id' => 1,
'aircraft_id' => 1, # TODO: Lookup
'dpt_airport' => $flight->planned_depairport,
'arr_airport' => $flight->planned_destairport,
'altitude' => $flight->planned_altitude,
'planned_flight_time' => $pft,
'route' => $flight->planned_route,
]
]);
$body = \json_decode($response->getBody()->getContents());
return $body->id;
}
/**
* Parse this file and run the updates
* @param array $files
*/
protected function runUpdates(array $files)
{
/**
* @var $flights Collection
*/
$flights = collect($files)->transform(function ($f) {
$file = storage_path('/replay/' . $f . '.json');
if (file_exists($file)) {
$this->info('Loading ' . $file);
$contents = file_get_contents($file);
$contents = \json_decode($contents);
return collect($contents->updates);
} else {
$this->error($file . ' not found, skipping');
return false;
}
})
# remove any of errored file entries
->filter(function ($value, $key) {
return $value !== false;
});
$this->info('Starting playback');
/**
* File the initial pirep to get a "preflight" status
*/
$flights->each(function ($updates, $idx) {
$update = $updates->first();
$pirep_id = $this->startPirep($update);
$this->pirepList[$update->callsign] = $pirep_id;
$this->info('Prefiled ' . $update->callsign . ', ID: ' . $pirep_id);
});
/**
* Iterate through all of the flights, retrieving the updates
* from each individual flight. Remove the update. Continue through
* until there are no updates left, at which point we remove the flight
* and updates.
*
* Continue until we have no more flights and updates left
*/
while ($flights->count() > 0) {
$flights = $flights->each(function ($updates, $idx) {
$update = $updates->shift();
})->filter(function ($updates, $idx) {
return $updates->count() > 0;
});
}
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$files = $this->argument('files');
$manual_mode = $this->option('manual');
if(!$manual_mode) {
$this->info('Going to send updates every 10s');
} else {
$this->info('In "manual advance" mode');
}
$this->runUpdates(explode(',', $files));
$this->info('Done!');
}
}

View File

@ -0,0 +1,20 @@
<?php
use Faker\Generator as Faker;
$factory->define(App\Models\Acars::class, function (Faker $faker) {
return [
'id' => substr($faker->unique()->sha1, 0, 12),
'pirep_id' => '', # TODO: Fill this out
'lat' => $faker->latitude,
'lon' => $faker->longitude,
'heading' => $faker->numberBetween(0, 359),
'altitude' => $faker->numberBetween(20, 400),
'vs' => $faker->numberBetween(-5000, 5000),
'gs' => $faker->numberBetween(300, 500),
'transponder' => $faker->numberBetween(200, 9999),
'autopilot' => $faker->text(10),
'fuel_flow' => $faker->randomFloat(2, 100, 1000),
'sim_time' => $faker->dateTime('now', 'UTC'),
];
});

View File

@ -1,23 +1,19 @@
<?php
use App\Models\Enums\PirepSource;
use App\Models\Enums\PirepState;
use Faker\Generator as Faker;
# Match the list available in tests/data/*.yml
$airlinesAvailable = [1];
/**
* Create a new PIREP
*/
$factory->define(App\Models\Pirep::class, function (Faker $faker) use ($airlinesAvailable) {
$factory->define(App\Models\Pirep::class, function (Faker $faker) {
static $raw_data;
return [
'id' => substr($faker->unique()->sha1, 0, 12),
'airline_id' => 1, #$faker->randomElement($airlinesAvailable),
'airline_id' => function () { # OVERRIDE THIS IF NEEDED
return factory(App\Models\Airline::class)->create()->id;
},
'user_id' => function () { # OVERRIDE THIS IF NEEDED
return factory(App\Models\User::class)->create()->id;
},
@ -39,11 +35,15 @@ $factory->define(App\Models\Pirep::class, function (Faker $faker) use ($airlines
'arr_airport_id' => function () {
return factory(App\Models\Airport::class)->create()->id;
},
'altitude' => $faker->numberBetween(20, 400),
'flight_time' => $faker->randomFloat(2),
'planned_flight_time' => $faker->randomFloat(2),
'gross_weight' => $faker->randomFloat(2),
'route' => $faker->text(200),
'notes' => $faker->text(200),
'source' => $faker->randomElement([PirepSource::MANUAL, PirepSource::ACARS]),
'state' => PirepState::PENDING, //$faker->randomElement([-1, 0, 1]), # REJECTED/PENDING/ACCEPTED
'state' => PirepState::PENDING,
'status' => PirepStatus::SCHEDULED,
'raw_data' => $raw_data ?: $raw_data = json_encode(['key' => 'value']),
'created_at' => $faker->dateTimeBetween('-1 week', 'now'),
'updated_at' => function(array $pirep) {

View File

@ -22,10 +22,10 @@ class CreateFlightTables extends Migration
$table->string('dpt_airport_id', 5);
$table->string('arr_airport_id', 5);
$table->string('alt_airport_id', 5)->nullable();
$table->text('route')->nullable();
$table->string('dpt_time', 10)->nullable();
$table->string('arr_time', 10)->nullable();
$table->unsignedDecimal('flight_time', 19)->nullable();
$table->text('route')->nullable();
$table->text('notes')->nullable();
$table->boolean('has_bid')->default(false);
$table->boolean('active')->default(true);

View File

@ -1,5 +1,8 @@
<?php
use App\Models\Enums\PirepState;
use App\Models\Enums\PirepStatus;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
@ -24,14 +27,16 @@ class CreatePirepTables extends Migration
$table->string('route_leg', 5)->nullable();
$table->string('dpt_airport_id', 5);
$table->string('arr_airport_id', 5);
$table->unsignedDecimal('flight_time', 19);
$table->unsignedInteger('altitude')->nullable();
$table->unsignedDecimal('flight_time', 19)->nullable();
$table->unsignedDecimal('planned_flight_time', 19)->nullable();
$table->unsignedDecimal('gross_weight', 19)->nullable();
$table->unsignedDecimal('fuel_used', 19)->nullable();
$table->string('route', 250)->nullable();
$table->string('notes', 250)->nullable();
$table->text('route')->nullable();
$table->text('notes')->nullable();
$table->unsignedTinyInteger('source')->default(0);
$table->tinyInteger('state')->default(0); # -1 rejected, 0 pending, 1 accepted
#$table->tinyInteger('status')->default(0); # -1 rejected, 0 pending, 1 accepted
$table->tinyInteger('state')->default(PirepState::PENDING);
$table->tinyInteger('status')->default(PirepStatus::SCHEDULED);
$table->longText('raw_data')->nullable();
$table->timestamps();
$table->softDeletes();

View File

@ -14,22 +14,24 @@ class CreateAcarsTables extends Migration
public function up()
{
Schema::create('acars', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('id', 12);
$table->string('pirep_id', 12);
$table->string('name', 10)->nullable();
$table->float('lat', 7, 4)->default(0.0);
$table->float('lon', 7, 4)->default(0.0);
# TODO: More columns here for what might be required
# polymorphic relation columns.
# parent_type can be flight, pirep or acars
# once
#$table->unsignedBigInteger('parent_id');
#$table->string('parent_type');
$table->unsignedInteger('heading')->nullable();
$table->unsignedInteger('altitude')->nullable();
$table->integer('vs')->nullable();
$table->unsignedInteger('gs')->nullable();
$table->unsignedInteger('transponder')->nullable();
$table->string('autopilot')->nullable();
$table->decimal('fuel_flow')->nullable();
$table->dateTimeTz('sim_time')->nullable();
$table->timestamps();
$table->primary('id');
$table->index('pirep_id');
$table->index('created_at');
});
}

View File

@ -346,7 +346,7 @@ pireps:
dpt_airport_id: KAUS
arr_airport_id: KJFK
flight_time: 180 # 6 hours
state: 0
state: 1
route: PLMMR2 SPA Q22 BEARI FAK PHLBO3
notes: just a pilot report
created_at: NOW
@ -359,7 +359,7 @@ pireps:
dpt_airport_id: KJFK
arr_airport_id: KAUS
flight_time: 180 # 6 hours
state: 0
state: 1
route: PLMMR2 SPA Q22 BEARI FAK PHLBO3
notes: just a pilot report
created_at: NOW
@ -372,7 +372,7 @@ pireps:
dpt_airport_id: KJFK
arr_airport_id: KAUS
flight_time: 180 # 6 hours
state: 0
state: 1
route: PLMMR2 SPA Q22 BEARI FAK PHLBO3
notes: just a pilot report
created_at: NOW

View File

@ -103,11 +103,17 @@ class Utils extends Facade
/**
* @param $hours
* @param null $minutes
* @return float|int
*/
public static function hoursToMinutes($hours)
public static function hoursToMinutes($hours, $minutes=null)
{
return $hours * 60;
$total = (int) $hours * 60;
if($minutes) {
$total += (int) $minutes;
}
return $total;
}
/**

View File

@ -0,0 +1,43 @@
<?php
namespace App\Http\Controllers\Api;
use Log;
use App\Models\Acars;
use Illuminate\Http\Request;
use App\Http\Controllers\AppBaseController;
use App\Repositories\AcarsRepository;
use App\Repositories\PirepRepository;
use App\Http\Resources\Acars as AcarsResource;
class AcarsController extends AppBaseController
{
protected $acarsRepo, $pirepRepo;
public function __construct(
AcarsRepository $acarsRepo,
PirepRepository $pirepRepo
) {
$this->acarsRepo = $acarsRepo;
$this->pirepRepo = $pirepRepo;
}
public function index(Request $request)
{
/*PirepResource::withoutWrapping();
return new PirepResource($this->pirepRepo->find($id));*/
}
/**
* Return the current ACARS map data in GeoJSON format
* @param Request $request
*/
public function geojson(Request $request)
{
}
}

View File

@ -2,17 +2,31 @@
namespace App\Http\Controllers\Api;
use Log;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use App\Models\Acars;
use App\Models\Enums\PirepState;
use App\Models\Enums\PirepStatus;
use App\Http\Resources\Acars as AcarsResource;
use App\Http\Resources\Pirep as PirepResource;
use App\Http\Controllers\AppBaseController;
use App\Repositories\AcarsRepository;
use App\Repositories\PirepRepository;
use App\Http\Controllers\AppBaseController;
class PirepController extends AppBaseController
{
protected $pirepRepo;
protected $acarsRepo, $pirepRepo;
public function __construct(PirepRepository $pirepRepo)
{
public function __construct(
AcarsRepository $acarsRepo,
PirepRepository $pirepRepo
) {
$this->acarsRepo = $acarsRepo;
$this->pirepRepo = $pirepRepo;
}
@ -21,4 +35,88 @@ class PirepController extends AppBaseController
PirepResource::withoutWrapping();
return new PirepResource($this->pirepRepo->find($id));
}
/**
* Create a new PIREP and place it in a "inprogress" and "prefile" state
* Once ACARS updates are being processed, then it can go into an 'ENROUTE'
* status, and whatever other statuses may be defined
*
* TODO: Allow extra fields, etc to be set. Aircraft, etc
*/
public function prefile(Request $request)
{
Log::info('PIREP Prefile, user '. Auth::user()->pilot_id,
$request->toArray());
/*$validator = Validator::make($request, [
'aircraft_id' => 'required',
'dpt_airport_id' => 'required',
'arr_airport_id' => 'required',
'altitude' => 'nullable|integer',
'route' => 'nullable',
'notes' => 'nullable',
]);*/
$attr = [];
$attr['user_id'] = Auth::user()->id;
$attr['airline_id'] = $request->get('airline_id');
$attr['aircraft_id'] = $request->get('aircraft_id');
$attr['dpt_airport_id'] = $request->get('dpt_airport');
$attr['arr_airport_id'] = $request->get('arr_airport');
$attr['altitude'] = $request->get('altitude');
$attr['route'] = $request->get('route');
$attr['notes'] = $request->get('notes');
$attr['state'] = PirepState::IN_PROGRESS;
$attr['status'] = PirepStatus::PREFILE;
try {
$pirep = $this->pirepRepo->create($attr);
} catch(\Exception $e) {
Log::error($e);
}
Log::info('PIREP PREFILED');
Log::info($pirep->id);
PirepResource::withoutWrapping();
return new PirepResource($pirep);
}
/**
* Get all of the ACARS updates for a PIREP
* @param $id
* @return AcarsResource
*/
public function acars_get($id)
{
$pirep = $this->pirepRepo->find($id);
$updates = $this->acarsRepo->forPirep($id);
return new AcarsResource($updates);
}
/**
* Post ACARS updates for a PIREP
* @param $id
* @param Request $request
* @return AcarsResource
*/
public function acars_store($id, Request $request)
{
$pirep = $this->pirepRepo->find($id);
Log::info('Posting ACARS update', $request->toArray());
$attrs = $request->toArray();
$attrs['pirep_id'] = $id;
$update = Acars::create($attrs);
$update->save();
# Change the PIREP status
$pirep->status = PirepStatus::ENROUTE;
$pirep->save();
AcarsResource::withoutWrapping();
return new AcarsResource($update);
}
}

View File

@ -0,0 +1,19 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\Resource;
class Acars extends Resource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
return parent::toArray($request);
}
}

View File

@ -1,10 +1,36 @@
<?php
namespace App;
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use App\Models\Traits\HashId;
class Acars extends Model
class Acars extends BaseModel
{
use HashId;
public $incrementing = false;
public $table = 'acars';
public $fillable = [
'pirep_id',
'name',
'lat',
'lon',
'altitude',
'vs',
'gs',
'transponder',
'autopilot',
'fuel_flow',
'sim_time',
];
/**
* FKs
*/
public function pirep()
{
return $this->belongsTo('App\Models\Pirep', 'pirep_id');
}
}

View File

@ -2,9 +2,7 @@
namespace App\Models;
use Eloquent as Model;
class Aircraft extends Model
class Aircraft extends BaseModel
{
public $table = 'aircraft';

View File

@ -2,13 +2,11 @@
namespace App\Models;
use Eloquent as Model;
/**
* Class Airline
* @package App\Models
*/
class Airline extends Model
class Airline extends BaseModel
{
public $table = 'airlines';

View File

@ -2,13 +2,11 @@
namespace App\Models;
use Eloquent as Model;
/**
* Class Airport
* @package App\Models
*/
class Airport extends Model
class Airport extends BaseModel
{
public $table = 'airports';
public $timestamps = false;

8
app/Models/BaseModel.php Normal file
View File

@ -0,0 +1,8 @@
<?php
namespace App\Models;
class BaseModel extends \Illuminate\Database\Eloquent\Model
{
}

View File

@ -6,11 +6,13 @@ namespace App\Models\Enums;
class PirepState extends EnumBase {
const REJECTED = -1;
const PENDING = 0;
const ACCEPTED = 1;
const IN_PROGRESS = 0;
const PENDING = 1;
const ACCEPTED = 2;
protected static $labels = [
PirepState::REJECTED => 'system.pireps.state.rejected',
PirepState::IN_PROGRESS => 'system.pireps.state.in_progress',
PirepState::PENDING => 'system.pireps.state.pending',
PirepState::ACCEPTED => 'system.pireps.state.accepted',
];

View File

@ -2,14 +2,12 @@
namespace App\Models;
use Eloquent as Model;
/**
* Class Fare
*
* @package App\Models
*/
class Fare extends Model
class Fare extends BaseModel
{
public $table = 'fares';

View File

@ -2,11 +2,9 @@
namespace App\Models;
use Eloquent as Model;
use App\Models\Traits\HashId;
class Flight extends Model
class Flight extends BaseModel
{
use HashId;

View File

@ -2,14 +2,12 @@
namespace App\Models;
use Eloquent as Model;
/**
* Class Flight
*
* @package App\Models
*/
class FlightFields extends Model
class FlightFields extends BaseModel
{
public $table = 'flight_fields';

View File

@ -2,9 +2,7 @@
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Navdata extends Model
class Navdata extends BaseModel
{
public $table = 'navdata';
public $timestamps = false;

View File

@ -2,7 +2,6 @@
namespace App\Models;
use Eloquent as Model;
use App\Models\Traits\HashId;
use Illuminate\Database\Eloquent\SoftDeletes;
@ -11,7 +10,7 @@ use Illuminate\Database\Eloquent\SoftDeletes;
*
* @package App\Models
*/
class Pirep extends Model
class Pirep extends BaseModel
{
use HashId;
use SoftDeletes;
@ -29,7 +28,9 @@ class Pirep extends Model
'route_leg',
'airline_id',
'aircraft_id',
'altitude',
'flight_time',
'planned_flight_time',
'dpt_airport_id',
'arr_airport_id',
'fuel_used',
@ -50,8 +51,11 @@ class Pirep extends Model
protected $casts = [
'id' => 'string',
'flight_time' => 'integer',
'planned_flight_time' => 'integer',
'level' => 'integer',
'fuel_used' => 'integer',
'altitude' => 'integer',
'fuel_used' => 'float',
'gross_weight' => 'float',
'source' => 'integer',
'state' => 'integer',
'status' => 'integer',

View File

@ -2,14 +2,12 @@
namespace App\Models;
use Eloquent as Model;
/**
* Class PirepEvent
*
* @package App\Models
*/
class PirepComment extends Model
class PirepComment extends BaseModel
{
public $table = 'pirep_comments';

View File

@ -2,14 +2,12 @@
namespace App\Models;
use Eloquent as Model;
/**
* Class PirepEvent
*
* @package App\Models
*/
class PirepEvent extends Model
class PirepEvent extends BaseModel
{
public $table = 'pirep_fields';

View File

@ -2,14 +2,12 @@
namespace App\Models;
use Eloquent as Model;
/**
* Class PirepField
*
* @package App\Models
*/
class PirepField extends Model
class PirepField extends BaseModel
{
public $table = 'pirep_fields';

View File

@ -2,14 +2,12 @@
namespace App\Models;
use Eloquent as Model;
/**
* Class PirepField
*
* @package App\Models
*/
class PirepFieldValues extends Model
class PirepFieldValues extends BaseModel
{
public $table = 'pirep_field_values';

View File

@ -2,13 +2,11 @@
namespace App\Models;
use Eloquent as Model;
/**
* Class Ranking
* @package App\Models
*/
class Rank extends Model
class Rank extends BaseModel
{
public $table = 'ranks';
@ -29,9 +27,9 @@ class Rank extends Model
protected $casts = [
'name' => 'string',
'hours' => 'integer',
'auto_approve_acars' => 'integer',
'auto_approve_manual' => 'integer',
'auto_promote' => 'integer',
'auto_approve_acars' => 'bool',
'auto_approve_manual' => 'bool',
'auto_promote' => 'bool',
];
/**

View File

@ -8,9 +8,7 @@
namespace App\Models;
use Eloquent as Model;
class Setting extends Model
class Setting extends BaseModel
{
public $table = 'settings';

View File

@ -2,13 +2,11 @@
namespace App\Models;
use Eloquent as Model;
/**
* Class Subfleet
* @package App\Models
*/
class Subfleet extends Model
class Subfleet extends BaseModel
{
public $table = 'subfleets';
protected $dates = ['deleted_at'];

View File

@ -37,6 +37,7 @@ class User extends Authenticatable
'airline_id',
'home_airport_id',
'curr_airport_id',
'last_pirep_id',
'rank_id',
'timezone',
'state',
@ -96,6 +97,11 @@ class User extends Authenticatable
return $this->belongsTo('App\Models\Airport', 'curr_airport_id');
}
public function last_pirep()
{
return $this->belongsTo('App\Models\Pirep', 'last_pirep_id');
}
public function bids()
{
return $this->hasMany('App\Models\UserBid', 'user_id');

View File

@ -2,12 +2,10 @@
namespace App\Models;
use Eloquent as Model;
/**
* @package App\Models
*/
class UserBid extends Model
class UserBid extends BaseModel
{
public $table = 'user_bids';

View File

@ -0,0 +1,22 @@
<?php
namespace App\Repositories;
use App\Models\Acars;
use App\Repositories\Traits\CacheableRepository;
use Prettus\Repository\Contracts\CacheableInterface;
class AcarsRepository extends BaseRepository implements CacheableInterface
{
use CacheableRepository;
public function model()
{
return Acars::class;
}
public function forPirep($pirep_id)
{
return $this->findWhere(['pirep_id' => $pirep_id]);
}
}

View File

@ -8,10 +8,8 @@ use App\Models\User;
use App\Repositories\Traits\CacheableRepository;
use Prettus\Repository\Contracts\CacheableInterface;
class PirepRepository extends BaseRepository implements CacheableInterface
class PirepRepository extends BaseRepository
{
use CacheableRepository;
protected $fieldSearchable = [
'user_id',
'flight_id',

View File

@ -2,17 +2,13 @@
namespace App\Repositories;
use Illuminate\Http\Request;
use Prettus\Repository\Contracts\CacheableInterface;
use App\Models\User;
use App\Models\Enums\PilotState;
use App\Repositories\Criteria\WhereCriteria;
use App\Repositories\Traits\CacheableRepository;
class UserRepository extends BaseRepository implements CacheableInterface
class UserRepository extends BaseRepository
{
use CacheableRepository;
protected $fieldSearchable = [
'name' => 'like',
'email' => 'like',

View File

@ -24,6 +24,13 @@ Route::group([], function ()
Route::match(['get'], 'flights/{id}', 'FlightController@get');
Route::match(['get'], 'pirep/{id}', 'PirepController@get');
Route::match(['post'], 'pirep/prefile', 'PirepController@prefile');
Route::match(['get'], 'pirep/{id}/acars', 'PirepController@acars_get');
Route::match(['post'], 'pirep/{id}/acars', 'PirepController@acars_store');
Route::match(['get'], 'acars', 'AcarsController@index');
Route::match(['get'], 'acars/geojson', 'AcarsController@geojson');
# This is the info of the user whose token is in use
Route::match(['get'], 'user', 'UserController@index');

View File

@ -12,6 +12,7 @@ use App\Events\PirepFiled;
use App\Events\PirepRejected;
use App\Events\UserStatsChanged;
use App\Models\User;
use App\Repositories\PirepRepository;
use Log;
@ -48,10 +49,15 @@ class PIREPService extends BaseService
# Figure out what default state should be. Look at the default
# behavior from the rank that the pilot is assigned to
$default_state = PirepState::PENDING;
if($pirep->source === PirepSource::ACARS) {
$default_state = $pirep->pilot->rank->auto_approve_acars;
if($pirep->pilot->rank->auto_approve_acars) {
$default_state = PirepState::ACCEPTED;
}
} else {
$default_state = $pirep->pilot->rank->auto_approve_manual;
if($pirep->pilot->rank->auto_approve_manual) {
$default_state = PirepState::ACCEPTED;
}
}
$pirep->save();
@ -70,13 +76,10 @@ class PIREPService extends BaseService
event(new PirepFiled($pirep));
if ($default_state === PirepState::ACCEPTED) {
$pirep = $this->accept($pirep);
}
# only update the pilot last state if they are accepted
if ($default_state === PirepState::ACCEPTED) {
$this->setPilotState($pirep);
$pirep = $this->accept($pirep);
$this->setPilotState($pirep->pilot, $pirep);
}
return $pirep;
@ -151,7 +154,7 @@ class PIREPService extends BaseService
$pirep->save();
$pirep->refresh();
$this->setPilotState($pirep);
$this->setPilotState($pilot, $pirep);
Log::info('PIREP '.$pirep->id.' state change to ACCEPTED');
@ -193,9 +196,8 @@ class PIREPService extends BaseService
/**
* @param Pirep $pirep
*/
public function setPilotState(Pirep $pirep)
public function setPilotState(User $pilot, Pirep $pirep)
{
$pilot = $pirep->pilot;
$pilot->refresh();
$previous_airport = $pilot->curr_airport_id;
@ -203,6 +205,8 @@ class PIREPService extends BaseService
$pilot->last_pirep_id = $pirep->id;
$pilot->save();
$pirep->refresh();
event(new UserStatsChanged($pilot, 'airport', $previous_airport));
}
}

View File

@ -46,8 +46,9 @@
"tivie/php-os-detector": "1.1.0",
"santigarcor/laratrust": "5.0.3",
"pragmarx/version": "0.2.2",
"nabeel/vacentral": "dev-master",
"jmikola/geojson": "1.0.2"
"guzzlehttp/guzzle": "6.3.0",
"jmikola/geojson": "1.0.2",
"nabeel/vacentral": "dev-master"
},
"require-dev": {
"phpunit/phpunit": "6.4.0",

147
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"content-hash": "99850757675507f6225a5bbbe35734aa",
"content-hash": "df612ce1827421974b40979645d5debe",
"packages": [
{
"name": "composer/semver",
@ -427,7 +427,7 @@
"Doctrine\\DBAL\\": "lib/"
}
},
"notification-url": "http://packagist.org/downloads/",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
@ -750,7 +750,7 @@
"Egulias\\EmailValidator\\": "EmailValidator"
}
},
"notification-url": "http://packagist.org/downloads/",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
@ -796,7 +796,7 @@
"Parsedown": ""
}
},
"notification-url": "http://packagist.org/downloads/",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
@ -856,7 +856,7 @@
"GuzzleHttp\\": "src/"
}
},
"notification-url": "http://packagist.org/downloads/",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
@ -914,7 +914,7 @@
"src/functions_include.php"
]
},
"notification-url": "http://packagist.org/downloads/",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
@ -969,7 +969,7 @@
"src/functions_include.php"
]
},
"notification-url": "http://packagist.org/downloads/",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
@ -1031,7 +1031,7 @@
"Hashids\\": "src/"
}
},
"notification-url": "http://packagist.org/downloads/",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
@ -1138,7 +1138,7 @@
"InfyOm\\AdminLTETemplates\\": "src/"
}
},
"notification-url": "http://packagist.org/downloads/",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
@ -1157,7 +1157,7 @@
"laravel",
"templates"
],
"time": "2017-11-25 04:43:54"
"time": "2017-11-25T04:43:54+00:00"
},
{
"name": "infyomlabs/laravel-generator",
@ -1199,7 +1199,7 @@
"src/helpers.php"
]
},
"notification-url": "http://packagist.org/downloads/",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
@ -1223,7 +1223,7 @@
"test",
"view"
],
"time": "2017-11-25 05:18:22"
"time": "2017-11-25T05:18:22+00:00"
},
{
"name": "jackiedo/timezonelist",
@ -1249,7 +1249,7 @@
"Jackiedo\\Timezonelist\\": "src/Jackiedo/Timezonelist"
}
},
"notification-url": "http://packagist.org/downloads/",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
@ -1360,7 +1360,7 @@
"stubs/"
]
},
"notification-url": "http://packagist.org/downloads/",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
@ -1406,7 +1406,7 @@
"Traitor\\": "src/"
}
},
"notification-url": "http://packagist.org/downloads/",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
@ -1594,7 +1594,7 @@
"Illuminate\\": "src/Illuminate/"
}
},
"notification-url": "http://packagist.org/downloads/",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
@ -2017,7 +2017,7 @@
}
],
"description": "Bloom filter implementation",
"time": "2017-11-30 17:51:14"
"time": "2017-11-30T17:51:14+00:00"
},
{
"name": "monolog/monolog",
@ -2123,7 +2123,7 @@
"Cron\\": "src/Cron/"
}
},
"notification-url": "http://packagist.org/downloads/",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
@ -2172,7 +2172,7 @@
"src/DeepCopy/deep_copy.php"
]
},
"notification-url": "http://packagist.org/downloads/",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
@ -2217,11 +2217,11 @@
"VaCentral\\": "src/"
}
},
"notification-url": "http://packagist.org/downloads/",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"time": "2017-12-08 04:00:06"
"time": "2017-12-08T04:00:06+00:00"
},
{
"name": "nesbot/carbon",
@ -2311,7 +2311,7 @@
"PhpParser\\": "lib/PhpParser"
}
},
"notification-url": "http://packagist.org/downloads/",
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
@ -2744,7 +2744,7 @@
]
}
},
"notification-url": "http://packagist.org/downloads/",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
@ -2897,7 +2897,7 @@
"Prophecy\\": "src/"
}
},
"notification-url": "http://packagist.org/downloads/",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
@ -3015,7 +3015,7 @@
"src/"
]
},
"notification-url": "http://packagist.org/downloads/",
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
@ -3156,7 +3156,7 @@
"src/"
]
},
"notification-url": "http://packagist.org/downloads/",
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
@ -3237,7 +3237,7 @@
"src/"
]
},
"notification-url": "http://packagist.org/downloads/",
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
@ -3358,7 +3358,7 @@
"PragmaRX\\Version\\Tests\\": "tests/"
}
},
"notification-url": "http://packagist.org/downloads/",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
@ -3418,7 +3418,7 @@
"PragmaRX\\Yaml\\Tests\\": "tests/"
}
},
"notification-url": "http://packagist.org/downloads/",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
@ -3481,7 +3481,7 @@
"Prettus\\Repository\\": "src/Prettus/Repository/"
}
},
"notification-url": "http://packagist.org/downloads/",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
@ -3950,7 +3950,7 @@
"Laratrust\\": "src/"
}
},
"notification-url": "http://packagist.org/downloads/",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
@ -4122,16 +4122,16 @@
},
{
"name": "sebastian/comparator",
"version": "2.1.0",
"version": "2.1.1",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/comparator.git",
"reference": "1174d9018191e93cb9d719edec01257fc05f8158"
"reference": "b11c729f95109b56a0fe9650c6a63a0fcd8c439f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/1174d9018191e93cb9d719edec01257fc05f8158",
"reference": "1174d9018191e93cb9d719edec01257fc05f8158",
"url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/b11c729f95109b56a0fe9650c6a63a0fcd8c439f",
"reference": "b11c729f95109b56a0fe9650c6a63a0fcd8c439f",
"shasum": ""
},
"require": {
@ -4153,7 +4153,7 @@
"src/"
]
},
"notification-url": "http://packagist.org/downloads/",
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
@ -4182,7 +4182,7 @@
"compare",
"equality"
],
"time": "2017-11-03T07:16:52+00:00"
"time": "2017-12-22T14:50:35+00:00"
},
{
"name": "sebastian/diff",
@ -4728,7 +4728,7 @@
"src/helpers.php"
]
},
"notification-url": "http://packagist.org/downloads/",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
@ -4911,7 +4911,7 @@
"/Tests/"
]
},
"notification-url": "http://packagist.org/downloads/",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
@ -4960,7 +4960,7 @@
"/Tests/"
]
},
"notification-url": "http://packagist.org/downloads/",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
@ -5020,7 +5020,7 @@
"/Tests/"
]
},
"notification-url": "http://packagist.org/downloads/",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
@ -5076,7 +5076,7 @@
"/Tests/"
]
},
"notification-url": "http://packagist.org/downloads/",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
@ -5139,7 +5139,7 @@
"/Tests/"
]
},
"notification-url": "http://packagist.org/downloads/",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
@ -5188,7 +5188,7 @@
"/Tests/"
]
},
"notification-url": "http://packagist.org/downloads/",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
@ -5242,7 +5242,7 @@
"/Tests/"
]
},
"notification-url": "http://packagist.org/downloads/",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
@ -5330,7 +5330,7 @@
"/Tests/"
]
},
"notification-url": "http://packagist.org/downloads/",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
@ -5379,7 +5379,7 @@
"/Tests/"
]
},
"notification-url": "http://packagist.org/downloads/",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
@ -5531,7 +5531,7 @@
"bootstrap.php"
]
},
"notification-url": "http://packagist.org/downloads/",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
@ -5588,7 +5588,7 @@
"bootstrap.php"
]
},
"notification-url": "http://packagist.org/downloads/",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
@ -5647,7 +5647,7 @@
"Resources/stubs"
]
},
"notification-url": "http://packagist.org/downloads/",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
@ -5699,7 +5699,7 @@
"Symfony\\Polyfill\\Util\\": ""
}
},
"notification-url": "http://packagist.org/downloads/",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
@ -5754,7 +5754,7 @@
"/Tests/"
]
},
"notification-url": "http://packagist.org/downloads/",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
@ -5811,7 +5811,7 @@
"/Tests/"
]
},
"notification-url": "http://packagist.org/downloads/",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
@ -5894,7 +5894,7 @@
"/Tests/"
]
},
"notification-url": "http://packagist.org/downloads/",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
@ -5978,7 +5978,7 @@
"/Tests/"
]
},
"notification-url": "http://packagist.org/downloads/",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
@ -6046,7 +6046,7 @@
"/Tests/"
]
},
"notification-url": "http://packagist.org/downloads/",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
@ -6111,7 +6111,7 @@
"/Tests/"
]
},
"notification-url": "http://packagist.org/downloads/",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
@ -6173,7 +6173,7 @@
"/Tests/"
]
},
"notification-url": "http://packagist.org/downloads/",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
@ -6304,7 +6304,7 @@
"Tivie\\OS\\": "src/"
}
},
"notification-url": "http://packagist.org/downloads/",
"notification-url": "https://packagist.org/downloads/",
"license": [
"APACHE 2.0"
],
@ -6525,7 +6525,7 @@
"Webpatser\\Uuid": "src/"
}
},
"notification-url": "http://packagist.org/downloads/",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
@ -6654,7 +6654,7 @@
"src/helper.php"
]
},
"notification-url": "http://packagist.org/downloads/",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
@ -6711,7 +6711,7 @@
"Zend\\Diactoros\\": "src/"
}
},
"notification-url": "http://packagist.org/downloads/",
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-2-Clause"
],
@ -6940,7 +6940,7 @@
"Facebook\\WebDriver\\": "lib/"
}
},
"notification-url": "http://packagist.org/downloads/",
"notification-url": "https://packagist.org/downloads/",
"license": [
"Apache-2.0"
],
@ -6992,7 +6992,7 @@
"Whoops\\": "src/Whoops/"
}
},
"notification-url": "http://packagist.org/downloads/",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
@ -7153,7 +7153,7 @@
"Laravel\\Dusk\\": "src/"
}
},
"notification-url": "http://packagist.org/downloads/",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
@ -7294,16 +7294,16 @@
},
{
"name": "orchestra/testbench-core",
"version": "v3.5.4",
"version": "v3.5.5",
"source": {
"type": "git",
"url": "https://github.com/orchestral/testbench-core.git",
"reference": "afecbf0d68c43f0fd7ae53447bb28bcf08faf0ad"
"reference": "5fa8871651d054bd1f6eb23bb56c1ec6a5622078"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/orchestral/testbench-core/zipball/afecbf0d68c43f0fd7ae53447bb28bcf08faf0ad",
"reference": "afecbf0d68c43f0fd7ae53447bb28bcf08faf0ad",
"url": "https://api.github.com/repos/orchestral/testbench-core/zipball/5fa8871651d054bd1f6eb23bb56c1ec6a5622078",
"reference": "5fa8871651d054bd1f6eb23bb56c1ec6a5622078",
"shasum": ""
},
"require": {
@ -7312,15 +7312,16 @@
},
"require-dev": {
"laravel/framework": "~5.5.0",
"mockery/mockery": "^0.9.4",
"mockery/mockery": "~1.0",
"orchestra/database": "~3.5.0",
"phpunit/phpunit": "~6.0"
},
"suggest": {
"laravel/framework": "Required for testing (~5.5.0).",
"mockery/mockery": "Allow to use Mockery for testing (^0.9.4).",
"mockery/mockery": "Allow to use Mockery for testing (~1.0).",
"orchestra/database": "Allow to use --realpath migration for testing (~3.5).",
"orchestra/testbench-browser-kit": "Allow to use legacy BrowserKit for testing (~3.5).",
"orchestra/testbench-dusk": "Allow to use Laravel Dusk for testing (~3.5).",
"phpunit/phpunit": "Allow to use PHPUnit for testing (~6.0)."
},
"type": "library",
@ -7355,7 +7356,7 @@
"orchestral",
"testing"
],
"time": "2017-10-08T07:47:55+00:00"
"time": "2017-12-25T05:21:42+00:00"
},
{
"name": "symfony/class-loader",
@ -7395,7 +7396,7 @@
"/Tests/"
]
},
"notification-url": "http://packagist.org/downloads/",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],

2
storage/replay/.gitignore vendored Executable file
View File

@ -0,0 +1,2 @@
*
!.gitignore

95
tests/AcarsTest.php Normal file
View File

@ -0,0 +1,95 @@
<?php
use App\Models\Enums\PirepState;
use App\Models\Enums\PirepStatus;
/**
* Test API calls and authentication, etc
*/
class AcarsTest extends TestCase
{
public function setUp()
{
parent::setUp();
$this->addData('base');
}
protected function getPirep($pirep_id)
{
$resp = $this->withHeaders($this->apiHeaders())
->get('/api/pirep/' . $pirep_id);
$resp->assertStatus(200);
return $resp->json();
}
/**
* Post a PIREP into a PREFILE state and post ACARS
*/
public function testAcarsUpdates()
{
$airport = factory(App\Models\Airport::class)->create();
$airline = factory(App\Models\Airline::class)->create();
$aircraft = factory(App\Models\Aircraft::class)->create();
$uri = '/api/pirep/prefile';
$pirep = [
'airline_id' => $airline->id,
'aircraft_id' => $aircraft->id,
'dpt_airport' => $airport->icao,
'arr_airport' => $airport->icao,
'altitude' => 38000,
'planned_flight_time' => 120,
'route' => 'POINTA POINTB',
];
$response = $this->withHeaders($this->apiHeaders())->post($uri, $pirep);
$response->assertStatus(201);
# Get the PIREP ID
$pirep_id = $response->json()['id'];
$this->assertNotNull($pirep_id);
# Check the PIREP state and status
$pirep = $this->getPirep($pirep_id);
$this->assertEquals(PirepState::IN_PROGRESS, $pirep['state']);
$this->assertEquals(PirepStatus::PREFILE, $pirep['status']);
# Post an ACARS update
$uri = '/api/pirep/' . $pirep_id . '/acars';
$acars = factory(App\Models\Acars::class)->make()->toArray();
$response = $this->withHeaders($this->apiHeaders())->post($uri, $acars);
$response->assertStatus(201);
$body = $response->json();
$this->assertNotNull($body['id']);
$this->assertEquals($pirep_id, $body['pirep_id']);
# Make sure PIREP state moved into ENROUTE
$pirep = $this->getPirep($pirep_id);
$this->assertEquals(PirepState::IN_PROGRESS, $pirep['state']);
$this->assertEquals(PirepStatus::ENROUTE, $pirep['status']);
$uri = '/api/pirep/' . $pirep_id . '/acars';
$response = $this->withHeaders($this->apiHeaders())->get($uri);
$response->assertStatus(200);
$body = $response->json();
$this->assertEquals(1, $this->count($body));
$this->assertEquals($pirep_id, $body[0]['pirep_id']);
}
public function testNonExistentPirepGet()
{
$uri = '/api/pirep/DOESNTEXIST/acars';
$response = $this->withHeaders($this->apiHeaders())->get($uri);
$response->assertStatus(404);
}
public function testNonExistentPirepStore()
{
$uri = '/api/pirep/DOESNTEXIST/acars';
$acars = factory(App\Models\Acars::class)->make()->toArray();
$response = $this->withHeaders($this->apiHeaders())->post($uri, $acars);
$response->assertStatus(404);
}
}

View File

@ -65,6 +65,7 @@ class PIREPTest extends TestCase
# Submit two PIREPs
$pireps = factory(Pirep::class, 2)->create([
'airline_id' => 1,
'user_id' => 1,
# 360min == 6 hours, rank should bump up
'flight_time' => 360,
@ -87,6 +88,7 @@ class PIREPTest extends TestCase
# it should automatically be accepted
#
$pirep = factory(Pirep::class)->create([
'airline_id' => 1,
'user_id' => 1,
# 120min == 2 hours, currently at 9 hours
# Rank bumps up at 10 hours