Files
intercept/utils/airline_codes.py
Smittix a154601e86 fix: address PR #145 review issues
- Escape ac.icao, callsign, typeCode with escapeHtml() in aircraft card (XSS)
- Add linking comments between duplicated IATA_TO_ICAO mappings
- VDL2 sidebar: single-click selects aircraft, double-click opens modal
- Remove stale ICAOs from acarsAircraftIcaos in cleanupOldAircraft()
- Add null guard to drawPolarPlot() in weather-satellite.js
- Move deferred imports (translate_message, get_flight_correlator) to module level
- Check all frequency checkboxes by default on initial load
- Remove extra blank lines and uncertain MC/MCO airline code entry
- Add TODO comments linking duplicated renderAcarsCard implementations

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 20:42:14 +00:00

161 lines
5.2 KiB
Python

"""IATA ↔ ICAO airline code mapping for flight number translation."""
from __future__ import annotations
import re
# IATA (2-letter) → ICAO (3-letter) mapping for common airlines
# NOTE: Duplicated in templates/adsb_dashboard.html (JS IATA_TO_ICAO) — keep both in sync
IATA_TO_ICAO: dict[str, str] = {
# North America — Major
"AA": "AAL", # American Airlines
"DL": "DAL", # Delta Air Lines
"UA": "UAL", # United Airlines
"WN": "SWA", # Southwest Airlines
"B6": "JBU", # JetBlue Airways
"AS": "ASA", # Alaska Airlines
"NK": "NKS", # Spirit Airlines
"F9": "FFT", # Frontier Airlines
"G4": "AAY", # Allegiant Air
"HA": "HAL", # Hawaiian Airlines
"SY": "SCX", # Sun Country Airlines
"WS": "WJA", # WestJet
"AC": "ACA", # Air Canada
"WG": "WGN", # Sunwing Airlines
"TS": "TSC", # Air Transat
"PD": "POE", # Porter Airlines
"MX": "MXA", # Breeze Airways
"QX": "QXE", # Horizon Air
"OH": "COM", # PSA Airlines (Compass)
"OO": "SKW", # SkyWest Airlines
"YX": "RPA", # Republic Airways
"9E": "FLG", # Endeavor Air (Pinnacle)
"CP": "CPZ", # Compass Airlines
"PT": "SWQ", # Piedmont Airlines
"MQ": "ENY", # Envoy Air
"YV": "ASH", # Mesa Airlines
"AX": "LOF", # Trans States / GoJet
"ZW": "AWI", # Air Wisconsin
"G7": "GJS", # GoJet Airlines
"EV": "ASQ", # ExpressJet / Atlantic Southeast
"AM": "AMX", # Aeromexico
"VB": "VIV", # VivaAerobus
"4O": "AIJ", # Interjet
"Y4": "VOI", # Volaris
# North America — Cargo
"5X": "UPS", # UPS Airlines
"FX": "FDX", # FedEx Express
# Europe — Major
"BA": "BAW", # British Airways
"LH": "DLH", # Lufthansa
"AF": "AFR", # Air France
"KL": "KLM", # KLM Royal Dutch
"IB": "IBE", # Iberia
"AZ": "ITY", # ITA Airways
"SK": "SAS", # SAS Scandinavian
"AY": "FIN", # Finnair
"OS": "AUA", # Austrian Airlines
"LX": "SWR", # Swiss International
"SN": "BEL", # Brussels Airlines
"TP": "TAP", # TAP Air Portugal
"EI": "EIN", # Aer Lingus
"U2": "EZY", # easyJet
"FR": "RYR", # Ryanair
"W6": "WZZ", # Wizz Air
"VY": "VLG", # Vueling
"PC": "PGT", # Pegasus Airlines
"TK": "THY", # Turkish Airlines
"LO": "LOT", # LOT Polish
"BT": "BTI", # airBaltic
"DY": "NAX", # Norwegian Air Shuttle
"VS": "VIR", # Virgin Atlantic
"EW": "EWG", # Eurowings
# Asia-Pacific — Major
"SQ": "SIA", # Singapore Airlines
"CX": "CPA", # Cathay Pacific
"QF": "QFA", # Qantas
"JL": "JAL", # Japan Airlines
"NH": "ANA", # All Nippon Airways
"KE": "KAL", # Korean Air
"OZ": "AAR", # Asiana Airlines
"CI": "CAL", # China Airlines
"BR": "EVA", # EVA Air
"CZ": "CSN", # China Southern
"MU": "CES", # China Eastern
"CA": "CCA", # Air China
"AI": "AIC", # Air India
"GA": "GIA", # Garuda Indonesia
"TG": "THA", # Thai Airways
"MH": "MAS", # Malaysia Airlines
"PR": "PAL", # Philippine Airlines
"VN": "HVN", # Vietnam Airlines
"NZ": "ANZ", # Air New Zealand
"3K": "JSA", # Jetstar Asia
"JQ": "JST", # Jetstar Airways
"AK": "AXM", # AirAsia
"TR": "TGW", # Scoot
"5J": "CEB", # Cebu Pacific
# Middle East / Africa
"EK": "UAE", # Emirates
"QR": "QTR", # Qatar Airways
"EY": "ETD", # Etihad Airways
"GF": "GFA", # Gulf Air
"SV": "SVA", # Saudia
"ET": "ETH", # Ethiopian Airlines
"MS": "MSR", # EgyptAir
"SA": "SAA", # South African Airways
"RJ": "RJA", # Royal Jordanian
"WY": "OMA", # Oman Air
# South America
"LA": "LAN", # LATAM Airlines
"G3": "GLO", # Gol Transportes Aéreos
"AD": "AZU", # Azul Brazilian Airlines
"AV": "AVA", # Avianca
"CM": "CMP", # Copa Airlines
"AR": "ARG", # Aerolíneas Argentinas
}
# Build reverse mapping (ICAO → IATA)
ICAO_TO_IATA: dict[str, str] = {v: k for k, v in IATA_TO_ICAO.items()}
# Regex to split flight number into airline prefix and numeric part
_FLIGHT_RE = re.compile(r'^([A-Z]{2,3})(\d+[A-Z]?)$')
def translate_flight(flight: str) -> list[str]:
"""Translate a flight number to all possible equivalent forms.
Given "UA2412" (IATA), returns ["UAL2412"] (ICAO).
Given "UAL2412" (ICAO), returns ["UA2412"] (IATA).
Returns empty list if no translation found.
"""
if not flight:
return []
upper = flight.strip().upper()
m = _FLIGHT_RE.match(upper)
if not m:
return []
prefix, number = m.group(1), m.group(2)
results = []
# Try IATA → ICAO
if prefix in IATA_TO_ICAO:
results.append(IATA_TO_ICAO[prefix] + number)
# Try ICAO → IATA
if prefix in ICAO_TO_IATA:
results.append(ICAO_TO_IATA[prefix] + number)
return results
def expand_search_terms(terms: set[str]) -> set[str]:
"""Expand a set of callsign/flight search terms with translated variants."""
expanded = set(terms)
for term in list(terms):
for translated in translate_flight(term):
expanded.add(translated)
return expanded