225 lines
6.9 KiB
Python
Executable file
225 lines
6.9 KiB
Python
Executable file
#!/usr/bin/python3
|
|
|
|
import sys
|
|
from typing import Any, Dict, List
|
|
|
|
import requests
|
|
import yaml
|
|
|
|
# Define the base URL for the Wikidata API
|
|
WIKIDATA_API_URL = "https://www.wikidata.org/w/api.php"
|
|
|
|
|
|
def get_entity_label(qid: str) -> str | None:
|
|
"""
|
|
Fetches the English label for a given Wikidata entity QID.
|
|
|
|
Args:
|
|
qid (str): The Wikidata entity ID (e.g., "Q6106").
|
|
|
|
Returns:
|
|
Optional[str]: The English label of the entity, or None if not found.
|
|
"""
|
|
params: Dict[str, str] = {
|
|
"action": "wbgetentities",
|
|
"ids": qid,
|
|
"format": "json",
|
|
"props": "labels",
|
|
"languages": "en",
|
|
}
|
|
try:
|
|
response = requests.get(WIKIDATA_API_URL, params=params)
|
|
response.raise_for_status()
|
|
entity = response.json().get("entities", {}).get(qid, {})
|
|
return entity.get("labels", {}).get("en", {}).get("value")
|
|
except requests.exceptions.RequestException as e:
|
|
print(f"Error fetching label for QID {qid}: {e}", file=sys.stderr)
|
|
return None
|
|
|
|
|
|
def get_entity_details(qid: str) -> dict[str, Any] | None:
|
|
"""
|
|
Fetches and processes detailed information for a given airport QID.
|
|
|
|
Args:
|
|
qid (str): The QID of the airport Wikidata entity.
|
|
|
|
Returns:
|
|
Optional[Dict[str, Any]]: A dictionary containing the detailed airport data.
|
|
"""
|
|
params: Dict[str, str] = {
|
|
"action": "wbgetentities",
|
|
"ids": qid,
|
|
"format": "json",
|
|
"props": "claims|labels",
|
|
}
|
|
try:
|
|
response = requests.get(WIKIDATA_API_URL, params=params)
|
|
response.raise_for_status()
|
|
entity = response.json().get("entities", {}).get(qid, {})
|
|
if not entity:
|
|
return None
|
|
|
|
claims = entity.get("claims", {})
|
|
|
|
# Helper to safely extract claim values
|
|
def get_simple_claim_value(prop_id: str) -> str | None:
|
|
claim = claims.get(prop_id)
|
|
if not claim:
|
|
return None
|
|
v = claim[0].get("mainsnak", {}).get("datavalue", {}).get("value")
|
|
assert isinstance(v, str) or v is None
|
|
return v
|
|
|
|
# Get IATA code, name, and website
|
|
iata = get_simple_claim_value("P238")
|
|
name = entity.get("labels", {}).get("en", {}).get("value")
|
|
website = get_simple_claim_value("P856")
|
|
|
|
# Get City Name by resolving its QID
|
|
city_qid_claim = claims.get("P131")
|
|
city_name = None
|
|
if city_qid_claim:
|
|
city_qid = (
|
|
city_qid_claim[0]
|
|
.get("mainsnak", {})
|
|
.get("datavalue", {})
|
|
.get("value", {})
|
|
.get("id")
|
|
)
|
|
if city_qid:
|
|
city_name = get_entity_label(city_qid)
|
|
|
|
# Get coordinates
|
|
coords_claim = claims.get("P625")
|
|
latitude, longitude = None, None
|
|
if coords_claim:
|
|
coords = (
|
|
coords_claim[0]
|
|
.get("mainsnak", {})
|
|
.get("datavalue", {})
|
|
.get("value", {})
|
|
)
|
|
latitude = coords.get("latitude")
|
|
longitude = coords.get("longitude")
|
|
|
|
# Get elevation
|
|
elevation_claim = claims.get("P2044")
|
|
elevation = None
|
|
if elevation_claim:
|
|
amount_str = (
|
|
elevation_claim[0]
|
|
.get("mainsnak", {})
|
|
.get("datavalue", {})
|
|
.get("value", {})
|
|
.get("amount")
|
|
)
|
|
if amount_str:
|
|
elevation = float(amount_str) if "." in amount_str else int(amount_str)
|
|
|
|
# Get Country Code
|
|
country_claim = claims.get("P17")
|
|
country_code = None
|
|
if country_claim:
|
|
country_qid = (
|
|
country_claim[0]
|
|
.get("mainsnak", {})
|
|
.get("datavalue", {})
|
|
.get("value", {})
|
|
.get("id")
|
|
)
|
|
if country_qid:
|
|
# Fetch the ISO 3166-1 alpha-2 code (P297) for the country entity
|
|
country_code_params = {
|
|
"action": "wbgetclaims",
|
|
"entity": country_qid,
|
|
"property": "P297",
|
|
"format": "json",
|
|
}
|
|
country_res = requests.get(WIKIDATA_API_URL, params=country_code_params)
|
|
country_res.raise_for_status()
|
|
country_claims = country_res.json().get("claims", {}).get("P297")
|
|
if country_claims:
|
|
code = (
|
|
country_claims[0]
|
|
.get("mainsnak", {})
|
|
.get("datavalue", {})
|
|
.get("value")
|
|
)
|
|
if code:
|
|
country_code = code.lower()
|
|
|
|
data = {
|
|
"iata": iata,
|
|
"name": name,
|
|
"city": city_name,
|
|
"qid": qid,
|
|
"latitude": latitude,
|
|
"longitude": longitude,
|
|
"elevation": elevation,
|
|
"website": website,
|
|
"country": country_code,
|
|
}
|
|
|
|
# Return the final structure, filtering out null values for cleaner output
|
|
return {iata: {k: v for k, v in data.items() if v is not None}}
|
|
|
|
except requests.exceptions.RequestException as e:
|
|
print(f"Error fetching entity details for QID {qid}: {e}", file=sys.stderr)
|
|
return None
|
|
|
|
|
|
def find_airport_by_iata(iata_code: str) -> dict[str, Any] | None:
|
|
"""
|
|
Finds an airport by its IATA code using Wikidata's search API.
|
|
|
|
Args:
|
|
iata_code (str): The IATA code of the airport (e.g., "PDX").
|
|
|
|
Returns:
|
|
Optional[Dict[str, Any]]: A dictionary with the airport data or None.
|
|
"""
|
|
params: Dict[str, str] = {
|
|
"action": "query",
|
|
"list": "search",
|
|
"srsearch": f"haswbstatement:P238={iata_code.upper()}",
|
|
"format": "json",
|
|
}
|
|
try:
|
|
response = requests.get(WIKIDATA_API_URL, params=params)
|
|
response.raise_for_status()
|
|
search_results: List[Dict[str, Any]] = (
|
|
response.json().get("query", {}).get("search", [])
|
|
)
|
|
|
|
if not search_results:
|
|
print(f"No airport found with IATA code: {iata_code}", file=sys.stderr)
|
|
return None
|
|
|
|
qid = search_results[0]["title"]
|
|
return get_entity_details(qid)
|
|
|
|
except requests.exceptions.RequestException as e:
|
|
print(f"Error searching on Wikidata API: {e}", file=sys.stderr)
|
|
return None
|
|
|
|
|
|
if __name__ == "__main__":
|
|
if len(sys.argv) != 2:
|
|
print("Usage: python airport_lookup.py <IATA_CODE>", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
iata_code_arg = sys.argv[1]
|
|
airport_data = find_airport_by_iata(iata_code_arg)
|
|
|
|
if airport_data:
|
|
print(
|
|
yaml.safe_dump(
|
|
airport_data,
|
|
default_flow_style=False,
|
|
allow_unicode=True,
|
|
sort_keys=False,
|
|
),
|
|
end="",
|
|
)
|