| """Open-Meteo wrapper. Free, no key needed. Resolves to (lat, lon) β block, |
| district, or state centroid β depending on what the caller has. |
| """ |
| from __future__ import annotations |
| import logging |
| from functools import lru_cache |
| from typing import Optional |
|
|
| import httpx |
|
|
| from kcc_core import config |
|
|
| logger = logging.getLogger(__name__) |
|
|
|
|
| @lru_cache(maxsize=4096) |
| def forecast(lat: float, lon: float, days: int = 7) -> dict: |
| """7-day forecast cached per (lat, lon) at ~3 decimal resolution.""" |
| url = (f"{config.OPEN_METEO_BASE}" |
| f"?latitude={lat:.3f}&longitude={lon:.3f}" |
| f"&daily=temperature_2m_max,temperature_2m_min,relative_humidity_2m_mean," |
| f"precipitation_sum,wind_speed_10m_max,weathercode" |
| f"¤t_weather=true&timezone=Asia%2FKolkata" |
| f"&forecast_days={min(max(days,1),16)}") |
| try: |
| r = httpx.get(url, timeout=12.0) |
| r.raise_for_status() |
| return r.json() |
| except Exception as e: |
| logger.warning(f"[weather] failed for ({lat},{lon}): {e}") |
| return {} |
|
|
|
|
| def summary(lat: float, lon: float) -> dict: |
| """Compact summary: temp_mean, humidity_mean, rain_mm_7d, hot_days, |
| rain_days, dominant_weathercode.""" |
| fc = forecast(lat, lon, days=7) |
| daily = fc.get("daily", {}) |
| if not daily: |
| return {} |
| tmax = daily.get("temperature_2m_max", []) |
| tmin = daily.get("temperature_2m_min", []) |
| hum = daily.get("relative_humidity_2m_mean", []) |
| rain = daily.get("precipitation_sum", []) |
| if not tmax: |
| return {} |
| n = len(tmax) |
| tmean = [(a + b) / 2 for a, b in zip(tmax, tmin) if a is not None and b is not None] |
| return { |
| "temp_mean": round(sum(tmean) / max(len(tmean), 1), 1), |
| "temp_max": max(tmax) if tmax else None, |
| "temp_min": min(tmin) if tmin else None, |
| "humidity_mean": round(sum(hum) / max(len(hum), 1), 1) if hum else None, |
| "rain_mm_7d": round(sum(rain), 1) if rain else 0.0, |
| "rain_days": sum(1 for r in (rain or []) if (r or 0) > 1.0), |
| "hot_days": sum(1 for t in tmax if t and t >= 35), |
| "days": n, |
| } |
|
|