cwadayi commited on
Commit
ec89378
·
verified ·
1 Parent(s): 899e05f

Update cwa_service.py

Browse files
Files changed (1) hide show
  1. cwa_service.py +84 -97
cwa_service.py CHANGED
@@ -1,97 +1,84 @@
1
- # cwa_service.py
2
- # -*- coding: utf-8 -*-
3
- from __future__ import annotations
4
- import requests
5
- from datetime import datetime, timedelta, timezone
6
- from config import CWA_ALARM_API
7
-
8
- def _parse_cwa_time(s: str) -> tuple[str, str]:
9
- """回傳 (台灣時間, UTC);若字串無時區,預設視為台灣時間。"""
10
- if not s:
11
- return ("未知", "未知")
12
- try:
13
- if "T" in s or s.endswith("Z") or "+" in s:
14
- dt = datetime.fromisoformat(s.replace("Z", "+00:00"))
15
- else:
16
- dt = datetime.strptime(s, "%Y-%m-%d %H:%M:%S")
17
- dt = dt.replace(tzinfo=timezone(timedelta(hours=8)))
18
- tw = dt.astimezone(timezone(timedelta(hours=8))).strftime("%Y-%m-%d %H:%M")
19
- utc = dt.astimezone(timezone.utc).strftime("%Y-%m-%d %H:%M")
20
- return (tw, utc)
21
- except Exception:
22
- return (s, "未知")
23
-
24
- def fetch_cwa_alarm_list(limit: int = 5) -> str:
25
- """抓 CWA 地震預警並格式化輸出。"""
26
- try:
27
- r = requests.get(CWA_ALARM_API, timeout=10)
28
- r.raise_for_status()
29
- payload = r.json()
30
- except Exception as e:
31
- return f" 地震預警查詢失敗:{e}"
32
-
33
- items = None
34
- if isinstance(payload, dict):
35
- items = payload.get("data") or payload.get("records") or payload.get("list") or payload.get("items")
36
- if items is None and isinstance(payload, list):
37
- items = payload
38
- if not items:
39
- return " 目前沒有地震預警。"
40
-
41
- def _key(it):
42
- s = it.get("originTime") or ""
43
- try:
44
- if "T" in s or s.endswith("Z") or "+" in s:
45
- dt = datetime.fromisoformat(s.replace("Z", "+00:00"))
46
- else:
47
- dt = datetime.strptime(s, "%Y-%m-%d %H:%M:%S").replace(tzinfo=timezone(timedelta(hours=8)))
48
- return dt.astimezone(timezone.utc)
49
- except Exception:
50
- return datetime.min.replace(tzinfo=timezone.utc)
51
-
52
- try:
53
- items = sorted(items, key=_key, reverse=True)
54
- except Exception:
55
- pass
56
-
57
- def _num(x):
58
- xs = str(x)
59
- ok = xs.replace(".", "", 1).replace("-", "", 1).isdigit()
60
- return float(xs) if ok else None
61
-
62
- lines = ["🚨 地震預警(最新):", "-" * 20]
63
- for idx, it in enumerate(items[:limit]):
64
- identifier = it.get("identifier") or it.get("eventId") or it.get("id") or "—"
65
- status = it.get("status") or "—"
66
- msg_type = it.get("msgType") or "—"
67
- msg_no = it.get("msgNo") or it.get("msgSeq") or "—"
68
- mag = _num(it.get("magnitudeValue") or it.get("magnitude") or it.get("ml") or it.get("mw"))
69
- mag_str = f"{mag:.1f}" if mag is not None else "—"
70
- depth = _num(it.get("depth"))
71
- depth_str = f"{depth:.0f}" if depth is not None else "—"
72
- lat = _num(it.get("epicenterLat") or it.get("latitude") or it.get("lat"))
73
- lon = _num(it.get("epicenterLon") or it.get("longitude") or it.get("lon"))
74
- lat_str = f"{lat:.2f}" if lat is not None else "—"
75
- lon_str = f"{lon:.2f}" if lon is not None else "—"
76
- origin = it.get("originTime") or ""
77
- tw_str, utc_str = _parse_cwa_time(origin)
78
- areas = it.get("locationDesc") or it.get("areas") or it.get("alertAreas")
79
- if isinstance(areas, list):
80
- areas_txt = "".join(str(a) for a in areas if a)
81
- elif isinstance(areas, str):
82
- areas_txt = areas
83
- else:
84
- areas_txt = "—"
85
- lines.append(
86
- f"事件: {identifier} | 狀態: {status} | 類型: {msg_type}#{msg_no}\n"
87
- f"震級/深度: M{mag_str} / {depth_str} km\n"
88
- f"震中: lat {lat_str}, lon {lon_str}\n"
89
- f"時間: {tw_str}(台灣) / {utc_str}(UTC)\n"
90
- f"預警地區: {areas_txt}"
91
- )
92
- lines.append("")
93
-
94
- if len(items) > limit:
95
- lines.append(f"... 另有 {len(items) - limit} 筆。")
96
-
97
- return "\n".join(lines).strip()
 
