cwadayi commited on
Commit
d55244b
·
verified ·
1 Parent(s): 0aba971

Update cwa_service.py

Browse files
Files changed (1) hide show
  1. cwa_service.py +58 -93
cwa_service.py CHANGED
@@ -9,7 +9,7 @@ from config import CWA_API_KEY, CWA_ALARM_API, CWA_SIGNIFICANT_API
9
 
10
  TAIPEI_TZ = timezone(timedelta(hours=8))
11
 
12
- # --- Helper Functions ---
13
  def _to_float(x):
14
  if x is None: return None
15
  s = str(x).strip()
@@ -26,104 +26,69 @@ def _parse_cwa_time(s: str) -> tuple[str, str]:
26
  except Exception:
27
  return (s, "未知")
28
 
29
- # --- 地震預警 (CWA_ALARM_API) ---
30
  def fetch_cwa_alarm_list(limit: int = 5) -> str:
31
- """抓 CWA 地震預警並格式化輸出。"""
32
- try:
33
- r = requests.get(CWA_ALARM_API, timeout=10)
34
- r.raise_for_status()
35
- payload = r.json()
36
- except Exception as e:
37
- return f"❌ 地震預警查詢失敗:{e}"
38
-
39
- items = payload.get("data", [])
40
- if not items:
41
- return "✅ 目前沒有地震預警。"
42
-
43
- def _key(it):
44
- try:
45
- return datetime.fromisoformat(it.get("originTime", "").replace("Z", "+00:00"))
46
- except:
47
- return datetime.min.replace(tzinfo=timezone.utc)
48
-
49
- items = sorted(items, key=_key, reverse=True)
50
- lines = ["🚨 地震預警(最新):", "-" * 20]
51
- for it in items[:limit]:
52
- mag = _to_float(it.get("magnitudeValue"))
53
- depth = _to_float(it.get("depth"))
54
- tw_str, _ = _parse_cwa_time(it.get("originTime", ""))
55
- identifier = str(it.get('identifier', '—')).replace('{', '{{').replace('}', '}}')
56
- msg_type = str(it.get('msgType', '—')).replace('{', '{{').replace('}', '}}')
57
- msg_no = str(it.get('msgNo', '—')).replace('{', '{{').replace('}', '}}')
58
- areas = str(it.get('alertAreas') or '—').replace('{', '{{').replace('}', '}}')
59
- mag_str = f"{mag:.1f}" if mag is not None else "—"
60
- depth_str = f"{depth:.0f}" if depth is not None else "—"
61
- lines.append(
62
- f"事件: {identifier} | 類型: {msg_type}#{msg_no}\n"
63
- f"規模/深度: M{mag_str} / {depth_str} km\n"
64
- f"時間: {tw_str}(台灣)\n"
65
- f"預警地區: {areas}"
66
- )
67
  return "\n\n".join(lines).strip()
68
 
69
- # --- 顯著有感地震 (E-A0015-001) ---
70
  def _parse_significant_earthquakes(obj: dict) -> pd.DataFrame:
71
- """[修改] 採用參考程式碼中最穩健的解析邏輯,應對 CWA 不穩定的資料格式"""
72
- records = obj.get("records") or obj.get("Records") or {}
73
- quakes = records.get("earthquake") or records.get("Earthquake") or []
74
- rows = []
75
- for q in quakes:
76
- # 兼容各種大小寫和可能的父節點名稱
77
- ei = q.get("EarthquakeInfo") or q.get("earthquakeInfo") or {}
78
- epic = ei.get("Epicenter") or ei.get("epicenter") or {}
79
- mag_info = ei.get("Magnitude") or ei.get("magnitude") or ei.get("EarthquakeMagnitude") or {}
80
-
81
- # 嘗試用所有已知的欄位名稱去取得資料
82
- depth_raw = ei.get("FocalDepth") or ei.get("depth") or ei.get("Depth")
83
- mag_raw = mag_info.get("MagnitudeValue") or mag_info.get("magnitudeValue") or mag_info.get("Value") or mag_info.get("value")
84
-
85
- rows.append({
86
- "ID": q.get("EarthquakeNo"),
87
- "Time": ei.get("OriginTime"),
88
- "Lat": _to_float(epic.get("EpicenterLatitude") or epic.get("epicenterLatitude")),
89
- "Lon": _to_float(epic.get("EpicenterLongitude") or epic.get("epicenterLongitude")),
90
- "Depth": _to_float(depth_raw),
91
- "Magnitude": _to_float(mag_raw),
92
- "Location": epic.get("Location") or epic.get("location"),
93
- "URL": q.get("Web") or q.get("ReportURL"),
94
- })
95
- df = pd.DataFrame(rows)
96
- if not df.empty and "Time" in df.columns:
97
- time_series = pd.to_datetime(df["Time"], errors="coerce")
98
- if pd.api.types.is_datetime64_any_dtype(time_series):
99
- # 假設 API 回傳的時間是 UTC 標準時間
100
- df["Time"] = time_series.dt.tz_localize("UTC").dt.tz_convert(TAIPEI_TZ)
101
  return df
102
 
103
  def fetch_significant_earthquakes(days: int = 7, limit: int = 5) -> str:
