Added Imported Flights Table

This commit is contained in:
2026-04-03 23:59:40 +10:00
parent 6a88d0cdfb
commit 877caa3291
16 changed files with 622 additions and 21 deletions
@@ -0,0 +1,192 @@
<?php
namespace App\Http\Controllers;
use App\Models\Airline;
use App\Models\ImportedFlight;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class FlightImportController extends Controller
{
const array FLIGHT_CLASSES = [
0 => 'Please Select',
1 => 'Economy',
2 => 'Business',
3 => 'First',
4 => 'Premium Economy',
5 => 'Private',
];
const array SEAT_TYPES = [
0 => 'Please Select',
1 => 'Window',
2 => 'Middle',
3 => 'Aisle',
];
const array FLIGHT_REASONS = [
0 => 'Please Select',
1 => 'Pleasure',
2 => 'Business',
3 => 'Crew',
4 => 'Other',
];
private function formatTime(?string $time): string
{
if (!$time) return '00:00';
[$hours, $minutes] = explode(':', $time);
return str_pad($hours, 2, '0', STR_PAD_LEFT) . ':' . $minutes;
}
public function getPossibleAirlines(string $airlineQuery) {
preg_match('/\((\w{2,3})\/(\w{3,4})\)/', $airlineQuery, $matches);
$iata = $matches[1] ?? null;
$icao = $matches[2] ?? null;
$airlines = Airline::when($iata || $icao, function ($query) use ($iata, $icao) {
$query->orderByRaw("
CASE
WHEN \"IATA_code\" = ? AND \"ICAO_code\" = ? THEN 0
WHEN \"IATA_code\" = ? THEN 1
WHEN \"ICAO_code\" = ? THEN 2
ELSE 3
END
", [$iata, $icao, $iata, $icao])
->where(function ($q) use ($iata, $icao) {
$q->where('IATA_code', $iata)
->orWhere('ICAO_code', $icao);
});
})
->orderByDesc('active')
->limit(10)
->get(['id', 'name', 'IATA_code', 'ICAO_code'])
->map(fn($a) => [
'value' => $a->id,
'title' => "{$a->name} ({$a->IATA_code}/{$a->ICAO_code})",
])
->values()
->toArray();
return [
'airline_options' => $airlines,
'raw_airline' => $airlineQuery,
];
}
public function reconcile(Request $request)
{
$user = Auth::user();
$flightToReconcile = ImportedFlight::where('user_id', $user->id)->orderBy('id')->first();
if (!$flightToReconcile) {
return null;
}
$date = null;
if ($flightToReconcile->date) {
$date = Carbon::createFromFormat('m-d-y', $flightToReconcile->date)->format('Y-m-d');
}
return [
'flight_classes' => collect(self::FLIGHT_CLASSES)->map(fn($title, $value) => [
'value' => $value,
'title' => $title,
])->values(),
'flight_reasons' => collect(self::FLIGHT_REASONS)->map(fn($title, $value) => [
'value' => $value,
'title' => $title,
])->values(),
'seat_types' => collect(self::SEAT_TYPES)->map(fn($title, $value) => [
'value' => $value,
'title' => $title,
])->values(),
'flight_number' => $flightToReconcile->flight_number ?? '',
'date' => $date ?? '',
'dep_time' => $this->formatTime($flightToReconcile->dep_time),
'arr_time' => $this->formatTime($flightToReconcile->arr_time),
'duration' => $this->formatTime($flightToReconcile->duration),
'registration' => $flightToReconcile->registration ?? '',
'note' => $flightToReconcile->note ?? '',
'seat_number' => $flightToReconcile->seat_number ?? '',
'flight_class' => $flightToReconcile->flight_class ?? '',
'seat_type' => $flightToReconcile->seat_type ?? '',
'flight_reason' => $flightToReconcile->flight_reason ?? '',
'airline_options' => $this->getPossibleAirlines($flightToReconcile->airline ?? '')['airline_options'],
];
}
public function store(Request $request)
{
try {
$request->validate([
'csv' => ['required', 'file', 'mimes:csv,txt', 'max:10240'],
]);
$path = $request->file('csv')->getRealPath();
$handle = fopen($path, 'r');
fgetcsv($handle);
// Read and normalise header row
$headers = array_map(
fn($h) => strtolower(trim(str_replace(' ', '_', $h))),
fgetcsv($handle)
);
\Log::debug('CSV headers', $headers);
$firstRow = fgetcsv($handle);
\Log::debug('First row', $firstRow ?? ['empty']);
\Log::debug('Header count: ' . count($headers) . ', Row count: ' . count($firstRow ?? []));
$map = [
'date' => 'date',
'flight_number' => 'flight_number',
'from' => 'from',
'to' => 'to',
'dep_time' => 'dep_time',
'arr_time' => 'arr_time',
'duration' => 'duration',
'airline' => 'airline',
'aircraft' => 'aircraft',
'registration' => 'registration',
'seat_number' => 'seat_number',
'seat_type' => 'seat_type',
'flight_class' => 'flight_class',
'flight_reason' => 'flight_reason',
'note' => 'note',
];
$userId = Auth::id();
$imported = 0;
while (($row = fgetcsv($handle)) !== false) {
if (count($row) !== count($headers)) {
continue; // skip malformed rows
}
$raw = array_combine($headers, $row);
$data = ['user_id' => $userId];
foreach ($map as $csvKey => $column) {
$data[$column] = $raw[$csvKey] ?? null;
}
ImportedFlight::create($data);
$imported++;
}
fclose($handle);
return response()->json(['imported' => $imported]);
} catch (\Exception $e) {
return response()->json(['message' => $e->getMessage()], 500);
}
}
}
+7
View File
@@ -21,6 +21,13 @@ class LogoController extends Controller
]);
}
public function getLogoById(int $id){
$airline = Airline::where('id', $id)
->first();
return $this->getAirlineLogo($airline);
}
public function getLogoByCode(string $code){
$column = strlen($code) == 2
+35
View File
@@ -0,0 +1,35 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class ImportedFlight extends Model
{
public $timestamps = false;
protected $fillable = [
'user_id',
'date',
'flight_number',
'from',
'to',
'dep_time',
'arr_time',
'duration',
'airline',
'aircraft',
'registration',
'seat_number',
'seat_type',
'flight_class',
'flight_reason',
'note',
];
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
}
+7
View File
@@ -7,6 +7,8 @@ use Database\Factories\UserFactory;
use Illuminate\Database\Eloquent\Attributes\Fillable;
use Illuminate\Database\Eloquent\Attributes\Hidden;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
@@ -29,4 +31,9 @@ class User extends Authenticatable
'password' => 'hashed',
];
}
public function ImportedFlights(): HasMany
{
return $this->hasMany(ImportedFlight::class);
}
}