| from __future__ import annotations |
| from typing import Optional, Dict |
| import requests |
| from datetime import datetime, timezone |
| import os |
|
|
| UA = {"User-Agent": "HF-Space-Trip-Planner/1.0 (+weather)"} |
|
|
| |
| |
| def _code_to_condition(code: int) -> str: |
| if code == 0: |
| return "sunny" |
| if 1 <= code <= 3: |
| return "cloudy" |
| if 45 <= code <= 48: |
| return "cloudy" |
| if 51 <= code <= 67: |
| return "rainy" |
| if 71 <= code <= 77: |
| return "snowy" |
| if 80 <= code <= 82: |
| return "rainy" |
| if 85 <= code <= 86: |
| return "snowy" |
| if 95 <= code <= 99: |
| return "rainy" |
| return "cloudy" |
|
|
| def _today_iso() -> str: |
| |
| return datetime.now(timezone.utc).date().isoformat() |
|
|
| def get_weather_summary( |
| lat: float, |
| lon: float, |
| date: Optional[str] = None, |
| override: Optional[str] = None, |
| ) -> Dict[str, object]: |
| """ |
| 外部API(Open-Meteo)を使って指定日の天気を簡約化して返す。 |
| 失敗時は 'cloudy' を返すフェイルセーフ。 |
| 返り値例: |
| { |
| "condition": "sunny|cloudy|rainy|snowy", |
| "temp_c": 27.3, # あれば |
| "source": "open-meteo", |
| "date": "YYYY-MM-DD" |
| } |
| """ |
| |
| if override and override != "(auto)": |
| return {"condition": override, "source": "override", "date": date or _today_iso()} |
|
|
| use_date = (date or _today_iso()) |
|
|
| try: |
| url = "https://api.open-meteo.com/v1/forecast" |
| params = { |
| "latitude": lat, |
| "longitude": lon, |
| "daily": "weathercode,temperature_2m_max,temperature_2m_min,precipitation_probability_max", |
| "timezone": "auto", |
| "start_date": use_date, |
| "end_date": use_date, |
| } |
| r = requests.get(url, params=params, headers=UA, timeout=20) |
| r.raise_for_status() |
| j = r.json() |
| days = j.get("daily", {}) |
| if not days: |
| raise RuntimeError("no daily in response") |
|
|
| codes = days.get("weathercode") or [] |
| tmax = days.get("temperature_2m_max") or [] |
| tmin = days.get("temperature_2m_min") or [] |
| ppop = days.get("precipitation_probability_max") or [] |
|
|
| code = int(codes[0]) if codes else 1 |
| condition = _code_to_condition(code) |
|
|
| |
| temp_c = None |
| if tmax and tmin: |
| temp_c = round((float(tmax[0]) + float(tmin[0])) / 2.0, 1) |
| elif tmax: |
| temp_c = round(float(tmax[0]), 1) |
|
|
| |
| if ppop: |
| try: |
| if int(ppop[0]) >= 60: |
| condition = "rainy" if condition != "snowy" else "snowy" |
| except Exception: |
| pass |
|
|
| out = {"condition": condition, "source": "open-meteo", "date": use_date} |
| if temp_c is not None: |
| out["temp_c"] = temp_c |
| return out |
|
|
| except Exception: |
| |
| return {"condition": "cloudy", "source": "fallback", "date": use_date} |
|
|