From 547b39511cd1bec34398940cbbac9dc9c8f677b4 Mon Sep 17 00:00:00 2001 From: kjporter Date: Fri, 28 Nov 2025 10:10:13 -0500 Subject: [PATCH 01/12] Add Preferred Routes --- app/Console/Commands/UpdateFAA.php | 149 ++++++++++++++++++ app/PreferredRoute.php | 31 ++++ ...5_11_25_083600_create_prd_routes_table.php | 38 +++++ 3 files changed, 218 insertions(+) create mode 100644 app/Console/Commands/UpdateFAA.php create mode 100644 app/PreferredRoute.php create mode 100644 database/migrations/2025_11_25_083600_create_prd_routes_table.php diff --git a/app/Console/Commands/UpdateFAA.php b/app/Console/Commands/UpdateFAA.php new file mode 100644 index 000000000..5420b5b6b --- /dev/null +++ b/app/Console/Commands/UpdateFAA.php @@ -0,0 +1,149 @@ + 'PRD', + 'model' => 'App\PreferredRoute', + 'file' => 'prefroutes_db.csv' + ] + ]; + private ProgressIndicator $progressIndicator; + private $path; + + /** + * Execute the console command. + */ + public function handle() { + $this->info('Updating FAA-specific datafiles'); + $this->path = Storage::disk('local')->path('private/'); + $this->setup_progress_indicator(); + $this->update_routes(); + } + + private function update_routes(): void { + foreach ($this->routes as $route) { + $route = (object) $route; + $this->progressIndicator->start("Fetching $route->name info from FAA server..."); + $this->call_api(SELF::FAA_DATA_URL . $route->file, $this->path, strtolower($route->name) . '_new.csv'); + $this->progressIndicator->finish("Fetching $route->name info from FAA server... done"); + + // Update table + $this->progressIndicator->start("Updating $route->name table..."); + if (($handle = fopen($this->path . strtolower($route->name) . '_new.csv', "r")) !== false) { + $route->model::truncate(); + while (($rte_line = fgetcsv($handle, null, ",")) !== false) { + if (!is_array($rte_line)) { + continue; + } + $bom = "\xef\xbb\xbf"; + if (substr($rte_line[0], 0, 3) === $bom) { + $rte_line[0] = substr($rte_line[0], 3); + } + if ($rte_line[0] == 'RCode' || $rte_line[0] == 'Orig') { // It's the header line, skip it + continue; + } + if ($route->name == 'CDR') { + $load_arr = [ + 'route_code' => $rte_line[0], + 'origin' => $rte_line[1], + 'destination' => $rte_line[2], + 'departure_fix' => substr($rte_line[3], 0, 5), // Enforce 5 characters + 'route_string' => $rte_line[4], + 'departure_center' => $rte_line[5], + 'arrival_center' => $rte_line[6], + 'transit_centers' => $rte_line[7], + 'coordination_required' => $rte_line[8], + 'play' => $rte_line[9], + 'navigation_equipment' => $rte_line[10] + ]; + } else { + $load_arr = [ + 'orig' => $rte_line[0], + 'route_string' => $rte_line[1], + 'dest' => $rte_line[2], + 'hours1' => $rte_line[3], + 'hours2' => $rte_line[4], + 'hours3' => $rte_line[5], + 'type' => $rte_line[6], + 'area' => $rte_line[7], + 'altitude' => (is_numeric($rte_line[8])) ? $rte_line[8] : null, + 'aircraft' => $rte_line[9], + 'direction' => $rte_line[10], + 'seq' => $rte_line[11], + 'dcntr' => $rte_line[12], + 'acntr' => $rte_line[13] + ]; + } + $route->model::create($load_arr); + $this->progressIndicator->advance(); + } + fclose($handle); + } + $this->progressIndicator->finish("Updating $route->name table... done"); + + // Remove, rename file + $this->info("Removing unused local $route->name files"); + if (file_exists($this->path . $route->name . '.csv')) { + unlink($this->path . $route->name . '.csv'); + $this->info("- Deleted $route->name.csv"); + } + if (file_exists($this->path . $route->name . '_new.csv')) { + rename($this->path . $route->name . '_new.csv', $this->path . $route->name . '.csv'); + } + $this->info("$route->name file cleanup complete"); + } + } + + private function call_api($url, $path = null, $write_file = null): string|null { + $progress = $this->progressIndicator; + $write_to_filename = (is_null($write_file)) ? basename($url) : $write_file; + $client = new Client(); + $response = $client->get($url, [ + 'config' => [ + 'curl' => [ + 'CURLOPT_RETURNTRANSFER' => true, + 'CURLOPT_CONNECTTIMEOUT' => 3, + 'CURLOPT_ENCODING' => 'gzip', + 'CURLOPT_SSL_VERIFYPEER' => false + ] + ], + 'progress' => function () use ($progress) { + $progress->advance(); + }, + 'sink' => $path . $write_to_filename + ]); + if ($response->getStatusCode() == 200) { + return $response->getBody(); + } + return null; + } + + private function setup_progress_indicator(): void { + $output = new ConsoleOutput; + $this->progressIndicator = new ProgressIndicator($output); + } +} diff --git a/app/PreferredRoute.php b/app/PreferredRoute.php new file mode 100644 index 000000000..9fccf98d4 --- /dev/null +++ b/app/PreferredRoute.php @@ -0,0 +1,31 @@ +where('dest', substr($arrival, 1))->where(function ($query) use ($ac_type) { + $query->where('aircraft', 'LIKE', '%' . strtoupper($ac_type) . '%')->orWhere('aircraft', ''); + })->first(); + if (!$routes) { + return ''; + } + /* + PRDs normally contain the origin and destination ID as the first and last points. + SimBrief doesn't like this. Remove. + */ + $route = explode(' ', $routes->route_string); + if ($route[0] == substr($departure, 1)) { + unset($route[0]); + } + if (end($route) == substr($arrival, 1)) { + array_pop($route); + } + return implode(' ', $route); + } +} diff --git a/database/migrations/2025_11_25_083600_create_prd_routes_table.php b/database/migrations/2025_11_25_083600_create_prd_routes_table.php new file mode 100644 index 000000000..a6418e86a --- /dev/null +++ b/database/migrations/2025_11_25_083600_create_prd_routes_table.php @@ -0,0 +1,38 @@ +id(); + $table->string('orig', length: 5); + $table->text('route_string'); + $table->string('dest', length: 5); + $table->string('hours1', length: 50); + $table->string('hours2', length: 50); + $table->string('hours3', length: 50); + $table->string('type', length: 4); + $table->string('area', length: 100); + $table->integer('altitude')->nullable(); + $table->string('aircraft', length: 255); + $table->string('direction', length: 255); + $table->integer('seq'); + $table->string('dcntr', length: 4); + $table->string('acntr', length: 4); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void { + Schema::dropIfExists('prd_route'); + } +}; From 680a2dec2755c2009928b43834d39713f4839e58 Mon Sep 17 00:00:00 2001 From: kjporter Date: Fri, 28 Nov 2025 10:10:36 -0500 Subject: [PATCH 02/12] Add Aircraft Types --- app/Aircraft.php | 10 + ...025_11_24_145400_create_aircraft_table.php | 34 ++ database/seeders/AircraftSeeder.php | 488 ++++++++++++++++++ 3 files changed, 532 insertions(+) create mode 100644 app/Aircraft.php create mode 100644 database/migrations/2025_11_24_145400_create_aircraft_table.php create mode 100644 database/seeders/AircraftSeeder.php diff --git a/app/Aircraft.php b/app/Aircraft.php new file mode 100644 index 000000000..e93d0d043 --- /dev/null +++ b/app/Aircraft.php @@ -0,0 +1,10 @@ +id(); + $table->string('ac_type')->unique(); + $table->enum('icao_wtc', ['L', 'M', 'H', 'J'])->nullable(); + $table->string('equipment')->nullable(); + $table->string('transponder')->nullable(); + $table->enum('perf_cat', ['A', 'B', 'C', 'D', 'E', 'H'])->nullable(); + $table->string('pbn')->nullable(); + }); + + Artisan::call('db:seed', [ + '--class' => 'AircraftSeeder', + ]); + } + + /** + * Reverse the migrations. + */ + public function down(): void { + Schema::dropIfExists('aircraft'); + } +}; diff --git a/database/seeders/AircraftSeeder.php b/database/seeders/AircraftSeeder.php new file mode 100644 index 000000000..68a0eadd1 --- /dev/null +++ b/database/seeders/AircraftSeeder.php @@ -0,0 +1,488 @@ +insert([ + 'ac_type' => 'A19N', + 'icao_wtc' => 'M', + 'equipment' => 'SDE3FGHIRWY', + 'transponder' => 'LB1', + 'perf_cat' => 'C', + 'pbn' => 'A1B1C1D1O1S1' + ]); + DB::table('aircraft')->insert([ + 'ac_type' => 'A20N', + 'icao_wtc' => 'M', + 'equipment' => 'SDE2E3FGHIJ1RWXY', + 'transponder' => 'LB1', + 'perf_cat' => 'C', + 'pbn' => 'A1B1C1D1O1S2' + ]); + DB::table('aircraft')->insert([ + 'ac_type' => 'A21N', + 'icao_wtc' => 'M', + 'equipment' => 'SDE3FGHIRWY', + 'transponder' => 'LB1', + 'perf_cat' => 'C', + 'pbn' => 'A1B1C1D1O1S1' + ]); + DB::table('aircraft')->insert([ + 'ac_type' => 'A306', + 'icao_wtc' => 'H', + 'equipment' => 'SDE3FGHIRWY', + 'transponder' => 'LB1', + 'perf_cat' => 'D', + 'pbn' => 'A1B1C1D1L1O1S1' + ]); + DB::table('aircraft')->insert([ + 'ac_type' => 'A30B', + 'icao_wtc' => 'H', + 'equipment' => 'SDE3FGHIRWY', + 'transponder' => 'LB1', + 'perf_cat' => 'D', + 'pbn' => 'A1B1C1D1L1O1S1' + ]); + DB::table('aircraft')->insert([ + 'ac_type' => 'A310', + 'icao_wtc' => 'H', + 'equipment' => 'SDFGHIM1RTUWXYZ', + 'transponder' => 'LB1', + 'perf_cat' => 'D', + 'pbn' => 'A1B1C1D1L1O1S2' + ]); + DB::table('aircraft')->insert([ + 'ac_type' => 'A318', + 'icao_wtc' => 'M', + 'equipment' => 'SDE3FGHIRWY', + 'transponder' => 'LB1', + 'perf_cat' => 'C', + 'pbn' => 'A1B1C1D1O1S1' + ]); + DB::table('aircraft')->insert([ + 'ac_type' => 'A319', + 'icao_wtc' => 'M', + 'equipment' => 'SDE3FGHIRWY', + 'transponder' => 'LB1', + 'perf_cat' => 'C', + 'pbn' => 'A1B1C1D1O1S1' + ]); + DB::table('aircraft')->insert([ + 'ac_type' => 'A320', + 'icao_wtc' => 'M', + 'equipment' => 'SDE3FGHIRWY', + 'transponder' => 'LB1', + 'perf_cat' => 'C', + 'pbn' => 'A1B1C1D1O1S1' + ]); + DB::table('aircraft')->insert([ + 'ac_type' => 'A321', + 'icao_wtc' => 'M', + 'equipment' => 'SDE3FGHIRWY', + 'transponder' => 'LB1', + 'perf_cat' => 'C', + 'pbn' => 'A1B1C1D1O1S1' + ]); + DB::table('aircraft')->insert([ + 'ac_type' => 'A332', + 'icao_wtc' => 'H', + 'equipment' => 'SDE3GHIJ2J3J5M1RVWXYZ', + 'transponder' => 'LB2D1', + 'perf_cat' => 'D', + 'pbn' => 'A1B1C1D1O1S2' + ]); + DB::table('aircraft')->insert([ + 'ac_type' => 'A333', + 'icao_wtc' => 'H', + 'equipment' => 'SDE3GHIJ2J3J5M1RVWXYZ', + 'transponder' => 'LB2D1', + 'perf_cat' => 'D', + 'pbn' => 'A1B1C1D1O1S2' + ]); + DB::table('aircraft')->insert([ + 'ac_type' => 'A338', + 'icao_wtc' => 'H', + 'equipment' => 'SADE1E2E3GHIJ1J3J5M1P2RWXY', + 'transponder' => 'LB2D1', + 'perf_cat' => 'D', + 'pbn' => 'A1B1C1D1L1O1S2' + ]); + DB::table('aircraft')->insert([ + 'ac_type' => 'A339', + 'icao_wtc' => 'H', + 'equipment' => 'SADE1E2E3GHIJ1J3J5M1P2RWXY', + 'transponder' => 'LB2D1', + 'perf_cat' => 'D', + 'pbn' => 'A1B1C1D1L1O1S2' + ]); + DB::table('aircraft')->insert([ + 'ac_type' => 'A342', + 'icao_wtc' => 'H', + 'equipment' => 'SDE2E3FGHIJ4J5M1RWXYZ', + 'transponder' => 'LB1D1', + 'perf_cat' => 'D', + 'pbn' => 'A1B1C1D1O1S2' + ]); + DB::table('aircraft')->insert([ + 'ac_type' => 'A343', + 'icao_wtc' => 'H', + 'equipment' => 'SDE2E3FGHIJ4J5M1RWXYZ', + 'transponder' => 'LB1D1', + 'perf_cat' => 'D', + 'pbn' => 'A1B1C1D1O1S2' + ]); + DB::table('aircraft')->insert([ + 'ac_type' => 'A345', + 'icao_wtc' => 'H', + 'equipment' => 'SDE2E3FGHIJ3J4J5M1RWXYZ', + 'transponder' => 'LB1D1', + 'perf_cat' => 'D', + 'pbn' => 'A1B1C1D1L1O1S1S2' + ]); + DB::table('aircraft')->insert([ + 'ac_type' => 'A346', + 'icao_wtc' => 'H', + 'equipment' => 'SDE2E3FGHIJ3J4J5M1RWXYZ', + 'transponder' => 'LB1D1', + 'perf_cat' => 'D', + 'pbn' => 'A1B1C1D1L1O1S1S2' + ]); + DB::table('aircraft')->insert([ + 'ac_type' => 'A359', + 'icao_wtc' => 'H', + 'equipment' => 'SDE2E3GHIJ1J3J4J5LM1ORWXYZ', + 'transponder' => 'LB1D1', + 'perf_cat' => 'D', + 'pbn' => 'A1B1C1D1L1O1S2' + ]); + DB::table('aircraft')->insert([ + 'ac_type' => 'A35K', + 'icao_wtc' => 'H', + 'equipment' => 'SDE2E3GHIJ1J3J4J5LM1ORWXYZ', + 'transponder' => 'LB1D1', + 'perf_cat' => 'D', + 'pbn' => 'A1B1C1D1L1O1S2' + ]); + DB::table('aircraft')->insert([ + 'ac_type' => 'A388', + 'icao_wtc' => 'H', + 'equipment' => 'SADE2E3FGHIJ3J4J5M1RWXYZ', + 'transponder' => 'LB1D1', + 'perf_cat' => 'D', + 'pbn' => 'A1B1C1D1L1O1S2' + ]); + DB::table('aircraft')->insert([ + 'ac_type' => 'B37M', + 'icao_wtc' => 'M', + 'equipment' => 'SDE1FGHILORVWXY', + 'transponder' => 'HB1', + 'perf_cat' => 'C', + 'pbn' => 'A1B1C1D1L1O1S1S2' + ]); + DB::table('aircraft')->insert([ + 'ac_type' => 'B38M', + 'icao_wtc' => 'M', + 'equipment' => 'SDE1FGHILORVWXY', + 'transponder' => 'HB1', + 'perf_cat' => 'C', + 'pbn' => 'A1B1C1D1L1O1S1S2' + ]); + DB::table('aircraft')->insert([ + 'ac_type' => 'B39M', + 'icao_wtc' => 'M', + 'equipment' => 'SDE1FGHILORVWXY', + 'transponder' => 'HB1', + 'perf_cat' => 'C', + 'pbn' => 'A1B1C1D1L1O1S1S2' + ]); + DB::table('aircraft')->insert([ + 'ac_type' => 'B712', + 'icao_wtc' => 'M', + 'equipment' => 'SDFGHRWY', + 'transponder' => 'C', + 'perf_cat' => 'C', + 'pbn' => 'A1B2' + ]); + DB::table('aircraft')->insert([ + 'ac_type' => 'B737', + 'icao_wtc' => 'M', + 'equipment' => 'SDE2E3FGHIRWXY', + 'transponder' => 'LB1', + 'perf_cat' => 'C', + 'pbn' => 'A1B1C1D1S1S2' + ]); + DB::table('aircraft')->insert([ + 'ac_type' => 'B738', + 'icao_wtc' => 'M', + 'equipment' => 'SDE2E3FGHIRWXY', + 'transponder' => 'LB1', + 'perf_cat' => 'C', + 'pbn' => 'A1B1C1D1S1S2' + ]); + DB::table('aircraft')->insert([ + 'ac_type' => 'B739', + 'icao_wtc' => 'M', + 'equipment' => 'SDE2E3FGHIRWXY', + 'transponder' => 'LB1', + 'perf_cat' => 'C', + 'pbn' => 'A1B1C1D1S1S2' + ]); + DB::table('aircraft')->insert([ + 'ac_type' => 'B74F', + 'icao_wtc' => 'H', + 'equipment' => 'SDE3FGHIM1M2RWXYZ', + 'transponder' => 'LB1', + 'perf_cat' => 'D', + 'pbn' => 'A1B1D1O1S2' + ]); + DB::table('aircraft')->insert([ + 'ac_type' => 'B748', + 'icao_wtc' => 'H', + 'equipment' => 'SDE3FGHIM1M2RWXYZ', + 'transponder' => 'LB1', + 'perf_cat' => 'D', + 'pbn' => 'A1B1D1O1S2' + ]); + DB::table('aircraft')->insert([ + 'ac_type' => 'B48F', + 'icao_wtc' => 'H', + 'equipment' => 'SDE3FGHIM1M2RWXYZ', + 'transponder' => 'LB1', + 'perf_cat' => 'D', + 'pbn' => 'A1B1D1O1S2' + ]); + DB::table('aircraft')->insert([ + 'ac_type' => 'B752', + 'icao_wtc' => 'M', + 'equipment' => 'SDE2E3FGHIJ3J4J7RWXY', + 'transponder' => 'LB1', + 'perf_cat' => 'C', + 'pbn' => 'A1B1D1L1O1S1' + ]); + DB::table('aircraft')->insert([ + 'ac_type' => 'B753', + 'icao_wtc' => 'M', + 'equipment' => 'SDE2E3FGHIJ3J4J7RWXY', + 'transponder' => 'LB1', + 'perf_cat' => 'C', + 'pbn' => 'A1B1D1L1O1S1' + ]); + DB::table('aircraft')->insert([ + 'ac_type' => 'B762', + 'icao_wtc' => 'H', + 'equipment' => 'SDFGIRWY', + 'transponder' => 'S', + 'perf_cat' => 'D', + 'pbn' => 'A1B1D1O1' + ]); + DB::table('aircraft')->insert([ + 'ac_type' => 'B763', + 'icao_wtc' => 'H', + 'equipment' => 'SDE3FGHIM3RWXYZ', + 'transponder' => 'LB1', + 'perf_cat' => 'D', + 'pbn' => 'A1B1C1D1L1O1' + ]); + DB::table('aircraft')->insert([ + 'ac_type' => 'B76F', + 'icao_wtc' => 'H', + 'equipment' => 'SDE3FGHIM3RWXYZ', + 'transponder' => 'LB1', + 'perf_cat' => 'D', + 'pbn' => 'A1B1C1D1L1O1' + ]); + DB::table('aircraft')->insert([ + 'ac_type' => 'B764', + 'icao_wtc' => 'H', + 'equipment' => 'SDE3FGHIM3RWXY', + 'transponder' => 'LB1', + 'perf_cat' => 'D', + 'pbn' => 'A1B1C1D1L1O1' + ]); + DB::table('aircraft')->insert([ + 'ac_type' => 'B772', + 'icao_wtc' => 'H', + 'equipment' => 'SDE2E3FGHIJ5LORVWXY', + 'transponder' => 'LB1D1', + 'perf_cat' => 'D', + 'pbn' => 'A1B1D1S2T1' + ]); + DB::table('aircraft')->insert([ + 'ac_type' => 'B77F', + 'icao_wtc' => 'H', + 'equipment' => 'SDE1E2E3FGHIJ2J3J4J5M1RWXYZ', + 'transponder' => 'LB1D1', + 'perf_cat' => 'D', + 'pbn' => 'A1B1C1D1L1O1S2' + ]); + DB::table('aircraft')->insert([ + 'ac_type' => 'B77L', + 'icao_wtc' => 'H', + 'equipment' => 'SDE1E2E3FGHIJ2J3J4J5M1RWXYZ', + 'transponder' => 'LB1D1', + 'perf_cat' => 'D', + 'pbn' => 'A1B1C1D1L1O1S2' + ]); + DB::table('aircraft')->insert([ + 'ac_type' => 'B77W', + 'icao_wtc' => 'H', + 'equipment' => 'SDE1E2E3FGHIJ2J3J4J5M1RWXYZ', + 'transponder' => 'LB1D1', + 'perf_cat' => 'D', + 'pbn' => 'A1B1C1D1L1O1S2' + ]); + DB::table('aircraft')->insert([ + 'ac_type' => 'B788', + 'icao_wtc' => 'H', + 'equipment' => 'SDE1E2E3FGHIJ2J3J4J5M1RWXYZ', + 'transponder' => 'LB1D1', + 'perf_cat' => 'D', + 'pbn' => 'A1B1C1D1L1O1S2' + ]); + DB::table('aircraft')->insert([ + 'ac_type' => 'B789', + 'icao_wtc' => 'H', + 'equipment' => 'SDE1E2E3FGHIJ2J3J4J5M1RWXYZ', + 'transponder' => 'LB1D1', + 'perf_cat' => 'D', + 'pbn' => 'A1B1C1D1L1O1S2' + ]); + DB::table('aircraft')->insert([ + 'ac_type' => 'B78X', + 'icao_wtc' => 'H', + 'equipment' => 'SDE1E2E3FGHIJ2J3J4J5M1RWXYZ', + 'transponder' => 'LB1D1', + 'perf_cat' => 'D', + 'pbn' => 'A1B1C1D1L1O1S2' + ]); + DB::table('aircraft')->insert([ + 'ac_type' => 'DC10', + 'icao_wtc' => 'H', + 'equipment' => 'SDE1E2E3FGHIJ2J3J4J5M1RWXYZ', + 'transponder' => 'LB1D1', + 'perf_cat' => 'D', + 'pbn' => 'A1B1C1D1L1O1S2' + ]); + DB::table('aircraft')->insert([ + 'ac_type' => 'MD11', + 'icao_wtc' => 'H', + 'equipment' => 'SDE1E2E3FGHIJ2J3J4J5M1RWXYZ', + 'transponder' => 'LB1D1', + 'perf_cat' => 'D', + 'pbn' => 'A1B1C1D1L1O1S2' + ]); + DB::table('aircraft')->insert([ + 'ac_type' => 'MD1F', + 'icao_wtc' => 'H', + 'equipment' => 'SDE1E2E3FGHIJ2J3J4J5M1RWXYZ', + 'transponder' => 'LB1D1', + 'perf_cat' => 'D', + 'pbn' => 'A1B1C1D1L1O1S2' + ]); + DB::table('aircraft')->insert([ + 'ac_type' => 'CRJ2', + 'icao_wtc' => 'M', + 'equipment' => 'SDFGIRWZ', + 'transponder' => 'S', + 'perf_cat' => 'C', + 'pbn' => 'D1' + ]); + DB::table('aircraft')->insert([ + 'ac_type' => 'CRJ5', + 'icao_wtc' => 'M', + 'equipment' => 'SDFGIRWYZ', + 'transponder' => 'S', + 'perf_cat' => 'C', + 'pbn' => 'D1' + ]); + DB::table('aircraft')->insert([ + 'ac_type' => 'CRJ7', + 'icao_wtc' => 'M', + 'equipment' => 'SDFGIRWYZ', + 'transponder' => 'S', + 'perf_cat' => 'C', + 'pbn' => 'D1' + ]); + DB::table('aircraft')->insert([ + 'ac_type' => 'CRJ9', + 'icao_wtc' => 'M', + 'equipment' => 'SDFGIRWYZ', + 'transponder' => 'S', + 'perf_cat' => 'C', + 'pbn' => 'D1' + ]); + DB::table('aircraft')->insert([ + 'ac_type' => 'CRJX', + 'icao_wtc' => 'M', + 'equipment' => 'SDFGIRWYZ', + 'transponder' => 'S', + 'perf_cat' => 'C', + 'pbn' => 'D1' + ]); + DB::table('aircraft')->insert([ + 'ac_type' => 'E135', + 'icao_wtc' => 'M', + 'equipment' => 'SDFGIRWZ', + 'transponder' => 'S', + 'perf_cat' => 'C', + 'pbn' => 'D1' + ]); + DB::table('aircraft')->insert([ + 'ac_type' => 'E140', + 'icao_wtc' => 'M', + 'equipment' => 'SDFGIRWZ', + 'transponder' => 'S', + 'perf_cat' => 'C', + 'pbn' => 'D1' + ]); + DB::table('aircraft')->insert([ + 'ac_type' => 'E145', + 'icao_wtc' => 'M', + 'equipment' => 'SDFGIRWZ', + 'transponder' => 'S', + 'perf_cat' => 'C', + 'pbn' => 'D1' + ]); + DB::table('aircraft')->insert([ + 'ac_type' => 'E170', + 'icao_wtc' => 'M', + 'equipment' => 'SDE3GILORVW', + 'transponder' => 'S', + 'perf_cat' => 'C', + 'pbn' => 'B2B3B4B5C1D1S2' + ]); + DB::table('aircraft')->insert([ + 'ac_type' => 'E175', + 'icao_wtc' => 'M', + 'equipment' => 'SDE3GILORVW', + 'transponder' => 'S', + 'perf_cat' => 'C', + 'pbn' => 'B2B3B4B5C1D1S2' + ]); + DB::table('aircraft')->insert([ + 'ac_type' => 'E190', + 'icao_wtc' => 'M', + 'equipment' => 'SDE3GILORVW', + 'transponder' => 'S', + 'perf_cat' => 'C', + 'pbn' => 'B2B3B4B5C1D1S2' + ]); + DB::table('aircraft')->insert([ + 'ac_type' => 'E195', + 'icao_wtc' => 'M', + 'equipment' => 'SDE3GILORVW', + 'transponder' => 'S', + 'perf_cat' => 'C', + 'pbn' => 'B2B3B4B5C1D1S2' + ]); + } +} From c8d31861a537d9c7d53c5dbc0a1e38b8744a5103 Mon Sep 17 00:00:00 2001 From: kjporter Date: Fri, 28 Nov 2025 10:11:04 -0500 Subject: [PATCH 03/12] Add Aircraft Types --- app/Exports/RealopsExport.php | 2 ++ app/Importers/RealopsFlightImporter.php | 6 +++++ ...11_24_145500_realops_flight_add_actype.php | 24 +++++++++++++++++++ .../dashboard/admin/realops/create.blade.php | 4 ++-- .../dashboard/admin/realops/edit.blade.php | 4 ++-- .../dashboard/admin/realops/index.blade.php | 2 ++ 6 files changed, 38 insertions(+), 4 deletions(-) create mode 100644 database/migrations/2025_11_24_145500_realops_flight_add_actype.php diff --git a/app/Exports/RealopsExport.php b/app/Exports/RealopsExport.php index 2cf97f97f..5270cb67b 100644 --- a/app/Exports/RealopsExport.php +++ b/app/Exports/RealopsExport.php @@ -22,6 +22,7 @@ public function headings(): array { 'Point of Arrival', 'Arrival Time', 'Gate', + 'Aircraft Type', 'Pilot Name', 'Pilot CID', 'Pilot Email' @@ -44,6 +45,7 @@ public function map($flight): array { $flight->arr_airport, $flight->est_time_enroute, $flight->gate, + $flight->aircraft_type, $pilot->full_name, $flight->assigned_pilot_id, $pilot->email diff --git a/app/Importers/RealopsFlightImporter.php b/app/Importers/RealopsFlightImporter.php index a0c12df9f..8613a8b4b 100644 --- a/app/Importers/RealopsFlightImporter.php +++ b/app/Importers/RealopsFlightImporter.php @@ -17,6 +17,7 @@ public function model($row) { } $est_time_enroute = null; $gate = null; + $aircraft_type = null; if (count($row) > 6) { $est_time_enroute = $row[6]; @@ -26,6 +27,10 @@ public function model($row) { $gate = $row[7]; } + if (count($row) > 8) { + $aircraft_type = $row[8]; + } + $flight = new RealopsFlight; $flight->flight_number = $row[0]; $flight->callsign = $row[1]; @@ -35,6 +40,7 @@ public function model($row) { $flight->arr_airport = $row[5]; $flight->est_time_enroute = $est_time_enroute; $flight->gate = $gate; + $flight->aircraft_type = $aircraft_type; return $flight; } diff --git a/database/migrations/2025_11_24_145500_realops_flight_add_actype.php b/database/migrations/2025_11_24_145500_realops_flight_add_actype.php new file mode 100644 index 000000000..a4e78cff4 --- /dev/null +++ b/database/migrations/2025_11_24_145500_realops_flight_add_actype.php @@ -0,0 +1,24 @@ +string('aircraft_type')->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void { + Schema::table('realops_flights', function ($table) { + $table->dropColumn('aircraft_type'); + }); + } +}; diff --git a/resources/views/dashboard/admin/realops/create.blade.php b/resources/views/dashboard/admin/realops/create.blade.php index fc4594730..0c2516099 100644 --- a/resources/views/dashboard/admin/realops/create.blade.php +++ b/resources/views/dashboard/admin/realops/create.blade.php @@ -58,8 +58,8 @@ {{ html()->text('arr_airport', null)->class(['form-control'])->placeholder('Required') }}
- - {{ html()->text('est_time_enroute', null)->class(['form-control'])->placeholder('HH:MM - Required')->id('realops_add_edit_ete') }} + + {{ html()->text('aircraft_type', null)->class(['form-control'])->placeholder('A20N') }}
diff --git a/resources/views/dashboard/admin/realops/edit.blade.php b/resources/views/dashboard/admin/realops/edit.blade.php index 5bf3aeab5..cc8aa30ea 100644 --- a/resources/views/dashboard/admin/realops/edit.blade.php +++ b/resources/views/dashboard/admin/realops/edit.blade.php @@ -58,8 +58,8 @@ {{ html()->text('arr_airport', $flight->arr_airport)->class(['form-control'])->placeholder('Required') }}
- - {{ html()->text('est_time_enroute', $flight->est_time_enroute_formatted)->class(['form-control'])->placeholder('HH:MM - Required')->id('realops_add_edit_ete') }} + + {{ html()->text('aircraft_type', $flight->aircraft_type)->class(['form-control'])->placeholder('A20N') }}
diff --git a/resources/views/dashboard/admin/realops/index.blade.php b/resources/views/dashboard/admin/realops/index.blade.php index b5c719d02..a42abeb19 100644 --- a/resources/views/dashboard/admin/realops/index.blade.php +++ b/resources/views/dashboard/admin/realops/index.blade.php @@ -32,6 +32,7 @@ Date Callsign
Flight Number + Aircraft Type Departure Time (UTC) Departure Airport Arrival Airport @@ -55,6 +56,7 @@
{{ $f->flight_number }} + {{ $f->aircraft_type }} {{ $f->dep_time_formatted }} {{ $f->dep_airport }} {{ $f->arr_airport }} From 6ed30ef99769d64888af7b514f2a1aa802eba5de Mon Sep 17 00:00:00 2001 From: kjporter Date: Fri, 28 Nov 2025 10:11:28 -0500 Subject: [PATCH 04/12] Integrate Pre-File Logic --- app/Http/Controllers/RealopsController.php | 10 +-- app/RealopsFlight.php | 95 +++++++++++++++++++++ resources/assets/img/navigraph-icon.png | Bin 0 -> 3328 bytes resources/assets/img/vatsim-icon.png | Bin 0 -> 7180 bytes resources/views/site/realops.blade.php | 27 +++--- 5 files changed, 117 insertions(+), 15 deletions(-) create mode 100644 resources/assets/img/navigraph-icon.png create mode 100644 resources/assets/img/vatsim-icon.png diff --git a/app/Http/Controllers/RealopsController.php b/app/Http/Controllers/RealopsController.php index 988a68f05..e4aee3d20 100644 --- a/app/Http/Controllers/RealopsController.php +++ b/app/Http/Controllers/RealopsController.php @@ -17,11 +17,13 @@ class RealopsController extends Controller { public function index(Request $request) { $airport_filter = $request->get('airport_filter'); $flightno_filter = $request->get('flightno_filter'); + $actype_filter = $request->get('actype_filter'); $date_filter = $request->get('date_filter'); $time_filter = $request->get('time_filter'); $flights = RealopsFlight::where('flight_number', 'like', '%' . $flightno_filter . '%') ->orWhere('callsign', 'like', '%' . $flightno_filter . '%') + ->orWhere('aircraft_type', 'like', '%' . $actype_filter . '%') ->when(! is_null($time_filter), function ($query) use ($time_filter) { $times = $this->timeBetween($time_filter, 15, 45); $query->whereTime('dep_time', ">=", Carbon::parse($times[0])) @@ -38,7 +40,7 @@ public function index(Request $request) { ->orderBy('dep_time', 'ASC') ->paginate(20); - return view('site.realops')->with('flights', $flights)->with('airport_filter', $airport_filter)->with('flightno_filter', $flightno_filter)->with('date_filter', $date_filter)->with('time_filter', $time_filter); + return view('site.realops', compact('flights', 'airport_filter', 'flightno_filter', 'actype_filter', 'date_filter', 'time_filter')); } public function bid($id) { @@ -148,6 +150,7 @@ public function createFlight(Request $request) { $flight->arr_airport = $request->input('arr_airport'); $flight->est_time_enroute = $request->input('est_time_enroute'); $flight->gate = $request->input('gate'); + $flight->aircraft_type = $request->input('aircraft_type'); $flight->save(); return redirect('/dashboard/admin/realops')->with('success', 'That flight was created successfully'); @@ -187,6 +190,7 @@ public function editFlight(Request $request, $id) { $flight->arr_airport = $request->input('arr_airport'); $flight->est_time_enroute = $request->input('est_time_enroute'); $flight->gate = $request->input('gate'); + $flight->aircraft_type = $request->input('aircraft_type'); $flight->save(); $pilot = $flight->assigned_pilot; @@ -204,9 +208,6 @@ public function bulkUploadFlights(Request $request) { ]); try { - // what is this for? - // this doesn't do anything - //$contents = file_get_contents($request->file('file')->getRealPath()); Excel::import(new RealopsFlightImporter, request()->file('file')); } catch (\Maatwebsite\Excel\Validators\ValidationException $e) { $failures = $e->failures(); @@ -214,7 +215,6 @@ public function bulkUploadFlights(Request $request) { $errors = ""; foreach ($failures as $failure) { - // L21#0 Blah blah blah (value: 'fbalsdhj') Log::info($failure); $errors = $errors.' L'.$failure->row().'#'.$failure->attribute().': '.join(',', $failure->errors()).' ('.$failure->values()[$failure->attribute()].')'; } diff --git a/app/RealopsFlight.php b/app/RealopsFlight.php index cfe96cc8a..ce5a9d40d 100644 --- a/app/RealopsFlight.php +++ b/app/RealopsFlight.php @@ -9,6 +9,14 @@ class RealopsFlight extends Model { protected $table = 'realops_flights'; + public function getFlightIdAttribute(): string { + return (!is_null($this->callsign)) ? $this->callsign : $this->flight_number; + } + + public function getAirlineAttribute(): string { + return substr($this->flight_id, 0, 3); + } + public function getAssignedPilotAttribute() { return RealopsPilot::find($this->assigned_pilot_id); } @@ -44,6 +52,36 @@ private function formatTime($time) { return $time_split[0] . ':' . $time_split[1]; } + private function timePart($time, $part): null|int { + $time_parts = explode(':', $time); + if (count($time_parts) == 0) { + return null; + } elseif ($part == 'h') { + return (int) $time_parts[0]; + } elseif ($part == 'm') { + return (int) $time_parts[1]; + } + return null; + } + + private function eta(): string { + $etd = explode(':', $this->dep_time); + $ete = explode(':', $this->est_time_enroute); + if (count($etd) == 0 || count($ete) == 0) { + return '0000'; + } + $hours = $etd[0] + $ete[0]; + $minutes = $etd[1] + $ete[1]; + if ($minutes > 59) { + $minutes -= 60; + $hours++; + } + if ($hours > 23) { + $hours -= 24; + } + return str_pad($hours, 2, '0', STR_PAD_LEFT) . str_pad($minutes, 2, '0', STR_PAD_LEFT); + } + public function getImageDirectory() { $flight_id = (!is_null($this->callsign)) ? $this->callsign : $this->flight_number; $airline = strtoupper(substr($flight_id, 0, 3)); @@ -53,4 +91,61 @@ public function getImageDirectory() { } return Vite::image('airline_logos/default.png'); } + + public function getIcaoFlightplanAttribute(): string { + $icao_string = 'FPL-' . $this->flight_number . '-IS '; + $icao_string .= '-' . $this->aircraft_type; + $ac = Aircraft::where('ac_type', 'LIKE', '%' . $this->aircraft_type . '%')->first(); + if ($ac) { + $icao_string .= '/' . $ac->icao_wtc . '-' . $ac->equipment . '/' . $ac->transponder; + } + $icao_string .= ' -' . $this->dep_airport . str_pad($this->timePart($this->dep_time, 'h'), 2, '0', STR_PAD_LEFT) . str_pad($this->timePart($this->dep_time, 'm'), 2, '0', STR_PAD_LEFT); + $icao_string .= ' -' . PreferredRoute::routeLookup($this->dep_airport, $this->arr_airport); + $icao_string .= ' -' . $this->arr_airport . $this->eta(); + $icao_string .= ' -DOF/' . Carbon::parse($this->flight_date)->format('ymd') . ' OPR/' . $this->airline; + if ($ac) { + $icao_string .= ' PBN/' . $ac->pbn . ' PER/' . $ac->perf_cat; + } + return '(' . $icao_string . ')'; + } + + // https://developers.navigraph.com/docs/simbrief/using-the-api#api-parameters + public function getSimbriefParamsAttribute(): string { + $pilot = RealopsPilot::find($this->assigned_pilot_id); + $params = [ + 'airline' => $this->airline, + 'fltnum' => substr($this->flight_number, 3), + 'type' => $this->aircraft_type, + 'orig' => $this->dep_airport, + 'dest' => $this->arr_airport, + 'date' => Carbon::parse($this->flight_date)->format('dMY'), + 'deph' => $this->timePart($this->dep_time, 'h'), + 'depm' => $this->timePart($this->dep_time, 'm'), + 'route' => PreferredRoute::routeLookup($this->dep_airport, $this->arr_airport), + 'steh' => $this->timePart($this->est_time_enroute, 'h'), + 'stem' => $this->timePart($this->est_time_enroute, 'm'), + 'callsign' => $this->callsign, + 'cpt' => $pilot->full_name, + 'pid' => $pilot->id, + 'acdata' => null + ]; + $ac = Aircraft::where('ac_type', 'LIKE', '%' . $this->aircraft_type . '%')->first(); + if ($ac) { + $ac_data = [ + "pbn" => $ac->pbn, + "dof" => Carbon::parse($this->flight_date)->format('ymd'), // May need to remove this... SimBrief may generate the DOF from data above + "opr" => $this->airline, + "per" => $ac->perf_cat, + "cat" => $ac->icao_wtc, + "equip" => $ac->equipment, + "transponder" => $ac->transponder + ]; + $params['acdata'] = json_encode((object) $ac_data); + } + + if (count($params) == 0) { + return ''; + } + return http_build_query($params); + } } diff --git a/resources/assets/img/navigraph-icon.png b/resources/assets/img/navigraph-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..c70c735edded6429ce4af93a04878e5d19c13ba0 GIT binary patch literal 3328 zcmV+b4gd0qP)002t}1^@s6I8J)%0004mX+uL$Nkc;* zaB^>EX>4Tx04R}tkv&MmKpe$iTcuJf4t7v+$WWauh*oiwDi*;)X)CnqU~=gfG-*gu zTpR`0f`cE6RR3hM!aiw#-;000McNliru=?fVOIRv911)Bf>02y>eSad^gZEa<4bO1wgWnpw> zWFU8GbZ8()Nlj2!fese{01DViL_t(|+U=ZcbX3(9$M-(>-nlcAgjd2d$z<{ZtjpGl z)uLz-1i=sj2?P)d$YTizQJ^*m7K^qb&m~kXlv+S7K^_5t6a*|pty*0^KwVaOCWJ=< z$xH^w%-nnK+5G^K07=MWGLtov{qfAc=l;cvsz$PV>>f5 ztcJT{e$_GZdS(|69~{*J>jtYhQ#779kH2ovFd{|Cx#=yis@SgKL(RhD`CWCNk>-8! zROaB;EwC!1>fE`JH$~O8=!!DVmrQ7Z1pxb}JRut%_srUS$m5w&G$Kt6d01uN9qvcE z{_|R$3?9!foF5YMu!QD)x-Nd?_0A}HZVrYltgZd}+#xirZ=IYqt*85|&+iUdSp1@E z8W2f!^Cm_v=hTpdwSCaQc0%)xs?Rl~>%$AvQ`(0dEU(ishJ>hJ|0G1XaFmV-DOmrW zlP!_gGr1wxlIERM^6X40WMGwj_R0N1*Kcpo#kzi*`^zux4;fgz;__qwXlNUNR8cW8 zBw+1GPmLr^8`_vTNY{oIWDaf{^sqd|B_l|P#vSSj5kcomGJ+b`N7=Jb=-N1+Or~pN zgBn)Jp~JTeUGL$Gxk%T07N+(~3|d&a%Qdp@9s8|lkt{B{h6g3A{ZC9}Lf3s-XNdER zq8H|4(7|$@DoQ4Ti%ZLr5CD=BZAU7{H}(4UM1J|w>e>o@Gy|~7wMyd{ z7Cym#;M2D%2YxtOa0db~Ynb|NOy^EUvximgc4P6GlAj4dBM4|15Of6qjUi6&mTdn= zrQhb>y`Iv`oy&oU)f*O~V6EFc6}R`pR{;P{7Zu|rw|lA}S`Gjv062+=TNqseYAuSZ}q`BjWNfi<%u9)7dyMV+vk4baT_5p3r;gl=l2StX+)G>hbX!wFjpY!^vWVawUoHrU_?ktv zX2sb$2mpZ14?QqMcRJn%qOhM}A@icuI>mnA({)#(x(E$}|CxxM_2IfC5OF^uzG5;- z`{Hdeyk4-59xrUmg_uZ!rUO7+6LlAah)d0?Dcf%ALI40;`aIZ6cRIIp0i&bMB)eEd z#F)5!AMLEX;D1L>&Lg51o2unOgu@89OjhJgiS`&zt+0-qI2CDdV+IjD2>||h)rS!A z`R>W~7b^#H2lh|waH)JjY`A4v-0p*4RRJ}Xh+g$mBYlh^E>&cCT|)ddXm~Y}tlSV; zMDz>*wDa>Zkzk0^x+U9J*W~cSk%DwVVj}=3e%5vj5$CI_{7$?r#=sws7TjY9u^s@r zL*Ptf3{$%$+4tTA>S#d^A;bXyhzO82LWF$Aq>1wJlZA`8!2<~j0Kgx>nckIx9cDJOsFDu8_ z{&VYOZWw>9X5ZNqkd8=_v@|Tt@>XWrKzFTu){eagnWN0nhjZf@Aw=IM5@^VhH0ANp z8Gi==$p83rJW+7!30>zax+W!Wp<$@SvUr3BCkBA7* z%}DLi+RQgH)fPd{fi04X5=NX2v003Cm zqy*>?6$t>KA>#Dz$@Vn>0D14{V@dIaxttrmY)o~=*e+R?R<>)~`oq*m`WTJgNN?D@ zv$I#zCK*CZA|hL3o}ful<|HQCm-M>lm$d7>{p@_HYa!?SnMyrXU;zLa$Z)QrC`%IK zqA4dg58V~+`GyeBHwG9Y7Bj}yn-qEVn9Q`}O>*k6EpMM%UVbsv5Mrtj;y3k7QK2aE za~-0hUVgOSgS2h;`;ya9zKnDJR8^-NU{!YdFQ~GzAU-~tHsTkA>#`(eJw7^P6#&4ydHYyd`Nd_`fvN@8 z)gp|G%xc)YxY+gr0ATHgt&=%7UcGvxT-Dr+vAwdy-n3e+yN9O@a5sx%<*JsmWoNWU z(}oE_nL>!W5z!;d(&OVtrRM?wytn@YJX><%C7ts*HO)~Atjd&p$znDCAuhTTU0o#~ z)j3}eKvstA6J)sAVpg|}OdsUaUw)djkJ_+hXJ-I_$A+bz{i$Etur-@=V_|Lgz6GpG zU~{`C+h496cze^1NQ+q&8G|2jH08d>Z?;oSiqkNE@Xge9S zumAugiM{EgdbQ5q+WihIJj}Ws5uJ^krc9QV?LiM~*x&&d8Naw1ZCgcr&KQ2(z*_ZRv#NX@GO$ty_7#k=H4RL`RdKOhC}dy( z09efCjSVRD#fmI%4V319!5W#K;$RGO>KRpQ6YMdULk<=IfTAd?0HCg~EP9#5{uW5R z0fm*682b%lEWeIXwWDi-?L^4J>e=Hik|k+H9b5?!Ux5Hy!2dC%U7Oas8N=^ujVgeT zRaN~YT7H3yx|0@kp!0hc7k zR@6|~7bV8W@)lUvHL2<H9xFR%qZb;SlEl0000< KMNUMnLSTaX%{Ar# literal 0 HcmV?d00001 diff --git a/resources/assets/img/vatsim-icon.png b/resources/assets/img/vatsim-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..44a354e2bfcd2b09aa0007e247436c4b5e55d24c GIT binary patch literal 7180 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 O0000 {{ html()->text('flightno_filter', $flightno_filter)->class(['form-control'])->placeholder('Flight (DAL367)')->id('flightno_filter') }} +
+ {{ html()->text('actype_filter', $actype_filter)->class(['form-control'])->placeholder('A20N')->id('actype_filter') }} +
{{ html()->text('date_filter', $date_filter)->class(['form-control'])->placeholder('Date (YYYY-MM-DD)')->id('date_filter') }}
@@ -52,6 +55,7 @@ Date Callsign
Flight Number + Aircraft Type Departure Time (UTC) Departure Airport Arrival Airport @@ -77,6 +81,7 @@
{{ $f->flight_number }} + {{ $f->aircraft_type }} {{ $f->dep_time_formatted }} {{ $f->dep_airport }} {{ $f->arr_airport }} @@ -109,16 +114,18 @@ @endif @if(auth()->guard('realops')->check() && toggleEnabled('realops_bidding')) - -
- @if(auth()->guard('realops')->user()->id == $f->assigned_pilot_id) - Cancel Bid - @elseif($f->assigned_pilot) - - @else - Bid - @endif -
+ + @if(auth()->guard('realops')->user()->id == $f->assigned_pilot_id) + Cancel Bid + @elseif($f->assigned_pilot) + + @else + Bid + @endif + @if($f->assigned_pilot) + MyVATSIMPrefile Flight Plan + SimBriefSend to SimBrief + @endif @endif From 64d455276b9c81e060d2bde1f3a085f2b551d5b8 Mon Sep 17 00:00:00 2001 From: kjporter Date: Wed, 10 Dec 2025 19:45:01 -0500 Subject: [PATCH 05/12] Add format references --- app/RealopsFlight.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/RealopsFlight.php b/app/RealopsFlight.php index ce5a9d40d..5f3c984dd 100644 --- a/app/RealopsFlight.php +++ b/app/RealopsFlight.php @@ -92,6 +92,7 @@ public function getImageDirectory() { return Vite::image('airline_logos/default.png'); } + // ICAO flight plan reference: https://www.faa.gov/documentLibrary/media/Form/7233-4_02-07-24.pdf public function getIcaoFlightplanAttribute(): string { $icao_string = 'FPL-' . $this->flight_number . '-IS '; $icao_string .= '-' . $this->aircraft_type; @@ -109,7 +110,7 @@ public function getIcaoFlightplanAttribute(): string { return '(' . $icao_string . ')'; } - // https://developers.navigraph.com/docs/simbrief/using-the-api#api-parameters + // SimBrief reference: https://developers.navigraph.com/docs/simbrief/using-the-api#api-parameters public function getSimbriefParamsAttribute(): string { $pilot = RealopsPilot::find($this->assigned_pilot_id); $params = [ From c77d095f5cb833afd559f06472b01eb49c9a05eb Mon Sep 17 00:00:00 2001 From: kjporter Date: Wed, 10 Dec 2025 19:45:43 -0500 Subject: [PATCH 06/12] Update field label --- resources/views/dashboard/admin/realops/create.blade.php | 2 +- resources/views/dashboard/admin/realops/edit.blade.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/views/dashboard/admin/realops/create.blade.php b/resources/views/dashboard/admin/realops/create.blade.php index 0c2516099..d56f325c5 100644 --- a/resources/views/dashboard/admin/realops/create.blade.php +++ b/resources/views/dashboard/admin/realops/create.blade.php @@ -58,7 +58,7 @@ {{ html()->text('arr_airport', null)->class(['form-control'])->placeholder('Required') }}
- + {{ html()->text('aircraft_type', null)->class(['form-control'])->placeholder('A20N') }}
diff --git a/resources/views/dashboard/admin/realops/edit.blade.php b/resources/views/dashboard/admin/realops/edit.blade.php index cc8aa30ea..6ff0a6476 100644 --- a/resources/views/dashboard/admin/realops/edit.blade.php +++ b/resources/views/dashboard/admin/realops/edit.blade.php @@ -58,7 +58,7 @@ {{ html()->text('arr_airport', $flight->arr_airport)->class(['form-control'])->placeholder('Required') }}
- + {{ html()->text('aircraft_type', $flight->aircraft_type)->class(['form-control'])->placeholder('A20N') }}
From c9156a3cca44ac66cff4a02c658142f582b3fe1c Mon Sep 17 00:00:00 2001 From: kjporter Date: Wed, 10 Dec 2025 19:45:53 -0500 Subject: [PATCH 07/12] Update filter label --- resources/views/site/realops.blade.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/views/site/realops.blade.php b/resources/views/site/realops.blade.php index ac23f3cda..2ecee872b 100644 --- a/resources/views/site/realops.blade.php +++ b/resources/views/site/realops.blade.php @@ -33,7 +33,7 @@ {{ html()->text('flightno_filter', $flightno_filter)->class(['form-control'])->placeholder('Flight (DAL367)')->id('flightno_filter') }}
- {{ html()->text('actype_filter', $actype_filter)->class(['form-control'])->placeholder('A20N')->id('actype_filter') }} + {{ html()->text('actype_filter', $actype_filter)->class(['form-control'])->placeholder('Aircraft Type (A20N)')->id('actype_filter') }}
{{ html()->text('date_filter', $date_filter)->class(['form-control'])->placeholder('Date (YYYY-MM-DD)')->id('date_filter') }} From 76a34d5475b97176ceb779bd2c31303febef385b Mon Sep 17 00:00:00 2001 From: kjporter Date: Thu, 11 Dec 2025 22:21:32 -0500 Subject: [PATCH 08/12] Removes Aircraft table, adds aircraft JSON support --- app/Aircraft.php | 21 +- app/RealopsFlight.php | 4 +- ...025_11_24_145400_create_aircraft_table.php | 34 -- database/seeders/AircraftSeeder.php | 488 ------------------ 4 files changed, 19 insertions(+), 528 deletions(-) delete mode 100644 database/migrations/2025_11_24_145400_create_aircraft_table.php delete mode 100644 database/seeders/AircraftSeeder.php diff --git a/app/Aircraft.php b/app/Aircraft.php index e93d0d043..019ca9e0c 100644 --- a/app/Aircraft.php +++ b/app/Aircraft.php @@ -2,9 +2,22 @@ namespace App; -use Illuminate\Database\Eloquent\Model; +use Illuminate\Support\Collection; +use Illuminate\Support\Facades\Storage; -class Aircraft extends Model { - protected $table = "aircraft"; - public $timestamps = false; +class Aircraft { + + public static $data; + private static $initialized = false; + const FILENAME = 'private/aircraft.json'; + + public static function init() { + if (self::$initialized) { + return 1; + } + if (Storage::disk('local')->exists(self::FILENAME)) { + self::$data = Collection::fromJson(Storage::disk('local')->get(self::FILENAME)); + } + } } +Aircraft::init(); diff --git a/app/RealopsFlight.php b/app/RealopsFlight.php index 5f3c984dd..917280771 100644 --- a/app/RealopsFlight.php +++ b/app/RealopsFlight.php @@ -96,7 +96,7 @@ public function getImageDirectory() { public function getIcaoFlightplanAttribute(): string { $icao_string = 'FPL-' . $this->flight_number . '-IS '; $icao_string .= '-' . $this->aircraft_type; - $ac = Aircraft::where('ac_type', 'LIKE', '%' . $this->aircraft_type . '%')->first(); + $ac = (object) Aircraft::$data->where('ac_type', $this->aircraft_type)->first(); if ($ac) { $icao_string .= '/' . $ac->icao_wtc . '-' . $ac->equipment . '/' . $ac->transponder; } @@ -130,7 +130,7 @@ public function getSimbriefParamsAttribute(): string { 'pid' => $pilot->id, 'acdata' => null ]; - $ac = Aircraft::where('ac_type', 'LIKE', '%' . $this->aircraft_type . '%')->first(); + $ac = (object) Aircraft::$data->where('ac_type', $this->aircraft_type)->first(); if ($ac) { $ac_data = [ "pbn" => $ac->pbn, diff --git a/database/migrations/2025_11_24_145400_create_aircraft_table.php b/database/migrations/2025_11_24_145400_create_aircraft_table.php deleted file mode 100644 index 9bf088306..000000000 --- a/database/migrations/2025_11_24_145400_create_aircraft_table.php +++ /dev/null @@ -1,34 +0,0 @@ -id(); - $table->string('ac_type')->unique(); - $table->enum('icao_wtc', ['L', 'M', 'H', 'J'])->nullable(); - $table->string('equipment')->nullable(); - $table->string('transponder')->nullable(); - $table->enum('perf_cat', ['A', 'B', 'C', 'D', 'E', 'H'])->nullable(); - $table->string('pbn')->nullable(); - }); - - Artisan::call('db:seed', [ - '--class' => 'AircraftSeeder', - ]); - } - - /** - * Reverse the migrations. - */ - public function down(): void { - Schema::dropIfExists('aircraft'); - } -}; diff --git a/database/seeders/AircraftSeeder.php b/database/seeders/AircraftSeeder.php deleted file mode 100644 index 68a0eadd1..000000000 --- a/database/seeders/AircraftSeeder.php +++ /dev/null @@ -1,488 +0,0 @@ -insert([ - 'ac_type' => 'A19N', - 'icao_wtc' => 'M', - 'equipment' => 'SDE3FGHIRWY', - 'transponder' => 'LB1', - 'perf_cat' => 'C', - 'pbn' => 'A1B1C1D1O1S1' - ]); - DB::table('aircraft')->insert([ - 'ac_type' => 'A20N', - 'icao_wtc' => 'M', - 'equipment' => 'SDE2E3FGHIJ1RWXY', - 'transponder' => 'LB1', - 'perf_cat' => 'C', - 'pbn' => 'A1B1C1D1O1S2' - ]); - DB::table('aircraft')->insert([ - 'ac_type' => 'A21N', - 'icao_wtc' => 'M', - 'equipment' => 'SDE3FGHIRWY', - 'transponder' => 'LB1', - 'perf_cat' => 'C', - 'pbn' => 'A1B1C1D1O1S1' - ]); - DB::table('aircraft')->insert([ - 'ac_type' => 'A306', - 'icao_wtc' => 'H', - 'equipment' => 'SDE3FGHIRWY', - 'transponder' => 'LB1', - 'perf_cat' => 'D', - 'pbn' => 'A1B1C1D1L1O1S1' - ]); - DB::table('aircraft')->insert([ - 'ac_type' => 'A30B', - 'icao_wtc' => 'H', - 'equipment' => 'SDE3FGHIRWY', - 'transponder' => 'LB1', - 'perf_cat' => 'D', - 'pbn' => 'A1B1C1D1L1O1S1' - ]); - DB::table('aircraft')->insert([ - 'ac_type' => 'A310', - 'icao_wtc' => 'H', - 'equipment' => 'SDFGHIM1RTUWXYZ', - 'transponder' => 'LB1', - 'perf_cat' => 'D', - 'pbn' => 'A1B1C1D1L1O1S2' - ]); - DB::table('aircraft')->insert([ - 'ac_type' => 'A318', - 'icao_wtc' => 'M', - 'equipment' => 'SDE3FGHIRWY', - 'transponder' => 'LB1', - 'perf_cat' => 'C', - 'pbn' => 'A1B1C1D1O1S1' - ]); - DB::table('aircraft')->insert([ - 'ac_type' => 'A319', - 'icao_wtc' => 'M', - 'equipment' => 'SDE3FGHIRWY', - 'transponder' => 'LB1', - 'perf_cat' => 'C', - 'pbn' => 'A1B1C1D1O1S1' - ]); - DB::table('aircraft')->insert([ - 'ac_type' => 'A320', - 'icao_wtc' => 'M', - 'equipment' => 'SDE3FGHIRWY', - 'transponder' => 'LB1', - 'perf_cat' => 'C', - 'pbn' => 'A1B1C1D1O1S1' - ]); - DB::table('aircraft')->insert([ - 'ac_type' => 'A321', - 'icao_wtc' => 'M', - 'equipment' => 'SDE3FGHIRWY', - 'transponder' => 'LB1', - 'perf_cat' => 'C', - 'pbn' => 'A1B1C1D1O1S1' - ]); - DB::table('aircraft')->insert([ - 'ac_type' => 'A332', - 'icao_wtc' => 'H', - 'equipment' => 'SDE3GHIJ2J3J5M1RVWXYZ', - 'transponder' => 'LB2D1', - 'perf_cat' => 'D', - 'pbn' => 'A1B1C1D1O1S2' - ]); - DB::table('aircraft')->insert([ - 'ac_type' => 'A333', - 'icao_wtc' => 'H', - 'equipment' => 'SDE3GHIJ2J3J5M1RVWXYZ', - 'transponder' => 'LB2D1', - 'perf_cat' => 'D', - 'pbn' => 'A1B1C1D1O1S2' - ]); - DB::table('aircraft')->insert([ - 'ac_type' => 'A338', - 'icao_wtc' => 'H', - 'equipment' => 'SADE1E2E3GHIJ1J3J5M1P2RWXY', - 'transponder' => 'LB2D1', - 'perf_cat' => 'D', - 'pbn' => 'A1B1C1D1L1O1S2' - ]); - DB::table('aircraft')->insert([ - 'ac_type' => 'A339', - 'icao_wtc' => 'H', - 'equipment' => 'SADE1E2E3GHIJ1J3J5M1P2RWXY', - 'transponder' => 'LB2D1', - 'perf_cat' => 'D', - 'pbn' => 'A1B1C1D1L1O1S2' - ]); - DB::table('aircraft')->insert([ - 'ac_type' => 'A342', - 'icao_wtc' => 'H', - 'equipment' => 'SDE2E3FGHIJ4J5M1RWXYZ', - 'transponder' => 'LB1D1', - 'perf_cat' => 'D', - 'pbn' => 'A1B1C1D1O1S2' - ]); - DB::table('aircraft')->insert([ - 'ac_type' => 'A343', - 'icao_wtc' => 'H', - 'equipment' => 'SDE2E3FGHIJ4J5M1RWXYZ', - 'transponder' => 'LB1D1', - 'perf_cat' => 'D', - 'pbn' => 'A1B1C1D1O1S2' - ]); - DB::table('aircraft')->insert([ - 'ac_type' => 'A345', - 'icao_wtc' => 'H', - 'equipment' => 'SDE2E3FGHIJ3J4J5M1RWXYZ', - 'transponder' => 'LB1D1', - 'perf_cat' => 'D', - 'pbn' => 'A1B1C1D1L1O1S1S2' - ]); - DB::table('aircraft')->insert([ - 'ac_type' => 'A346', - 'icao_wtc' => 'H', - 'equipment' => 'SDE2E3FGHIJ3J4J5M1RWXYZ', - 'transponder' => 'LB1D1', - 'perf_cat' => 'D', - 'pbn' => 'A1B1C1D1L1O1S1S2' - ]); - DB::table('aircraft')->insert([ - 'ac_type' => 'A359', - 'icao_wtc' => 'H', - 'equipment' => 'SDE2E3GHIJ1J3J4J5LM1ORWXYZ', - 'transponder' => 'LB1D1', - 'perf_cat' => 'D', - 'pbn' => 'A1B1C1D1L1O1S2' - ]); - DB::table('aircraft')->insert([ - 'ac_type' => 'A35K', - 'icao_wtc' => 'H', - 'equipment' => 'SDE2E3GHIJ1J3J4J5LM1ORWXYZ', - 'transponder' => 'LB1D1', - 'perf_cat' => 'D', - 'pbn' => 'A1B1C1D1L1O1S2' - ]); - DB::table('aircraft')->insert([ - 'ac_type' => 'A388', - 'icao_wtc' => 'H', - 'equipment' => 'SADE2E3FGHIJ3J4J5M1RWXYZ', - 'transponder' => 'LB1D1', - 'perf_cat' => 'D', - 'pbn' => 'A1B1C1D1L1O1S2' - ]); - DB::table('aircraft')->insert([ - 'ac_type' => 'B37M', - 'icao_wtc' => 'M', - 'equipment' => 'SDE1FGHILORVWXY', - 'transponder' => 'HB1', - 'perf_cat' => 'C', - 'pbn' => 'A1B1C1D1L1O1S1S2' - ]); - DB::table('aircraft')->insert([ - 'ac_type' => 'B38M', - 'icao_wtc' => 'M', - 'equipment' => 'SDE1FGHILORVWXY', - 'transponder' => 'HB1', - 'perf_cat' => 'C', - 'pbn' => 'A1B1C1D1L1O1S1S2' - ]); - DB::table('aircraft')->insert([ - 'ac_type' => 'B39M', - 'icao_wtc' => 'M', - 'equipment' => 'SDE1FGHILORVWXY', - 'transponder' => 'HB1', - 'perf_cat' => 'C', - 'pbn' => 'A1B1C1D1L1O1S1S2' - ]); - DB::table('aircraft')->insert([ - 'ac_type' => 'B712', - 'icao_wtc' => 'M', - 'equipment' => 'SDFGHRWY', - 'transponder' => 'C', - 'perf_cat' => 'C', - 'pbn' => 'A1B2' - ]); - DB::table('aircraft')->insert([ - 'ac_type' => 'B737', - 'icao_wtc' => 'M', - 'equipment' => 'SDE2E3FGHIRWXY', - 'transponder' => 'LB1', - 'perf_cat' => 'C', - 'pbn' => 'A1B1C1D1S1S2' - ]); - DB::table('aircraft')->insert([ - 'ac_type' => 'B738', - 'icao_wtc' => 'M', - 'equipment' => 'SDE2E3FGHIRWXY', - 'transponder' => 'LB1', - 'perf_cat' => 'C', - 'pbn' => 'A1B1C1D1S1S2' - ]); - DB::table('aircraft')->insert([ - 'ac_type' => 'B739', - 'icao_wtc' => 'M', - 'equipment' => 'SDE2E3FGHIRWXY', - 'transponder' => 'LB1', - 'perf_cat' => 'C', - 'pbn' => 'A1B1C1D1S1S2' - ]); - DB::table('aircraft')->insert([ - 'ac_type' => 'B74F', - 'icao_wtc' => 'H', - 'equipment' => 'SDE3FGHIM1M2RWXYZ', - 'transponder' => 'LB1', - 'perf_cat' => 'D', - 'pbn' => 'A1B1D1O1S2' - ]); - DB::table('aircraft')->insert([ - 'ac_type' => 'B748', - 'icao_wtc' => 'H', - 'equipment' => 'SDE3FGHIM1M2RWXYZ', - 'transponder' => 'LB1', - 'perf_cat' => 'D', - 'pbn' => 'A1B1D1O1S2' - ]); - DB::table('aircraft')->insert([ - 'ac_type' => 'B48F', - 'icao_wtc' => 'H', - 'equipment' => 'SDE3FGHIM1M2RWXYZ', - 'transponder' => 'LB1', - 'perf_cat' => 'D', - 'pbn' => 'A1B1D1O1S2' - ]); - DB::table('aircraft')->insert([ - 'ac_type' => 'B752', - 'icao_wtc' => 'M', - 'equipment' => 'SDE2E3FGHIJ3J4J7RWXY', - 'transponder' => 'LB1', - 'perf_cat' => 'C', - 'pbn' => 'A1B1D1L1O1S1' - ]); - DB::table('aircraft')->insert([ - 'ac_type' => 'B753', - 'icao_wtc' => 'M', - 'equipment' => 'SDE2E3FGHIJ3J4J7RWXY', - 'transponder' => 'LB1', - 'perf_cat' => 'C', - 'pbn' => 'A1B1D1L1O1S1' - ]); - DB::table('aircraft')->insert([ - 'ac_type' => 'B762', - 'icao_wtc' => 'H', - 'equipment' => 'SDFGIRWY', - 'transponder' => 'S', - 'perf_cat' => 'D', - 'pbn' => 'A1B1D1O1' - ]); - DB::table('aircraft')->insert([ - 'ac_type' => 'B763', - 'icao_wtc' => 'H', - 'equipment' => 'SDE3FGHIM3RWXYZ', - 'transponder' => 'LB1', - 'perf_cat' => 'D', - 'pbn' => 'A1B1C1D1L1O1' - ]); - DB::table('aircraft')->insert([ - 'ac_type' => 'B76F', - 'icao_wtc' => 'H', - 'equipment' => 'SDE3FGHIM3RWXYZ', - 'transponder' => 'LB1', - 'perf_cat' => 'D', - 'pbn' => 'A1B1C1D1L1O1' - ]); - DB::table('aircraft')->insert([ - 'ac_type' => 'B764', - 'icao_wtc' => 'H', - 'equipment' => 'SDE3FGHIM3RWXY', - 'transponder' => 'LB1', - 'perf_cat' => 'D', - 'pbn' => 'A1B1C1D1L1O1' - ]); - DB::table('aircraft')->insert([ - 'ac_type' => 'B772', - 'icao_wtc' => 'H', - 'equipment' => 'SDE2E3FGHIJ5LORVWXY', - 'transponder' => 'LB1D1', - 'perf_cat' => 'D', - 'pbn' => 'A1B1D1S2T1' - ]); - DB::table('aircraft')->insert([ - 'ac_type' => 'B77F', - 'icao_wtc' => 'H', - 'equipment' => 'SDE1E2E3FGHIJ2J3J4J5M1RWXYZ', - 'transponder' => 'LB1D1', - 'perf_cat' => 'D', - 'pbn' => 'A1B1C1D1L1O1S2' - ]); - DB::table('aircraft')->insert([ - 'ac_type' => 'B77L', - 'icao_wtc' => 'H', - 'equipment' => 'SDE1E2E3FGHIJ2J3J4J5M1RWXYZ', - 'transponder' => 'LB1D1', - 'perf_cat' => 'D', - 'pbn' => 'A1B1C1D1L1O1S2' - ]); - DB::table('aircraft')->insert([ - 'ac_type' => 'B77W', - 'icao_wtc' => 'H', - 'equipment' => 'SDE1E2E3FGHIJ2J3J4J5M1RWXYZ', - 'transponder' => 'LB1D1', - 'perf_cat' => 'D', - 'pbn' => 'A1B1C1D1L1O1S2' - ]); - DB::table('aircraft')->insert([ - 'ac_type' => 'B788', - 'icao_wtc' => 'H', - 'equipment' => 'SDE1E2E3FGHIJ2J3J4J5M1RWXYZ', - 'transponder' => 'LB1D1', - 'perf_cat' => 'D', - 'pbn' => 'A1B1C1D1L1O1S2' - ]); - DB::table('aircraft')->insert([ - 'ac_type' => 'B789', - 'icao_wtc' => 'H', - 'equipment' => 'SDE1E2E3FGHIJ2J3J4J5M1RWXYZ', - 'transponder' => 'LB1D1', - 'perf_cat' => 'D', - 'pbn' => 'A1B1C1D1L1O1S2' - ]); - DB::table('aircraft')->insert([ - 'ac_type' => 'B78X', - 'icao_wtc' => 'H', - 'equipment' => 'SDE1E2E3FGHIJ2J3J4J5M1RWXYZ', - 'transponder' => 'LB1D1', - 'perf_cat' => 'D', - 'pbn' => 'A1B1C1D1L1O1S2' - ]); - DB::table('aircraft')->insert([ - 'ac_type' => 'DC10', - 'icao_wtc' => 'H', - 'equipment' => 'SDE1E2E3FGHIJ2J3J4J5M1RWXYZ', - 'transponder' => 'LB1D1', - 'perf_cat' => 'D', - 'pbn' => 'A1B1C1D1L1O1S2' - ]); - DB::table('aircraft')->insert([ - 'ac_type' => 'MD11', - 'icao_wtc' => 'H', - 'equipment' => 'SDE1E2E3FGHIJ2J3J4J5M1RWXYZ', - 'transponder' => 'LB1D1', - 'perf_cat' => 'D', - 'pbn' => 'A1B1C1D1L1O1S2' - ]); - DB::table('aircraft')->insert([ - 'ac_type' => 'MD1F', - 'icao_wtc' => 'H', - 'equipment' => 'SDE1E2E3FGHIJ2J3J4J5M1RWXYZ', - 'transponder' => 'LB1D1', - 'perf_cat' => 'D', - 'pbn' => 'A1B1C1D1L1O1S2' - ]); - DB::table('aircraft')->insert([ - 'ac_type' => 'CRJ2', - 'icao_wtc' => 'M', - 'equipment' => 'SDFGIRWZ', - 'transponder' => 'S', - 'perf_cat' => 'C', - 'pbn' => 'D1' - ]); - DB::table('aircraft')->insert([ - 'ac_type' => 'CRJ5', - 'icao_wtc' => 'M', - 'equipment' => 'SDFGIRWYZ', - 'transponder' => 'S', - 'perf_cat' => 'C', - 'pbn' => 'D1' - ]); - DB::table('aircraft')->insert([ - 'ac_type' => 'CRJ7', - 'icao_wtc' => 'M', - 'equipment' => 'SDFGIRWYZ', - 'transponder' => 'S', - 'perf_cat' => 'C', - 'pbn' => 'D1' - ]); - DB::table('aircraft')->insert([ - 'ac_type' => 'CRJ9', - 'icao_wtc' => 'M', - 'equipment' => 'SDFGIRWYZ', - 'transponder' => 'S', - 'perf_cat' => 'C', - 'pbn' => 'D1' - ]); - DB::table('aircraft')->insert([ - 'ac_type' => 'CRJX', - 'icao_wtc' => 'M', - 'equipment' => 'SDFGIRWYZ', - 'transponder' => 'S', - 'perf_cat' => 'C', - 'pbn' => 'D1' - ]); - DB::table('aircraft')->insert([ - 'ac_type' => 'E135', - 'icao_wtc' => 'M', - 'equipment' => 'SDFGIRWZ', - 'transponder' => 'S', - 'perf_cat' => 'C', - 'pbn' => 'D1' - ]); - DB::table('aircraft')->insert([ - 'ac_type' => 'E140', - 'icao_wtc' => 'M', - 'equipment' => 'SDFGIRWZ', - 'transponder' => 'S', - 'perf_cat' => 'C', - 'pbn' => 'D1' - ]); - DB::table('aircraft')->insert([ - 'ac_type' => 'E145', - 'icao_wtc' => 'M', - 'equipment' => 'SDFGIRWZ', - 'transponder' => 'S', - 'perf_cat' => 'C', - 'pbn' => 'D1' - ]); - DB::table('aircraft')->insert([ - 'ac_type' => 'E170', - 'icao_wtc' => 'M', - 'equipment' => 'SDE3GILORVW', - 'transponder' => 'S', - 'perf_cat' => 'C', - 'pbn' => 'B2B3B4B5C1D1S2' - ]); - DB::table('aircraft')->insert([ - 'ac_type' => 'E175', - 'icao_wtc' => 'M', - 'equipment' => 'SDE3GILORVW', - 'transponder' => 'S', - 'perf_cat' => 'C', - 'pbn' => 'B2B3B4B5C1D1S2' - ]); - DB::table('aircraft')->insert([ - 'ac_type' => 'E190', - 'icao_wtc' => 'M', - 'equipment' => 'SDE3GILORVW', - 'transponder' => 'S', - 'perf_cat' => 'C', - 'pbn' => 'B2B3B4B5C1D1S2' - ]); - DB::table('aircraft')->insert([ - 'ac_type' => 'E195', - 'icao_wtc' => 'M', - 'equipment' => 'SDE3GILORVW', - 'transponder' => 'S', - 'perf_cat' => 'C', - 'pbn' => 'B2B3B4B5C1D1S2' - ]); - } -} From 4e04f517e4ef6ff12becb8cfae9ac8f8f36c0ee1 Mon Sep 17 00:00:00 2001 From: kjporter Date: Sun, 1 Feb 2026 08:29:37 -0500 Subject: [PATCH 09/12] Update realops.blade.php Fix bidding toggle logic and simplify buttons --- resources/views/site/realops.blade.php | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/resources/views/site/realops.blade.php b/resources/views/site/realops.blade.php index 2ecee872b..9416b697e 100644 --- a/resources/views/site/realops.blade.php +++ b/resources/views/site/realops.blade.php @@ -62,7 +62,7 @@ Estimated Enroute Time (HH:MM) Gate Bidding Status - @if(auth()->guard('realops')->check() && toggleEnabled('realops_bidding')) + @if(auth()->guard('realops')->check()) Actions @endif @@ -100,9 +100,6 @@ @if(auth()->guard('realops')->check() && auth()->guard('realops')->id() == $f->assigned_pilot_id)

Assigned to You - @unlesstoggle('realops_bidding') - Cancel Bid - @endtoggle

@else

Assigned

@@ -113,18 +110,16 @@

Bidding Closed, No Assignment

@endif - @if(auth()->guard('realops')->check() && toggleEnabled('realops_bidding')) + @if(auth()->guard('realops')->check()) @if(auth()->guard('realops')->user()->id == $f->assigned_pilot_id) - Cancel Bid - @elseif($f->assigned_pilot) - - @else - Bid - @endif - @if($f->assigned_pilot) MyVATSIMPrefile Flight Plan SimBriefSend to SimBrief + Cancel Bid + @elseif(toggleEnabled('realops_bidding') && $f->assigned_pilot) + Bid + @elseif(toggleEnabled('realops_bidding')) + Bid @endif @endif From 1fef616751e6a3d056a782d8b6c9cf7a71dad0d5 Mon Sep 17 00:00:00 2001 From: kjporter Date: Fri, 6 Feb 2026 20:30:39 -0500 Subject: [PATCH 10/12] Fix ETE and null aircraft bugs --- app/Aircraft.php | 5 +++++ app/RealopsFlight.php | 13 +++++++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/app/Aircraft.php b/app/Aircraft.php index 019ca9e0c..e337d0333 100644 --- a/app/Aircraft.php +++ b/app/Aircraft.php @@ -19,5 +19,10 @@ public static function init() { self::$data = Collection::fromJson(Storage::disk('local')->get(self::FILENAME)); } } + + public static function fetch($acid) { + $ac = self::$data->where('ac_type', $acid)->first(); + return (is_null($ac)) ? null : (object) $ac; + } } Aircraft::init(); diff --git a/app/RealopsFlight.php b/app/RealopsFlight.php index 917280771..01411aa9a 100644 --- a/app/RealopsFlight.php +++ b/app/RealopsFlight.php @@ -82,6 +82,11 @@ private function eta(): string { return str_pad($hours, 2, '0', STR_PAD_LEFT) . str_pad($minutes, 2, '0', STR_PAD_LEFT); } + private function ete(): string { + $ete = explode(':', $this->est_time_enroute); + return str_pad($ete[0], 2, '0', STR_PAD_LEFT) . str_pad($ete[1], 2, '0', STR_PAD_LEFT); + } + public function getImageDirectory() { $flight_id = (!is_null($this->callsign)) ? $this->callsign : $this->flight_number; $airline = strtoupper(substr($flight_id, 0, 3)); @@ -96,13 +101,13 @@ public function getImageDirectory() { public function getIcaoFlightplanAttribute(): string { $icao_string = 'FPL-' . $this->flight_number . '-IS '; $icao_string .= '-' . $this->aircraft_type; - $ac = (object) Aircraft::$data->where('ac_type', $this->aircraft_type)->first(); + $ac = Aircraft::fetch($this->aircraft_type); if ($ac) { $icao_string .= '/' . $ac->icao_wtc . '-' . $ac->equipment . '/' . $ac->transponder; } $icao_string .= ' -' . $this->dep_airport . str_pad($this->timePart($this->dep_time, 'h'), 2, '0', STR_PAD_LEFT) . str_pad($this->timePart($this->dep_time, 'm'), 2, '0', STR_PAD_LEFT); $icao_string .= ' -' . PreferredRoute::routeLookup($this->dep_airport, $this->arr_airport); - $icao_string .= ' -' . $this->arr_airport . $this->eta(); + $icao_string .= ' -' . $this->arr_airport . $this->ete(); $icao_string .= ' -DOF/' . Carbon::parse($this->flight_date)->format('ymd') . ' OPR/' . $this->airline; if ($ac) { $icao_string .= ' PBN/' . $ac->pbn . ' PER/' . $ac->perf_cat; @@ -130,11 +135,11 @@ public function getSimbriefParamsAttribute(): string { 'pid' => $pilot->id, 'acdata' => null ]; - $ac = (object) Aircraft::$data->where('ac_type', $this->aircraft_type)->first(); + $ac = Aircraft::fetch($this->aircraft_type); if ($ac) { $ac_data = [ "pbn" => $ac->pbn, - "dof" => Carbon::parse($this->flight_date)->format('ymd'), // May need to remove this... SimBrief may generate the DOF from data above + "dof" => Carbon::parse($this->flight_date)->format('ymd'), "opr" => $this->airline, "per" => $ac->perf_cat, "cat" => $ac->icao_wtc, From 612978eeba48c3e60b8abfd082b5cd61c55f6fc0 Mon Sep 17 00:00:00 2001 From: Kyle Date: Sun, 22 Feb 2026 10:57:15 -0500 Subject: [PATCH 11/12] Update PreferredRoute.php --- app/PreferredRoute.php | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/app/PreferredRoute.php b/app/PreferredRoute.php index 9fccf98d4..cdb46a44b 100644 --- a/app/PreferredRoute.php +++ b/app/PreferredRoute.php @@ -15,11 +15,15 @@ public static function routeLookup(string $departure, string $arrival, string $a if (!$routes) { return ''; } - /* - PRDs normally contain the origin and destination ID as the first and last points. - SimBrief doesn't like this. Remove. - */ - $route = explode(' ', $routes->route_string); + $clean_route = self::remove_origin_destination_points($routes->route_string, $departure, $arrival); + return $clean_route; + } + + private static function remove_origin_destination_points($route_string, $departure, $arrival): string { + $route = explode(' ', $route_string); + if (!is_array($route)) { + return ''; + } if ($route[0] == substr($departure, 1)) { unset($route[0]); } From 9f348c100b2c0866b866c51fa998b7ad6eb38d58 Mon Sep 17 00:00:00 2001 From: Kyle Date: Sun, 22 Feb 2026 21:11:09 -0500 Subject: [PATCH 12/12] Update Aircraft.php --- app/Aircraft.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/Aircraft.php b/app/Aircraft.php index e337d0333..c1829c85d 100644 --- a/app/Aircraft.php +++ b/app/Aircraft.php @@ -18,6 +18,7 @@ public static function init() { if (Storage::disk('local')->exists(self::FILENAME)) { self::$data = Collection::fromJson(Storage::disk('local')->get(self::FILENAME)); } + self::$initialized = true; } public static function fetch($acid) {