import sqlite3 from contextlib import contextmanager import config SCHEMA = """ CREATE TABLE IF NOT EXISTS pending_flights ( id INTEGER PRIMARY KEY AUTOINCREMENT, created_at TEXT NOT NULL, updated_at TEXT NOT NULL, status TEXT NOT NULL DEFAULT 'queued' CHECK (status IN ('queued','pending_review','track_unavailable', 'approved','rejected','decode_failed')), image_path TEXT NOT NULL, raw_bcbp TEXT, passenger_name TEXT, pnr TEXT, from_iata TEXT, to_iata TEXT, from_icao TEXT, to_icao TEXT, operating_carrier_iata TEXT, operating_carrier_icao TEXT, flight_number TEXT, julian_day INTEGER, flight_date TEXT, known_departure_time TEXT, compartment TEXT, seat TEXT, process_after TEXT, fetch_attempts INTEGER NOT NULL DEFAULT 0, track_coordinates TEXT, track_times TEXT, track_point_count INTEGER, flightaware_url TEXT, takeoff_actual TEXT, landing_actual TEXT, gate_departure_scheduled TEXT, gate_departure_actual TEXT, gate_arrival_scheduled TEXT, gate_arrival_actual TEXT, departure_utc_offset TEXT, arrival_utc_offset TEXT, schedule_error TEXT, airtrail_flight_id INTEGER, parse_warnings TEXT, error_message TEXT, reviewed_at TEXT, approved_at TEXT ); CREATE INDEX IF NOT EXISTS idx_pending_flights_status_process_after ON pending_flights(status, process_after); CREATE TABLE IF NOT EXISTS settings ( key TEXT PRIMARY KEY, value TEXT ); """ def get_connection() -> sqlite3.Connection: config.ensure_dirs() conn = sqlite3.connect(config.DB_PATH, timeout=30) conn.row_factory = sqlite3.Row conn.execute("PRAGMA journal_mode=WAL") conn.execute("PRAGMA foreign_keys=ON") return conn @contextmanager def connect(): conn = get_connection() try: yield conn conn.commit() finally: conn.close() # Columns added after the original schema. CREATE TABLE IF NOT EXISTS above # is a no-op against an already-existing table, so anything added there # needs a matching entry here too or it silently never appears on an # upgraded (vs. freshly created) database. MIGRATIONS = [ ("takeoff_actual", "TEXT"), ("landing_actual", "TEXT"), ("gate_departure_scheduled", "TEXT"), ("gate_departure_actual", "TEXT"), ("gate_arrival_scheduled", "TEXT"), ("gate_arrival_actual", "TEXT"), ("departure_utc_offset", "TEXT"), ("arrival_utc_offset", "TEXT"), ("schedule_error", "TEXT"), ] def _run_migrations(conn: sqlite3.Connection) -> None: existing = {row["name"] for row in conn.execute("PRAGMA table_info(pending_flights)")} for column, coltype in MIGRATIONS: if column not in existing: conn.execute(f"ALTER TABLE pending_flights ADD COLUMN {column} {coltype}") def init_db() -> None: with connect() as conn: conn.executescript(SCHEMA) _run_migrations(conn)