api fixes/updates

This commit is contained in:
Nabeel Shahzad 2018-05-01 20:58:05 -05:00
parent 50abda71cb
commit b96f1cd7c4
17 changed files with 1282 additions and 1604 deletions

View File

@ -29,6 +29,7 @@ class CreateAcarsTables extends Migration
$table->unsignedInteger('gs')->nullable();
$table->unsignedInteger('transponder')->nullable();
$table->string('autopilot')->nullable();
$table->decimal('fuel')->nullable();
$table->decimal('fuel_flow')->nullable();
$table->string('sim_time')->nullable();

File diff suppressed because it is too large Load Diff

View File

@ -240,7 +240,8 @@ class PirepController extends Controller
*/
public function update($id, UpdateRequest $request)
{
Log::info('PIREP Update, user '.Auth::id(), $request->post());
Log::info('PIREP Update, user '.Auth::id());
Log::info($request->getContent());
$user = Auth::user();
$pirep = Pirep::find($id);

View File

@ -26,12 +26,13 @@ class PositionRequest extends FormRequest
'positions.*.lat' => 'required|numeric',
'positions.*.lon' => 'required|numeric',
'positions.*.altitude' => 'nullable|numeric',
'positions.*.heading' => 'nullable|integer|between:0,360',
'positions.*.heading' => 'nullable|numeric|between:0,360',
'positions.*.vs' => 'nullable',
'positions.*.gs' => 'nullable',
'positions.*.transponder' => 'nullable',
'positions.*.autopilot' => 'nullable',
'positions.*.fuel_flow' => 'nullable',
'positions.*.fuel' => 'nullable|numeric',
'positions.*.fuel_flow' => 'nullable|numeric',
'positions.*.log' => 'nullable',
'positions.*.created_at' => 'nullable|date',
];

View File

@ -43,9 +43,9 @@ class UpdateRequest extends FormRequest
'notes' => 'nullable',
'source_name' => 'nullable|max:25',
'landing_rate' => 'nullable|numeric',
'block_off_time' => 'nullable|date',
'block_on_time' => 'nullable|date',
'created_at' => 'nullable|date',
'block_off_time' => 'nullable',
'block_on_time' => 'nullable',
'created_at' => 'nullable',
'status' => 'nullable',
'score' => 'nullable|integer',

View File

