import requests from datetime import date, datetime, timedelta from modules.weather import Weather def fetch_json(url: str, timeout: int = 10) -> dict: resp = requests.get(url, timeout=timeout) resp.raise_for_status() data = resp.json() if data.get("error"): raise RuntimeError(f"Open-Meteo API error: {data.get('reason', data)}") if "daily" not in data: raise RuntimeError(f"Unexpected Open-Meteo response: {data}") return data def did_or_will_rain( target_date, latitude, longitude, forecast_threshold=50, ): """ Returns True if: - it rained on a past date - rain is forecast on a future date above the threshold Parameters ---------- target_date : str | date | datetime Date to check ("YYYY-MM-DD" or date object) latitude : float longitude : float forecast_threshold : int Minimum rain probability (%) for future dates Returns ------- bool """ # Normalize date if isinstance(target_date, str): target_date = datetime.strptime(target_date, "%Y-%m-%d").date() elif isinstance(target_date, datetime): target_date = target_date.date() today = date.today() # Historical date if target_date < today: url = ( "https://archive-api.open-meteo.com/v1/archive" f"?latitude={latitude}" f"&longitude={longitude}" f"&start_date={target_date}" f"&end_date={target_date}" "&daily=precipitation_sum" "&timezone=auto" ) data = fetch_json(url) rain_mm = data["daily"]["precipitation_sum"][0] return rain_mm > 0 # Today / future date else: url = ( "https://api.open-meteo.com/v1/forecast" f"?latitude={latitude}" f"&longitude={longitude}" "&daily=precipitation_probability_max,precipitation_sum" "&forecast_days=16" "&timezone=auto" ) data = fetch_json(url) dates = data["daily"]["time"] target_date_str = target_date.isoformat() if target_date_str not in dates: raise ValueError("Date outside forecast range") idx = dates.index(target_date_str) probability = data["daily"]["precipitation_probability_max"][idx] precipitation = data["daily"]["precipitation_sum"][idx] return ( probability >= forecast_threshold or precipitation > 0 ) def weather_comment(code: int) -> str: WMO_CODES = { 0: "☀️ Sunny", 1: "🌤️ Mainly clear", 2: "⛅ Partly cloudy", 3: "☁️ Cloudy", 45: "🌫️ Foggy", 48: "🌫️ Icy fog", 51: "🌦️ Light drizzle", 53: "🌦️ Moderate drizzle", 55: "🌧️ Dense drizzle", 56: "🌨️ Light freezing drizzle", 57: "🌨️ Heavy freezing drizzle", 61: "🌧️ Slight rain", 63: "🌧️ Moderate rain", 65: "🌧️ Heavy rain", 66: "🌨️ Light freezing rain", 67: "🌨️ Heavy freezing rain", 71: "❄️ Slight snowfall", 73: "❄️ Moderate snowfall", 75: "❄️ Heavy snowfall", 77: "🌨️ Snow grains", 80: "🌦️ Slight rain showers", 81: "🌧️ Moderate rain showers", 82: "⛈️ Violent rain showers", 85: "🌨️ Slight snow showers", 86: "🌨️ Heavy snow showers", 95: "⛈️ Thunderstorm", 96: "⛈️ Thunderstorm with slight hail", 99: "⛈️ Thunderstorm with heavy hail", } return WMO_CODES.get(code, "Unknown weather") def weather_values(date, latitude, longitude): # return temperature, wind, precipitation, and a comment (cloudy/sunny/rainy/windy) url = ( "https://api.open-meteo.com/v1/forecast" f"?latitude={latitude}" f"&longitude={longitude}" "&daily=temperature_2m_max,temperature_2m_min,precipitation_sum,precipitation_probability_max,windspeed_10m_max,weathercode" "&forecast_days=16" "&timezone=auto" ) data = fetch_json(url) dates = data["daily"]["time"] # date_str = date.isoformat() if isinstance(date, date) else date date_str = date.isoformat() if date_str not in dates: raise ValueError("Date outside forecast range") idx = dates.index(date_str) temp_max = data["daily"]["temperature_2m_max"][idx] temp_min = data["daily"]["temperature_2m_min"][idx] precipitation = data["daily"]["precipitation_sum"][idx] precipitation_probability = data["daily"]["precipitation_probability_max"][idx] windspeed = data["daily"]["windspeed_10m_max"][idx] weathercode = data["daily"]["weathercode"][idx] comment = weather_comment(weathercode) return Weather( temp_max=temp_max, temp_min=temp_min, precipitation=precipitation, precipitation_probability=precipitation_probability, wind_speed=windspeed, comment=comment, ) def last_rained_date(latitude, longitude, days_back=15): url = ( "https://archive-api.open-meteo.com/v1/archive" f"?latitude={latitude}" f"&longitude={longitude}" f"&start_date={(date.today() - timedelta(days=days_back + 1 )).isoformat()}" f"&end_date={(date.today() - timedelta(days=1)).isoformat()}" # YESTERDAY "&daily=precipitation_sum" "&timezone=auto" ) data = fetch_json(url) dates = data["daily"]["time"] precipitation = data["daily"]["precipitation_sum"] for d, p in zip(reversed(dates), reversed(precipitation)): if p > 5: # Consider it rained if precipitation > 5mm return date.fromisoformat(d) return None # No rain in the past `days_back` days # Marseille LAT = 43.2965 LON = 5.3698 # Did it rain yesterday? print(did_or_will_rain("2026-06-03", LAT, LON)) # Will it rain tomorrow? print(did_or_will_rain("2026-06-04", LAT, LON)) # Require at least 70% confidence print( did_or_will_rain( "2026-06-05", LAT, LON, forecast_threshold=70, ))