kcc-agri / advisors /weather.py
hritikm15's picture
Day 9 β€” v4 merge deploy: kcc_core + advisors + Proof tab + pest heatmap
49818d2 verified
"""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"&current_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,
}