@ -33,6 +33,8 @@ use PhpUnitsOfMeasure\Exception\NonStringUnitName;
* @property integer block_time
* @property integer flight_time In minutes
* @property integer planned_flight_time
* @property mixed planned_distance
* @property mixed distance
* @property integer score
* @property User user
* @property Flight|null flight
@ -105,11 +107,20 @@ class Pirep extends Model
'score' => 'integer',
'source' => 'integer',
'state' => 'integer',
'block_off_time' => 'datetime',
'block_on_time' => 'datetime',
'submitted_at' => 'datetime',
#'block_off_time' => 'datetime',
#'block_on_time' => 'datetime',
#'submitted_at' => 'datetime',
];
/*protected $dates = [
'block_off_time',
'block_on_time',
'submitted_at',
'created_at',
'updated_at',
'deleted_at'
];*/
public static $rules = [
'airline_id' => 'required|exists:airlines,id',
'aircraft_id' => 'required|exists:aircraft,id',
@ -140,6 +151,24 @@ class Pirep extends Model
return $flight_id;
}
/**
* Return the block off time in carbon format
* @return Carbon
*/
public function getBlockOffTimeAttribute()
{
return new Carbon($this->attributes['block_off_time']);
}
/**
* Return the block on time
* @return Carbon
*/
public function getBlockOnTimeAttribute()
{
return new Carbon($this->attributes['block_on_time']);
}
/**
* Return a new Length unit so conversions can be made
* @return int|Distance
@ -236,16 +265,16 @@ class Pirep extends Model
*/
public function getProgressPercentAttribute()
{
$upper_bound = $this->flight_time;
if($this->planned_flight_time) {
$upper_bound = $this->planned_flight_time;
$upper_bound = $this->distance['nmi'];
if($this->planned_distance) {
$upper_bound = $this->planned_distance['nmi'];
}
if(!$upper_bound) {
$upper_bound = 1;
}
return round(($this->flight_time / $upper_bound) * 100, 0);
return round(($this->distance['nmi'] / $upper_bound) * 100, 0);
}
/**

View File

@ -18,6 +18,7 @@ use App\Models\Setting;
use App\Models\Subfleet;
use App\Repositories\SettingRepository;
use App\Services\ModuleService;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\ServiceProvider;
use View;
@ -27,6 +28,11 @@ class AppServiceProvider extends ServiceProvider
public function boot(): void
{
Schema::defaultStringLength(191);
Carbon::serializeUsing(function ($carbon) {
return $carbon->format('U');
});
$this->app->bind('setting', SettingRepository::class);
View::share('moduleSvc', app(ModuleService::class));
@ -39,6 +45,7 @@ class AppServiceProvider extends ServiceProvider
PirepField::observe(PirepFieldObserver::class);
Setting::observe(SettingObserver::class);
Subfleet::observe(SubfleetObserver::class);
}
/**

View File

@ -10838,246 +10838,6 @@ button.close {
.select2-container--classic.select2-container--open .select2-dropdown {
border-color: #5897fb; }
@charset "UTF-8";
/*!
* Pikaday
* Copyright © 2014 David Bushell | BSD & MIT license | http://dbushell.com/
*/
.pika-single {
z-index: 9999;
display: block;
position: relative;
color: #333;
background: #fff;
border: 1px solid #ccc;
border-bottom-color: #bbb;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
}
/*
clear child float (pika-lendar), using the famous micro clearfix hack
http://nicolasgallagher.com/micro-clearfix-hack/
*/
.pika-single:before,
.pika-single:after {
content: " ";
display: table;
}
.pika-single:after { clear: both }
.pika-single { *zoom: 1 }
.pika-single.is-hidden {
display: none;
}
.pika-single.is-bound {
position: absolute;
box-shadow: 0 5px 15px -5px rgba(0,0,0,.5);
}
.pika-lendar {
float: left;
width: 240px;
margin: 8px;
}
.pika-title {
position: relative;
text-align: center;
}
.pika-label {
display: inline-block;
*display: inline;
position: relative;
z-index: 9999;
overflow: hidden;
margin: 0;
padding: 5px 3px;
font-size: 14px;
line-height: 20px;
font-weight: bold;
background-color: #fff;
}
.pika-title select {
cursor: pointer;
position: absolute;
z-index: 9998;
margin: 0;
left: 0;
top: 5px;
filter: alpha(opacity=0);
opacity: 0;
}
.pika-prev,
.pika-next {
display: block;
cursor: pointer;
position: relative;
outline: none;
border: 0;
padding: 0;
width: 20px;
height: 30px;
/* hide text using text-indent trick, using width value (it's enough) */
text-indent: 20px;
white-space: nowrap;
overflow: hidden;
background-color: transparent;
background-position: center center;
background-repeat: no-repeat;
background-size: 75% 75%;
opacity: .5;
*position: absolute;
*top: 0;
}
.pika-prev:hover,
.pika-next:hover {
opacity: 1;
}
.pika-prev,
.is-rtl .pika-next {
float: left;
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAYAAAAsEj5rAAAAUklEQVR42u3VMQoAIBADQf8Pgj+OD9hG2CtONJB2ymQkKe0HbwAP0xucDiQWARITIDEBEnMgMQ8S8+AqBIl6kKgHiXqQqAeJepBo/z38J/U0uAHlaBkBl9I4GwAAAABJRU5ErkJggg==');
*left: 0;
}
.pika-next,
.is-rtl .pika-prev {
float: right;
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAYAAAAsEj5rAAAAU0lEQVR42u3VOwoAMAgE0dwfAnNjU26bYkBCFGwfiL9VVWoO+BJ4Gf3gtsEKKoFBNTCoCAYVwaAiGNQGMUHMkjGbgjk2mIONuXo0nC8XnCf1JXgArVIZAQh5TKYAAAAASUVORK5CYII=');
*right: 0;
}
.pika-prev.is-disabled,
.pika-next.is-disabled {
cursor: default;
opacity: .2;
}
.pika-select {
display: inline-block;
*display: inline;
}
.pika-table {
width: 100%;
border-collapse: collapse;
border-spacing: 0;
border: 0;
}
.pika-table th,
.pika-table td {
width: 14.285714285714286%;
padding: 0;
}
.pika-table th {
color: #999;
font-size: 12px;
line-height: 25px;
font-weight: bold;
text-align: center;
}
.pika-button {
cursor: pointer;
display: block;
box-sizing: border-box;
-moz-box-sizing: border-box;
outline: none;
border: 0;
margin: 0;
width: 100%;
padding: 5px;
color: #666;
font-size: 12px;
line-height: 15px;
text-align: right;
background: #f5f5f5;
}
.pika-week {
font-size: 11px;
color: #999;
}
.is-today .pika-button {
color: #33aaff;
font-weight: bold;
}
.is-selected .pika-button,
.has-event .pika-button {
color: #fff;
font-weight: bold;
background: #33aaff;
box-shadow: inset 0 1px 3px #178fe5;
border-radius: 3px;
}
.has-event .pika-button {
background: #005da9;
box-shadow: inset 0 1px 3px #0076c9;
}
.is-disabled .pika-button,
.is-inrange .pika-button {
background: #D5E9F7;
}
.is-startrange .pika-button {
color: #fff;
background: #6CB31D;
box-shadow: none;
border-radius: 3px;
}
.is-endrange .pika-button {
color: #fff;
background: #33aaff;
box-shadow: none;
border-radius: 3px;
}
.is-disabled .pika-button {
pointer-events: none;
cursor: default;
color: #999;
opacity: .3;
}
.is-outside-current-month .pika-button {
color: #999;
opacity: .3;
}
.is-selection-disabled {
pointer-events: none;
cursor: default;
}
.pika-button:hover,
.pika-row.pick-whole-week:hover .pika-button {
color: #fff;
background: #ff8000;
box-shadow: none;
border-radius: 3px;
}
/* styling for abbr */
.pika-table abbr {
border-bottom: none;
cursor: help;
}
/*! X-editable - v1.5.1
* In-place editing with Twitter Bootstrap, jQuery UI or pure jQuery
* http://github.com/vitalets/x-editable

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -1,16 +1,16 @@
{
"/assets/frontend/js/app.js": "/assets/frontend/js/app.js?id=3d5b0dff38050f6b5e0b",
"/assets/frontend/js/app.js": "/assets/frontend/js/app.js?id=cd886ba18357a1c96ec0",
"/assets/frontend/css/now-ui-kit.css": "/assets/frontend/css/now-ui-kit.css?id=b0a0f05b94a4486db4f2",
"/assets/admin/css/vendor.min.css": "/assets/admin/css/vendor.min.css?id=cc80aec3cf1646f83d8d",
"/assets/admin/js/app.js": "/assets/admin/js/app.js?id=926a7c24ec36533523e3",
"/assets/admin/js/app.js": "/assets/admin/js/app.js?id=559d5e8e5d6ecc64a3af",
"/assets/installer/js/app.js": "/assets/installer/js/app.js?id=c65781eda730445d666e",
"/assets/fonts/glyphicons-halflings-regular.woff2": "/assets/fonts/glyphicons-halflings-regular.woff2?id=b5b5055c6d812c0f9f0d",
"/assets/admin/fonts/glyphicons-halflings-regular.woff2": "/assets/admin/fonts/glyphicons-halflings-regular.woff2?id=b5b5055c6d812c0f9f0d",
"/assets/admin/img/clear.png": "/assets/admin/img/clear.png?id=0e92f4c3efc6988a3c96",
"/assets/admin/img/loading.gif": "/assets/admin/img/loading.gif?id=90a4b76b4f11558691f6",
"/assets/global/js/jquery.js": "/assets/global/js/jquery.js?id=6a07da9fae934baf3f74",
"/assets/admin/css/vendor.css": "/assets/admin/css/vendor.css?id=99aedbd62dfa118e7b73",
"/assets/admin/js/vendor.js": "/assets/admin/js/vendor.js?id=5130233c88c71fc60135",
"/assets/admin/css/vendor.css": "/assets/admin/css/vendor.css?id=d4c03403265f42272050",
"/assets/admin/js/vendor.js": "/assets/admin/js/vendor.js?id=aa7db4bedfe23409f625",
"/assets/admin/css/blue.png": "/assets/admin/css/blue.png?id=753a3c0dec86d3a38d9c",
"/assets/admin/css/blue@2x.png": "/assets/admin/css/blue@2x.png?id=97da23d47b838cbd4bef",
"/assets/global/js/vendor.js": "/assets/global/js/vendor.js?id=6436d215691e8f38eb12",

View File

@ -1,14 +1,9 @@
const leaflet = require('leaflet');
import draw_base_map from './base_map'
import { addWMSLayer } from './helpers';
import {addWMSLayer} from './helpers';
import {
ACTUAL_ROUTE_COLOR,
PLAN_ROUTE_COLOR,
CIRCLE_COLOR
} from './config'
import {ACTUAL_ROUTE_COLOR, CIRCLE_COLOR, PLAN_ROUTE_COLOR} from './config'
/**
* Show some popup text when a feature is clicked on
@ -16,12 +11,12 @@ import {
* @param layer
*/
export const onFeaturePointClick = (feature, layer) => {
let popup_html = '';
if (feature.properties && feature.properties.popup) {
popup_html += feature.properties.popup
}
let popup_html = '';
if (feature.properties && feature.properties.popup) {
popup_html += feature.properties.popup
}
layer.bindPopup(popup_html)
layer.bindPopup(popup_html)
};
/**
@ -31,14 +26,14 @@ export const onFeaturePointClick = (feature, layer) => {
* @returns {*}
*/
export const pointToLayer = (feature, latlng) => {
return leaflet.circleMarker(latlng, {
radius: 5,
fillColor: CIRCLE_COLOR,
color: '#000',
weight: 1,
opacity: 1,
fillOpacity: 0.8
})
return leaflet.circleMarker(latlng, {
radius: 5,
fillColor: CIRCLE_COLOR,
color: '#000',
weight: 1,
opacity: 1,
fillOpacity: 0.8
})
}
/**
@ -48,90 +43,125 @@ export const pointToLayer = (feature, latlng) => {
*/
export default (opts) => {
opts = Object.assign({
route_points: null,
planned_route_line: null,
actual_route_points: null,
actual_route_line: null,
render_elem: 'map',
metar_wms: {
url: '',
params: {}
},
}, opts);
opts = Object.assign({
console.log(opts);
route_points: null,
planned_route_line: null,
actual_route_points: null,
actual_route_line: null,
render_elem: 'map',
live_map: false,
aircraft_icon: '/assets/img/acars/aircraft.png',
metar_wms: {
url: '',
params: {}
},
}, opts);
let map = draw_base_map(opts);
if (opts.metar_wms.url !== '') {
addWMSLayer(map, opts.metar_wms);
}
let geodesicLayer = leaflet.geodesic([], {
weight: 4,
opacity: 0.9,
color: PLAN_ROUTE_COLOR,
steps: 50,
wrap: false,
}).addTo(map);
geodesicLayer.geoJson(opts.planned_route_line);
try {
map.fitBounds(geodesicLayer.getBounds())
} catch (e) {
console.log(e)
}
// Draw the route points after
if (opts.route_points !== null) {
let route_points = leaflet.geoJSON(opts.route_points, {
onEachFeature: onFeaturePointClick,
pointToLayer: pointToLayer,
style: {
'color': PLAN_ROUTE_COLOR,
'weight': 3,
'opacity': 0.65,
},
const aircraftIcon = leaflet.icon({
iconUrl: opts.aircraft_icon,
iconSize: [42, 42],
iconAnchor: [21, 21],
});
route_points.addTo(map);
}
let map = draw_base_map(opts);
let layerLiveFlight;
/**
* draw the actual route
*/
if (opts.metar_wms.url !== '') {
addWMSLayer(map, opts.metar_wms);
}
if (opts.actual_route_line !== null && opts.actual_route_line.features.length > 0) {
let geodesicLayer = leaflet.geodesic([], {
weight: 3,
opacity: 0.9,
color: ACTUAL_ROUTE_COLOR,
steps: 50,
wrap: false,
weight: 4,
opacity: 0.9,
color: PLAN_ROUTE_COLOR,
steps: 50,
wrap: false,
}).addTo(map);
geodesicLayer.geoJson(opts.actual_route_line);
geodesicLayer.geoJson(opts.planned_route_line);
try {
map.fitBounds(geodesicLayer.getBounds())
map.fitBounds(geodesicLayer.getBounds())
} catch (e) {
console.log(e)
console.log(e)
}
}
if (opts.actual_route_points !== null && opts.actual_route_points.features.length > 0) {
let route_points = leaflet.geoJSON(opts.actual_route_points, {
onEachFeature: onFeaturePointClick,
pointToLayer: pointToLayer,
style: {
'color': ACTUAL_ROUTE_COLOR,
'weight': 3,
'opacity': 0.65,
},
});
// Draw the route points after
if (opts.route_points !== null) {
let route_points = leaflet.geoJSON(opts.route_points, {
onEachFeature: onFeaturePointClick,
pointToLayer: pointToLayer,
style: {
'color': PLAN_ROUTE_COLOR,
'weight': 3,
'opacity': 0.65,
},
});
route_points.addTo(map)
}
route_points.addTo(map);
}
/**
* draw the actual route
*/
if (opts.actual_route_line !== null && opts.actual_route_line.features.length > 0) {
let geodesicLayer = leaflet.geodesic([], {
weight: 3,
opacity: 0.9,
color: ACTUAL_ROUTE_COLOR,
steps: 50,
wrap: false,
}).addTo(map);
geodesicLayer.geoJson(opts.actual_route_line);
try {
map.fitBounds(geodesicLayer.getBounds())
} catch (e) {
console.log(e)
}
}
if (opts.actual_route_points !== null && opts.actual_route_points.features.length > 0) {
let route_points = leaflet.geoJSON(opts.actual_route_points, {
onEachFeature: onFeaturePointClick,
pointToLayer: pointToLayer,
style: {
'color': ACTUAL_ROUTE_COLOR,
'weight': 3,
'opacity': 0.65,
},
});
route_points.addTo(map)
}
/**
*
*/
const liveFlight = () => {
const uri = opts.pirep_uri;
const live_route = $.ajax({
url: uri,
dataType: 'json',
error: console.log
});
$.when(live_route).done((routeJson) => {
layerLiveFlight = leaflet.geoJSON(routeJson, {
pointToLayer: function (feature, latlon) {
return leaflet.marker(latlon, {
icon: aircraftIcon,
rotationAngle: feature.properties.heading
})
}
});
layerLiveFlight.addTo(map)
});
};
setInterval(liveFlight, 10000);
};

View File

@ -9,10 +9,12 @@
@section('scripts')
<script type="text/javascript">
phpvms.map.render_route_map({
pirep_uri: '{!! url('/api/pireps/'.$pirep->id.'/acars/geojson') !!}',
route_points: {!! json_encode($map_features['planned_rte_points']) !!},
planned_route_line: {!! json_encode($map_features['planned_rte_line']) !!},
actual_route_line: {!! json_encode($map_features['actual_route_line']) !!},
actual_route_points: {!! json_encode($map_features['actual_route_points']) !!},
aircraft_icon: '{!! public_asset('/assets/img/acars/aircraft.png') !!}',
});
</script>
@endsection

View File

@ -9,7 +9,13 @@
<div class="col-12">
<p>
<h2 style="margin-bottom: 5px;">{{$pirep->airline->code}}{{ $pirep->ident }}</h2>
<p>Arrived {{$pirep->created_at->diffForHumans()}}</p>
<p>
@if($pirep->state === PirepState::IN_PROGRESS)
@else
Arrived {{$pirep->created_at->diffForHumans()}}
@endif
</p>
</p>
</div>
@ -55,8 +61,10 @@
<div class="progress" style="margin: 20px 0;">
<div class="progress-bar progress-bar-success" role="progressbar"
aria-valuenow="40" aria-valuemin="0" aria-valuemax="100"
style="width: {{$pirep->progress_percent}}%">
{{ Utils::minutesToTimeString($pirep->flight_time) }}
style="width: {{$pirep->progress_percent}}%;">
{{--<p style="padding: 10px">
{{ Utils::minutesToTimeString($pirep->flight_time) }}
</p>--}}
</div>
</div>
</div>
@ -78,8 +86,8 @@
<div class="col-4">
<h2>&nbsp;</h2>
<table class="table table-hover table-condensed">
<tr>
<table class="table table-striped">
{{--<tr>
<td width="30%">Status</td>
<td>
@php
@ -96,6 +104,15 @@
{{ PirepState::label($pirep->state) }}
</div>
</td>
</tr>--}}
<tr>
<td width="30%">State</td>
<td>
<div class="badge badge-info">
{{ PirepStatus::label($pirep->status) }}
</div>
</td>
</tr>
<tr>

View File

@ -16,9 +16,10 @@
<table class="table">
@foreach($pireps as $pirep)
<tr>
<td>{{ $pirep->ident }}</td>
<td>{{ $pirep->airline->code }}{{ $pirep->ident }}</td>
<td>{{ $pirep->dpt_airport_id }}</td>
<td>{{ $pirep->arr_airport_id }}</td>
<td>{{ $pirep->aircraft->name }}</td>
<td>
{{ PirepStatus::label($pirep->status) }}
</td>

View File

@ -9,8 +9,8 @@ class UtilsTest extends TestCase
public function testDates()
{
$carbon = new \Carbon\Carbon('2018-03-18 00:20:43');
//echo $carbon;
$carbon = new \Carbon\Carbon('2018-04-28T12:55:40Z');
$this->assertNotNull($carbon);
}
public function testSecondsToTimeParts()