Corin1998 commited on
Commit
3d3f75a
·
verified ·
1 Parent(s): 3189473

Create weather.py

Browse files
Files changed (1) hide show
  1. services/weather.py +104 -0
services/weather.py ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+ from typing import Optional, Dict
3
+ import requests
4
+ from datetime import datetime, timezone
5
+ import os
6
+
7
+ UA = {"User-Agent": "HF-Space-Trip-Planner/1.0 (+weather)"}
8
+
9
+ # WMO weather codes -> 簡易条件
10
+ # https://open-meteo.com/en/docs#api-formats
11
+ def _code_to_condition(code: int) -> str:
12
+ if code == 0:
13
+ return "sunny"
14
+ if 1 <= code <= 3:
15
+ return "cloudy"
16
+ if 45 <= code <= 48:
17
+ return "cloudy" # 霧・もや
18
+ if 51 <= code <= 67:
19
+ return "rainy" # 霧雨〜凍雨
20
+ if 71 <= code <= 77:
21
+ return "snowy" # 雪
22
+ if 80 <= code <= 82:
23
+ return "rainy" # にわか雨
24
+ if 85 <= code <= 86:
25
+ return "snowy" # にわか雪
26
+ if 95 <= code <= 99:
27
+ return "rainy" # 雷雨(豪雨側に丸め)
28
+ return "cloudy"
29
+
30
+ def _today_iso() -> str:
31
+ # Hugging Face の実行環境ではUTC基準になりがちだが、日付文字列だけ必要
32
+ return datetime.now(timezone.utc).date().isoformat()
33
+
34
+ def get_weather_summary(
35
+ lat: float,
36
+ lon: float,
37
+ date: Optional[str] = None,
38
+ override: Optional[str] = None,
39
+ ) -> Dict[str, object]:
40
+ """
41
+ 外部API(Open-Meteo)を使って指定日の天気を簡約化して返す。
42
+ 失敗時は 'cloudy' を返すフェイルセーフ。
43
+ 返り値例:
44
+ {
45
+ "condition": "sunny|cloudy|rainy|snowy",
46
+ "temp_c": 27.3, # あれば
47
+ "source": "open-meteo",
48
+ "date": "YYYY-MM-DD"
49
+ }
50
+ """
51
+ # UI のオーバーライド優先
52
+ if override and override != "(auto)":
53
+ return {"condition": override, "source": "override", "date": date or _today_iso()}
54
+
55
+ use_date = (date or _today_iso())
56
+
57
+ try:
58
+ url = "https://api.open-meteo.com/v1/forecast"
59
+ params = {
60
+ "latitude": lat,
61
+ "longitude": lon,
62
+ "daily": "weathercode,temperature_2m_max,temperature_2m_min,precipitation_probability_max",
63
+ "timezone": "auto",
64
+ "start_date": use_date,
65
+ "end_date": use_date,
66
+ }
67
+ r = requests.get(url, params=params, headers=UA, timeout=20)
68
+ r.raise_for_status()
69
+ j = r.json()
70
+ days = j.get("daily", {})
71
+ if not days:
72
+ raise RuntimeError("no daily in response")
73
+
74
+ codes = days.get("weathercode") or []
75
+ tmax = days.get("temperature_2m_max") or []
76
+ tmin = days.get("temperature_2m_min") or []
77
+ ppop = days.get("precipitation_probability_max") or []
78
+
79
+ code = int(codes[0]) if codes else 1
80
+ condition = _code_to_condition(code)
81
+
82
+ # 気温は平均っぽく
83
+ temp_c = None
84
+ if tmax and tmin:
85
+ temp_c = round((float(tmax[0]) + float(tmin[0])) / 2.0, 1)
86
+ elif tmax:
87
+ temp_c = round(float(tmax[0]), 1)
88
+
89
+ # 強い降水確率なら rainy に寄せる
90
+ if ppop:
91
+ try:
92
+ if int(ppop[0]) >= 60:
93
+ condition = "rainy" if condition != "snowy" else "snowy"
94
+ except Exception:
95
+ pass
96
+
97
+ out = {"condition": condition, "source": "open-meteo", "date": use_date}
98
+ if temp_c is not None:
99
+ out["temp_c"] = temp_c
100
+ return out
101
+
102
+ except Exception:
103
+ # API失敗時のフォールバック
104
+ return {"condition": "cloudy", "source": "fallback", "date": use_date}