Added Notifications

This commit is contained in:
2026-05-18 14:31:53 +10:00
parent 1d5b9f340f
commit 10b5b6a5c9
18 changed files with 545 additions and 166 deletions
+7 -76
View File
@@ -7,6 +7,7 @@ use App\Models\Aircraft;
use App\Models\IataEquipmentCode;
use App\Models\Notification;
use App\Models\UserFlight;
use App\Services\FlightStatsService;
use Carbon\Carbon;
use Carbon\CarbonImmutable;
use Illuminate\Console\Attributes\Description;
@@ -19,80 +20,10 @@ use Illuminate\Support\Facades\Log;
#[Description('Command description')]
class UpdateDepartedFlights extends Command
{
/**
* Fetch live flight data from FlightStats.
* Returns null if the request fails or no data is found.
*/
protected function fetchFlightData(string $airlineCode, string $flightNumber, CarbonImmutable $date): ?FlightStatData
public function __construct(protected FlightStatsService $flightStats)
{
$url = sprintf(
'https://www.flightstats.com/v2/api-next/flight-tracker/%s/%s/%d/%d/%d',
$airlineCode,
$flightNumber,
$date->year,
$date->month,
$date->day,
);
$response = Http::withOptions([
'verify' => config('app.verify_ssl'),
])->get($url);
if (!$response->successful()) {
Log::warning("FlightStats request failed for {$airlineCode}{$flightNumber}: HTTP {$response->status()}");
return null;
}
$flightData = $response->json('data');
if (empty($flightData)) {
return null;
}
return FlightStatData::fromApiResponse($flightData);
}
/**
* Attempt to resolve the best matching Aircraft record for a given IATA equipment code.
* Prefers passenger variants over freighters/BBJs where multiple matches exist.
*/
protected function guessAircraftFromIata(string $iataCode): ?Aircraft
{
$equipment = IataEquipmentCode::where('iata_code', $iataCode)->first();
if (!$equipment) {
Log::info("Unknown IATA equipment code: {$iataCode}");
return null;
}
$candidates = Aircraft::where('designator', $equipment->icao_code)->get();
if ($candidates->isEmpty()) {
Log::info("No aircraft found for ICAO: {$equipment->icao_code} (IATA: {$iataCode})");
return null;
}
if ($candidates->count() === 1) {
return $candidates->first();
}
// Prefer passenger variants — deprioritise freighters, BBJs, and convertibles
$deprioritised = ['freighter', 'bbj', 'combi', 'mixed', 'cargo', 'prestige', 'winglet', 'sharklet', 'freight'];
$pattern = implode('|', $deprioritised);
$passengerVariants = $candidates->filter(
fn(Aircraft $a) => !preg_match("/({$pattern})/i", $a->display_name_short)
);
if ($passengerVariants->count() === 1) {
return $passengerVariants->first();
}
if ($passengerVariants->isNotEmpty()) {
return $passengerVariants->first();
}
return $candidates->first();
parent::__construct();
}
protected function notifyDataError(UserFlight $flight): void
@@ -110,7 +41,7 @@ class UpdateDepartedFlights extends Command
/**
* Execute the console command.
*/
public function handle()
public function handle(): void
{
$now = now()->utc();
$oneHourAgo = $now->copy()->subHours(1);
@@ -136,7 +67,7 @@ class UpdateDepartedFlights extends Command
$arrivalDate = $flight->arrival_date->setTimezone($flight->arrivalAirport->timezone);
$data = $this->fetchFlightData($airlineCode, $flightNumber, $arrivalDate);
$data = $this->flightStats->fetchFlightData($airlineCode, $flightNumber, $arrivalDate);
if (!$data) {
$this->warn("No flight data returned for {$airlineCode}{$flightNumber}");
@@ -170,7 +101,7 @@ class UpdateDepartedFlights extends Command
$currentAircraft = $flight->aircraft;
if ($currentAircraft?->iata_code !== $data->equipment_iata) {
$match = $this->guessAircraftFromIata($data->equipment_iata);
$match = $this->flightStats->guessAircraftFromIata($data->equipment_iata);
if ($match) {
$updates['aircraft_id'] = $match->id;