Spaces:
Sleeping
Sleeping
| """Open-Meteo data fetching and local CSV/JSON cache helpers.""" | |
| from __future__ import annotations | |
| import json | |
| from datetime import date, datetime, timedelta, timezone | |
| from typing import Any | |
| import numpy as np | |
| import pandas as pd | |
| import requests | |
| from weatherpred.config import ( | |
| DAILY_COLUMNS, | |
| DATA_DIR, | |
| FORECAST_DAYS, | |
| HISTORY_CSV_PATH, | |
| HISTORY_JSON_PATH, | |
| HISTORY_YEARS, | |
| LATEST_JSON_PATH, | |
| TOKYO_LATITUDE, | |
| TOKYO_LONGITUDE, | |
| TOKYO_TIMEZONE, | |
| ) | |
| def fetch_json(url: str, params: dict[str, str | float | int]) -> dict[str, Any]: | |
| response = requests.get(url, params=params, timeout=40) | |
| response.raise_for_status() | |
| return response.json() | |
| def normalize_daily_weather(raw: dict[str, Any], observed: bool) -> pd.DataFrame: | |
| daily = raw["daily"] | |
| frame = pd.DataFrame( | |
| { | |
| "date": pd.to_datetime(daily["time"]), | |
| "high_c": daily["temperature_2m_max"], | |
| "low_c": daily["temperature_2m_min"], | |
| "rain_mm": daily["precipitation_sum"], | |
| "wind_kmh": daily["wind_speed_10m_max"], | |
| } | |
| ) | |
| frame["confidence"] = 1.0 if observed else np.nan | |
| return frame.dropna(subset=["high_c", "low_c", "rain_mm", "wind_kmh"]) | |
| def fetch_historical_weather( | |
| years: int = HISTORY_YEARS, | |
| latitude: float = TOKYO_LATITUDE, | |
| longitude: float = TOKYO_LONGITUDE, | |
| ) -> pd.DataFrame: | |
| end = date.today() - timedelta(days=1) | |
| start = end.replace(year=end.year - years) | |
| params = { | |
| "latitude": latitude, | |
| "longitude": longitude, | |
| "timezone": TOKYO_TIMEZONE, | |
| "start_date": start.isoformat(), | |
| "end_date": end.isoformat(), | |
| "daily": ",".join(DAILY_COLUMNS), | |
| } | |
| raw = fetch_json("https://archive-api.open-meteo.com/v1/archive", params) | |
| return normalize_daily_weather(raw, observed=True) | |
| def fetch_api_forecast( | |
| latitude: float = TOKYO_LATITUDE, | |
| longitude: float = TOKYO_LONGITUDE, | |
| days: int = FORECAST_DAYS, | |
| ) -> pd.DataFrame: | |
| params = { | |
| "latitude": latitude, | |
| "longitude": longitude, | |
| "timezone": TOKYO_TIMEZONE, | |
| "forecast_days": days, | |
| "current": "temperature_2m,precipitation,wind_speed_10m", | |
| "daily": ",".join(DAILY_COLUMNS), | |
| } | |
| raw = fetch_json("https://api.open-meteo.com/v1/forecast", params) | |
| latest = { | |
| "fetched_at": datetime.now(timezone.utc).isoformat(), | |
| "current": raw.get("current", {}), | |
| "daily": raw.get("daily", {}), | |
| } | |
| DATA_DIR.mkdir(parents=True, exist_ok=True) | |
| LATEST_JSON_PATH.write_text(json.dumps(latest, indent=2) + "\n", encoding="utf-8") | |
| return normalize_daily_weather(raw, observed=False) | |
| def save_history(history: pd.DataFrame) -> None: | |
| DATA_DIR.mkdir(parents=True, exist_ok=True) | |
| history.to_csv(HISTORY_CSV_PATH, index=False) | |
| HISTORY_JSON_PATH.write_text( | |
| history.assign(date=history["date"].dt.strftime("%Y-%m-%d")).to_json( | |
| orient="records", indent=2 | |
| ) | |
| + "\n", | |
| encoding="utf-8", | |
| ) | |
| def load_or_fetch_history(force_refresh: bool = False) -> pd.DataFrame: | |
| if HISTORY_CSV_PATH.exists() and not force_refresh: | |
| return pd.read_csv(HISTORY_CSV_PATH, parse_dates=["date"]) | |
| history = fetch_historical_weather() | |
| save_history(history) | |
| return history | |