avwx.station.search
Station text-based search.
1"""Station text-based search.""" 2 3# stdlib 4from __future__ import annotations 5 6from contextlib import suppress 7from functools import lru_cache 8from typing import TYPE_CHECKING 9 10# module 11from avwx.exceptions import MissingExtraModule 12from avwx.load_utils import LazyCalc 13from avwx.station.meta import STATIONS 14from avwx.station.station import Station, station_filter 15 16if TYPE_CHECKING: 17 from collections.abc import Iterable 18 19# Catch import error only if user attemps a text search 20with suppress(ModuleNotFoundError): 21 from rapidfuzz import fuzz, process, utils 22 23 24TYPE_ORDER = [ 25 "large_airport", 26 "medium_airport", 27 "small_airport", 28 "seaplane_base", 29 "heliport", 30 "balloonport", 31 "weather_station", 32] 33 34 35def _format_search(airport: dict, keys: Iterable[str]) -> str | None: 36 values = [airport.get(k) for k in keys] 37 code = values[0] or values[2] 38 if not code: 39 return None 40 values.insert(0, code) 41 return " - ".join(k for k in values if k) 42 43 44def _build_corpus() -> list[str]: 45 keys = ("icao", "iata", "gps", "local", "city", "state", "name") 46 return [text for s in STATIONS.values() if (text := _format_search(s, keys))] 47 48 49_CORPUS = LazyCalc(_build_corpus) 50 51 52def _sort_key(result: tuple[Station, int]) -> tuple[int, ...]: 53 station, score = result 54 try: 55 type_order = TYPE_ORDER.index(station.type) 56 except ValueError: 57 type_order = 10 58 return (score, 10 - type_order) 59 60 61@lru_cache(maxsize=128) 62def search( 63 text: str, 64 limit: int = 10, 65 *, 66 is_airport: bool = False, 67 sends_reports: bool = True, 68) -> list[Station]: 69 """Text search for stations against codes, name, city, and state. 70 71 Results may be shorter than limit value. 72 """ 73 try: 74 results = process.extract( 75 text, 76 _CORPUS.value, 77 limit=limit * 20, 78 scorer=fuzz.token_set_ratio, 79 processor=utils.default_process, 80 ) 81 except NameError as name_error: 82 extra = "fuzz" 83 raise MissingExtraModule(extra) from name_error 84 results = [(Station.from_code(k[:4]), s) for k, s, _ in results] 85 results.sort(key=_sort_key, reverse=True) 86 results = [s for s, _ in results if station_filter(s, is_airport=is_airport, reporting=sends_reports)] 87 return results[:limit] if len(results) > limit else results
TYPE_ORDER =
['large_airport', 'medium_airport', 'small_airport', 'seaplane_base', 'heliport', 'balloonport', 'weather_station']
@lru_cache(maxsize=128)
def
search( text: str, limit: int = 10, *, is_airport: bool = False, sends_reports: bool = True) -> list[avwx.station.Station]:
62@lru_cache(maxsize=128) 63def search( 64 text: str, 65 limit: int = 10, 66 *, 67 is_airport: bool = False, 68 sends_reports: bool = True, 69) -> list[Station]: 70 """Text search for stations against codes, name, city, and state. 71 72 Results may be shorter than limit value. 73 """ 74 try: 75 results = process.extract( 76 text, 77 _CORPUS.value, 78 limit=limit * 20, 79 scorer=fuzz.token_set_ratio, 80 processor=utils.default_process, 81 ) 82 except NameError as name_error: 83 extra = "fuzz" 84 raise MissingExtraModule(extra) from name_error 85 results = [(Station.from_code(k[:4]), s) for k, s, _ in results] 86 results.sort(key=_sort_key, reverse=True) 87 results = [s for s, _ in results if station_filter(s, is_airport=is_airport, reporting=sends_reports)] 88 return results[:limit] if len(results) > limit else results
Text search for stations against codes, name, city, and state.
Results may be shorter than limit value.