104
- if not CWA_API_KEY: return "❌ 顯著地震查詢失敗:管理者尚未設定 CWA_API_KEY。"
 
 
 
 
 
 
 
 
 
 
105
  now = datetime.now(timezone.utc)
106
- time_from = (now - timedelta(days=days)).strftime("%Y-%m-%d")
107
- params = {"Authorization": CWA_API_KEY, "format": "JSON", "timeFrom": time_from}
108
- try:
109
- r = requests.get(CWA_SIGNIFICANT_API, params=params, timeout=15)
110
- r.raise_for_status()
111
- data = r.json()
112
- df = _parse_significant_earthquakes(data)
113
- if df.empty: return f" 過去 {days} 天內沒有顯著有感地震報告。"
114
- df = df.sort_values(by="Time", ascending=False).head(limit)
115
-
116
- lines = [f"🚨 CWA 最新顯著有感地震 (近{days}天內):", "-" * 20]
117
- for _, row in df.iterrows():
118
- mag_str = f"{row['Magnitude']:.1f}" if pd.notna(row['Magnitude']) else "—"
119
- depth_str = f"{row['Depth']:.0f}" if pd.notna(row['Depth']) else "—"
120
- lines.append(
121
- f"時間: {row['Time'].strftime('%Y-%m-%d %H:%M') if pd.notna(row['Time']) else '—'}\n"
122
- f"地點: {row['Location'] or '—'}\n"
123
- f"規模: M{mag_str} | 深度: {depth_str} km\n"
124
- f"報告: {row['URL'] or '無'}"
125
- )
126
- return "\n\n".join(lines)
127
- except Exception as e:
128
- return f"❌ 顯著地震查詢失敗:{e}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
129
 
 
9
 
10
  TAIPEI_TZ = timezone(timedelta(hours=8))
11
 
12
+ # --- Helper Functions (此區塊不變) ---
13
  def _to_float(x):
14
  if x is None: return None
15
  s = str(x).strip()
 
26
  except Exception:
27
  return (s, "未知")
28
 
29
+ # --- 地震預警 (CWA_ALARM_API) (此函式不變) ---
30
  def fetch_cwa_alarm_list(limit: int = 5) -> str:
31
+ # ... (此函式內容不變) ...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
  return "\n\n".join(lines).strip()
33
 
34
+ # --- 顯著有感地震 (E-A0015-001) (此區塊不變) ---
35
  def _parse_significant_earthquakes(obj: dict) -> pd.DataFrame:
36
+ # ... (此函式內容不變) ...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  return df
38
 
39
  def fetch_significant_earthquakes(days: int = 7, limit: int = 5) -> str:
40
+ # ... (此函式內容不變) ...
41
+ return "\n\n".join(lines)
42
+
43
+ # --- [新功能] 最新一筆顯著地震 ---
44
+ def fetch_latest_significant_earthquake() -> dict | None:
45
+ """從 CWA 獲取最新一筆顯著有感地震的詳細資料,包含報告圖。"""
46
+ if not CWA_API_KEY:
47
+ # 在這種情況下拋出錯誤,讓呼叫者處理
48
+ raise ValueError("錯誤:尚未設定 CWA_API_KEY Secret。")
49
+
50
+ # 查詢過去24小時內最新的一筆資料
51
  now = datetime.now(timezone.utc)
52
+ time_from = (now - timedelta(days=1)).strftime("%Y-%m-%dT%H:%M:%S")
53
+
54
+ params = {
55
+ "Authorization": CWA_API_KEY,
56
+ "format": "JSON",
57
+ "timeFrom": time_from,
58
+ "limit": 1, # 只取最新一筆
59
+ "sort": "OriginTime desc" # 依時間倒序
60
+ }
61
+
62
+ r = requests.get(CWA_SIGNIFICANT_API, params=params, timeout=15)
63
+ r.raise_for_status()
64
+ data = r.json()
65
+
66
+ quakes = data.get("records", {}).get("Earthquake", [])
67
+ if not quakes:
68
+ return None
69
+
70
+ q = quakes[0]
71
+ ei = q.get("EarthquakeInfo", {})
72
+ epic = ei.get("Epicenter", {})
73
+ mag_info = ei.get("Magnitude", {})
74
+
75
+ depth_raw = ei.get("FocalDepth") or ei.get("Depth")
76
+ mag_raw = mag_info.get("MagnitudeValue") or mag_info.get("value") or mag_info.get("Magnitude")
77
+
78
+ # 將解析出的資料存入字典
79
+ result = {
80
+ "Time": ei.get("OriginTime"),
81
+ "Location": epic.get("Location"),
82
+ "Magnitude": _to_float(mag_raw),
83
+ "Depth": _to_float(depth_raw),
84
+ "URL": q.get("Web"),
85
+ "ImageURL": q.get("ReportImageURI") # 獲取報告圖網址
86
+ }
87
+
88
+ # 將時間轉換為台北時區
89
+ if result["Time"]:
90
+ dt = pd.to_datetime(result["Time"]).tz_localize("UTC").tz_convert(TAIPEI_TZ)
91
+ result["TimeStr"] = dt.strftime('%Y-%m-%d %H:%M')
92
+
93
+ return result
94