import requests from datetime import datetime from typing import Dict from .config import OPENWEATHER_BASE, get_openweather_key, UNITS from .utils import day_list def geocode_city(city: str) -> Dict: url = f"{OPENWEATHER_BASE}/geo/1.0/direct" r = requests.get( url, params={"q": city, "limit": 1, "appid": get_openweather_key()}, timeout=20, ) r.raise_for_status() arr = r.json() if not arr: raise RuntimeError(f"City not found: {city}") d = arr[0] return { "name": d.get("name"), "lat": d.get("lat"), "lon": d.get("lon"), "country": d.get("country"), } def forecast_5day(lat: float, lon: float, units=UNITS) -> Dict: # 5 day / 3-hour forecast url = f"{OPENWEATHER_BASE}/data/2.5/forecast" r = requests.get( url, params={"lat": lat, "lon": lon, "appid": get_openweather_key(), "units": units}, timeout=25, ) r.raise_for_status() return r.json() def summarize_forecast_for_range(city: str, start_date, days: int) -> Dict: loc = geocode_city(city) fc = forecast_5day(loc["lat"], loc["lon"]) daily = {} for item in fc.get("list", []): dt_txt = item.get("dt_txt") # 'YYYY-MM-DD HH:MM:SS' dt = datetime.strptime(dt_txt, "%Y-%m-%d %H:%M:%S") dkey = dt.date().isoformat() temp = item.get("main", {}).get("temp") weather = item.get("weather", [{}])[0].get("description", "") wind = item.get("wind", {}).get("speed", 0) if dkey not in daily: daily[dkey] = {"temps": [], "desc": {}, "wind": []} daily[dkey]["temps"].append(temp) daily[dkey]["wind"].append(wind) daily[dkey]["desc"][weather] = daily[dkey]["desc"].get(weather, 0) + 1 dates = [d.isoformat() for d in day_list(start_date, days)] rows = [] for dkey in dates: if dkey in daily: temps = daily[dkey]["temps"] avg = sum(temps) / len(temps) if temps else None wavg = ( sum(daily[dkey]["wind"]) / len(daily[dkey]["wind"]) if daily[dkey]["wind"] else 0 ) desc = max(daily[dkey]["desc"], key=daily[dkey]["desc"].get) rows.append( { "date": dkey, "temp_avg": round(avg, 1) if avg is not None else None, "wind": round(wavg, 1), "desc": desc, } ) else: rows.append( { "date": dkey, "temp_avg": None, "wind": None, "desc": "No forecast (beyond 5 days)", } ) summary_text = "\n".join( [ f"{r['date']}: {r['desc']}, avg {r['temp_avg']}°C, wind {r['wind']} m/s" for r in rows ] ) return {"location": loc, "daily": rows, "summary_text": summary_text}