1
+ # cwa_service.py
2
+ # -*- coding: utf-8 -*-
3
+ from __future__ import annotations
4
+ import requests
5
+ import re
6
+ import pandas as pd
7
+ from datetime import datetime, timedelta, timezone
8
+ from config import CWA_API_KEY, CWA_ALARM_API, CWA_SIGNIFICANT_API
9
+
10
+ TAIPEI_TZ = timezone(timedelta(hours=8))
11
+
12
+ # --- Helper Function from Gradio Script ---
13
+ def _to_float(x):
14
+ if x is None: return None
15
+ s = str(x).strip()
16
+ m = re.search(r"[-+]?\d+(?:\.\d+)?", s)
17
+ return float(m.group()) if m else None
18
+
19
+ # --- 地震預警 (舊功能) ---
20
+ def fetch_cwa_alarm_list(limit: int = 5) -> str:
21
+ # ... (此函式內容不變) ...
22
+ return "\n".join(lines).strip()
23
+
24
+ # --- [新功能] 顯著有感地震 (E-A0015-001) ---
25
+ def _parse_significant_earthquakes(obj: dict) -> pd.DataFrame:
26
+ """移植自 Gradio 程式碼,用於解析 E-A0015-001 的 JSON 資料"""
27
+ quakes = obj.get("records", {}).get("Earthquake", [])
28
+ rows = []
29
+ for q in quakes:
30
+ ei = q.get("EarthquakeInfo", {})
31
+ epic = ei.get("Epicenter", {})
32
+ mag_info = ei.get("Magnitude", {})
33
+ rows.append({
34
+ "ID": q.get("EarthquakeNo"),
35
+ "Time": ei.get("OriginTime"),
36
+ "Lat": _to_float(epic.get("EpicenterLatitude")),
37
+ "Lon": _to_float(epic.get("EpicenterLongitude")),
38
+ "Depth": _to_float(ei.get("FocalDepth")),
39
+ "Magnitude": _to_float(mag_info.get("MagnitudeValue")),
40
+ "Location": epic.get("Location"),
41
+ "URL": q.get("Web"),
42
+ })
43
+ df = pd.DataFrame(rows)
44
+ if not df.empty:
45
+ df["Time"] = pd.to_datetime(df["Time"], errors="coerce").dt.tz_convert(TAIPEI_TZ)
46
+ return df
47
+
48
+ def fetch_significant_earthquakes(days: int = 7, limit: int = 5) -> str:
49
+ """從 CWA 獲取最新的顯著有感地震"""
50
+ if not CWA_API_KEY:
51
+ return "❌ 顯著地震查詢失敗:管理者尚未設定 CWA_API_KEY。"
52
+
53
+ now = datetime.now(timezone.utc)
54
+ time_from = (now - timedelta(days=days)).strftime("%Y-%m-%d")
55
+
56
+ params = {
57
+ "Authorization": CWA_API_KEY,
58
+ "format": "JSON",
59
+ "timeFrom": time_from,
60
+ }
61
+ try:
62
+ r = requests.get(CWA_SIGNIFICANT_API, params=params, timeout=15)
63
+ r.raise_for_status()
64
+ data = r.json()
65
+
66
+ df = _parse_significant_earthquakes(data)
67
+ if df.empty:
68
+ return f" 過去 {days} 天內沒有顯著有感地震報告。"
69
+
70
+ df = df.sort_values(by="Time", ascending=False).head(limit)
71
+
72
+ lines = [f"🚨 CWA 最新顯著有感地震 (近{days}天內):", "-" * 20]
73
+ for _, row in df.iterrows():
74
+ lines.append(
75
+ f"時間: {row['Time'].strftime('%Y-%m-%d %H:%M')}\n"
76
+ f"地點: {row['Location']}\n"
77
+ f"震級: M{row['Magnitude']:.1f} | 深度: {row['Depth']:.0f} km\n"
78
+ f"報告: {row['URL'] or '無'}"
79
+ )
80
+ return "\n\n".join(lines)
81
+
82
+ except Exception as e:
83
+ return f"❌ 顯著地震查詢失敗:{e}"
84
+