import json import os from typing import Dict, Any import requests from .metrics import CACHE_HITS_TOTAL, CACHE_MISSES_TOTAL, OPENMETEO_CALLS_TOTAL API_URL = "https://geocoding-api.open-meteo.com/v1/search" CITIES = ["Tel Aviv", "Beersheba", "Jerusalem", "Szeged"] CACHE_FILE = os.environ.get("CACHE_FILE", "coordinates_cache.json") def _fetch_coordinates(city: str) -> Dict[str, Any]: OPENMETEO_CALLS_TOTAL.labels(city=city).inc() params = {"name": city, "count": 1} r = requests.get(API_URL, params=params, timeout=10) r.raise_for_status() data = r.json() if "results" not in data or not data["results"]: raise ValueError(f"No results found for {city}") result = data["results"][0] return { "name": result.get("name"), "latitude": result.get("latitude"), "longitude": result.get("longitude"), "country": result.get("country"), } def _load_cache() -> Dict[str, Any] | None: if os.path.exists(CACHE_FILE): with open(CACHE_FILE, "r", encoding="utf-8") as f: return json.load(f) return None def _save_cache(data: Dict[str, Any]) -> None: with open(CACHE_FILE, "w", encoding="utf-8") as f: json.dump(data, f, indent=2) def get_all_coordinates() -> Dict[str, Any]: cached = _load_cache() # Check if all cities from CITIES list are in cache if cached and all(city in cached for city in CITIES): CACHE_HITS_TOTAL.inc() # Filter cache to only include cities from CITIES list filtered_data = {city: cached[city] for city in CITIES} return {"source": "cache", "data": filtered_data} CACHE_MISSES_TOTAL.inc() # Fetch missing cities results = cached if cached else {} for city in CITIES: if city not in results: results[city] = _fetch_coordinates(city) _save_cache(results) # Return only cities from CITIES list filtered_data = {city: results[city] for city in CITIES} return {"source": "open-meteo", "data": filtered_data} def get_coordinates_for_city(city: str) -> Dict[str, Any]: cached = _load_cache() if cached and city in cached: CACHE_HITS_TOTAL.inc() return {"source": "cache", "data": cached[city]} CACHE_MISSES_TOTAL.inc() result = _fetch_coordinates(city) # Update cache with the new city if cached is None: cached = {} cached[city] = result _save_cache(cached) return {"source": "open-meteo", "data": result}