diff --git a/flight-comparator/api_server.py b/flight-comparator/api_server.py index 23bb16a..71c3322 100644 --- a/flight-comparator/api_server.py +++ b/flight-comparator/api_server.py @@ -880,40 +880,56 @@ async def search_airports( raise HTTPException(status_code=500, detail=f"Failed to load airport data: {e}") query = q.lower().strip() - results = [] - # Search all airports + # Priority buckets — higher bucket = shown first + p0_exact_iata: list[Airport] = [] # IATA == query exactly (e.g. "BER") + p1_iata_prefix: list[Airport] = [] # IATA starts with query (e.g. "BE" → BER) + p2_city_prefix: list[Airport] = [] # city starts with query (e.g. "ber" → Berlin) + p3_city_contains: list[Airport] = [] # city contains query + p4_name_prefix: list[Airport] = [] # name starts with query + p5_name_contains: list[Airport] = [] # name contains query + p6_country: list[Airport] = [] # country code contains query + seen: set[str] = set() + for airport in airports_data: - # Skip invalid airport data (data quality issues in OpenFlights dataset) try: - # Search in IATA code (exact match prioritized) - if airport['iata'].lower() == query: - results.insert(0, Airport(**airport)) # Exact match at top + iata_l = airport['iata'].lower() + city_l = airport.get('city', '').lower() + name_l = airport['name'].lower() + country_l = airport.get('country', '').lower() + + if iata_l in seen: continue - # Search in IATA code (partial match) - if query in airport['iata'].lower(): - results.append(Airport(**airport)) + obj = Airport(**airport) + + if iata_l == query: + p0_exact_iata.append(obj) + elif iata_l.startswith(query): + p1_iata_prefix.append(obj) + elif city_l.startswith(query): + p2_city_prefix.append(obj) + elif query in city_l: + p3_city_contains.append(obj) + elif name_l.startswith(query): + p4_name_prefix.append(obj) + elif query in name_l: + p5_name_contains.append(obj) + elif query in country_l: + p6_country.append(obj) + else: continue - # Search in city name - if query in airport.get('city', '').lower(): - results.append(Airport(**airport)) - continue - - # Search in airport name - if query in airport['name'].lower(): - results.append(Airport(**airport)) - continue - - # Search in country code - if query in airport['country'].lower(): - results.append(Airport(**airport)) - continue + seen.add(iata_l) except Exception: # Skip airports with invalid data (e.g., invalid IATA codes like 'DU9') continue + results = ( + p0_exact_iata + p1_iata_prefix + p2_city_prefix + + p3_city_contains + p4_name_prefix + p5_name_contains + p6_country + ) + # Calculate pagination total = len(results) total_pages = math.ceil(total / limit) if total > 0 else 0 @@ -1607,6 +1623,13 @@ app.include_router(router_v1) # Helper Functions # ============================================================================= +# Airports missing from the OpenFlights dataset (opened/renamed after dataset freeze) +_MISSING_AIRPORTS = [ + {'iata': 'BER', 'name': 'Berlin Brandenburg Airport', 'city': 'Berlin', 'country': 'DE'}, + {'iata': 'IST', 'name': 'Istanbul Airport', 'city': 'Istanbul', 'country': 'TR'}, +] + + @lru_cache(maxsize=1) def get_airport_data(): """ @@ -1637,6 +1660,19 @@ def get_airport_data(): 'longitude': airport.get('lon', 0.0), }) + # Patch in modern airports missing from the OpenFlights dataset + existing_iatas = {a['iata'] for a in airports} + for extra in _MISSING_AIRPORTS: + if extra['iata'] not in existing_iatas: + airports.append({ + 'iata': extra['iata'], + 'name': extra['name'], + 'city': extra['city'], + 'country': extra['country'], + 'latitude': 0.0, + 'longitude': 0.0, + }) + return airports