cwadayi commited on
Commit
824dced
·
verified ·
1 Parent(s): 0c95b93

Update cwa_service.py

Browse files
Files changed (1) hide show
  1. cwa_service.py +93 -20
cwa_service.py CHANGED
@@ -1,9 +1,29 @@
1
  # cwa_service.py
 
 
 
 
 
2
 
3
- # (檔案中其他的程式碼保持不變...)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
 
5
  def fetch_cwa_alarm_list(limit: int = 5) -> str:
6
- """抓 CWA 地震預警並格式化輸出。"""
7
  try:
8
  r = requests.get(CWA_ALARM_API, timeout=10)
9
  r.raise_for_status()
@@ -12,14 +32,11 @@ def fetch_cwa_alarm_list(limit: int = 5) -> str:
12
  return f"❌ 地震預警查詢失敗:{e}"
13
 
14
  items = payload.get("data", [])
15
- if not items:
16
- return "✅ 目前沒有地震預警。"
17
 
18
  def _key(it):
19
- try:
20
- return datetime.fromisoformat(it.get("originTime", "").replace("Z", "+00:00"))
21
- except:
22
- return datetime.min.replace(tzinfo=timezone.utc)
23
 
24
  items = sorted(items, key=_key, reverse=True)
25
  lines = ["🚨 地震預警(最新):", "-" * 20]
@@ -31,19 +48,9 @@ def fetch_cwa_alarm_list(limit: int = 5) -> str:
31
  msg_type = str(it.get('msgType', '—')).replace('{', '{{').replace('}', '}}')
32
  msg_no = str(it.get('msgNo', '—')).replace('{', '{{').replace('}', '}}')
33
 
34
- # --- 修改後的邏輯 ---
35
- # 專門處理 alertAreas 列表
36
  alert_areas_list = it.get('alertAreas')
37
- if isinstance(alert_areas_list, list) and alert_areas_list:
38
- # 如果 alertAreas 是一個非空列表,將地區名稱用逗號串接起來
39
- areas_str = ", ".join(str(area) for area in alert_areas_list)
40
- else:
41
- # 否則,維持顯示為 '—'
42
- areas_str = "—"
43
-
44
- # 最後再處理 f-string 可能的逸出字元
45
  areas = areas_str.replace('{', '{{').replace('}', '}}')
46
- # --- 修改結束 ---
47
 
48
  mag_str = f"{mag:.1f}" if mag is not None else "—"
49
  depth_str = f"{depth:.0f}" if depth is not None else "—"
@@ -55,4 +62,70 @@ def fetch_cwa_alarm_list(limit: int = 5) -> str:
55
  )
56
  return "\n\n".join(lines).strip()
57
 
58
- # (檔案中其他的程式碼保持不變...)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  # cwa_service.py
2
+ import requests
3
+ import re
4
+ import pandas as pd
5
+ from datetime import datetime, timedelta, timezone
6
+ from config import CWA_API_KEY, CWA_ALARM_API, CWA_SIGNIFICANT_API
7
 
8
+ TAIPEI_TZ = timezone(timedelta(hours=8))
9
+
10
+ def _to_float(x):
11
+ if x is None: return None
12
+ s = str(x).strip()
13
+ m = re.search(r"[-+]?\d+(?:\.\d+)?", s)
14
+ return float(m.group()) if m else None
15
+
16
+ def _parse_cwa_time(s: str) -> tuple[str, str]:
17
+ if not s: return ("未知", "未知")
18
+ try:
19
+ dt = datetime.fromisoformat(s.replace("Z", "+00:00"))
20
+ tw = dt.astimezone(TAIPEI_TZ).strftime("%Y-%m-%d %H:%M")
21
+ utc = dt.astimezone(timezone.utc).strftime("%Y-%m-%d %H:%M")
22
+ return (tw, utc)
23
+ except Exception:
24
+ return (s, "未知")
25
 
26
  def fetch_cwa_alarm_list(limit: int = 5) -> str:
 
27
  try:
28
  r = requests.get(CWA_ALARM_API, timeout=10)
29
  r.raise_for_status()
 
32
  return f"❌ 地震預警查詢失敗:{e}"
33
 
34
  items = payload.get("data", [])
35
+ if not items: return "✅ 目前沒有地震預警。"
 
36
 
37
  def _key(it):
38
+ try: return datetime.fromisoformat(it.get("originTime", "").replace("Z", "+00:00"))
39
+ except: return datetime.min.replace(tzinfo=timezone.utc)
 
 
40
 
41
  items = sorted(items, key=_key, reverse=True)
42
  lines = ["🚨 地震預警(最新):", "-" * 20]
 
48
  msg_type = str(it.get('msgType', '—')).replace('{', '{{').replace('}', '}}')
49
  msg_no = str(it.get('msgNo', '—')).replace('{', '{{').replace('}', '}}')
50
 
 
 
51
  alert_areas_list = it.get('alertAreas')
52
+ areas_str = ", ".join(str(area) for area in alert_areas_list) if isinstance(alert_areas_list, list) and alert_areas_list else "—"
 
 
 
 
 
 
 
53
  areas = areas_str.replace('{', '{{').replace('}', '}}')
 
54
 
55
  mag_str = f"{mag:.1f}" if mag is not None else "—"
56
  depth_str = f"{depth:.0f}" if depth is not None else "—"
 
62
  )
63
  return "\n\n".join(lines).strip()
64
 
65
+ def _parse_significant_earthquakes(obj: dict) -> pd.DataFrame:
66
+ records = obj.get("records", {})
67
+ quakes = records.get("Earthquake", [])
68
+ rows = []
69
+ for q in quakes:
70
+ ei = q.get("EarthquakeInfo", {})
71
+ epic = ei.get("Epicenter", {})
72
+ mag_info = ei.get("Magnitude", {})
73
+ depth_raw = ei.get("FocalDepth")
74
+ mag_raw = mag_info.get("MagnitudeValue")
75
+ rows.append({
76
+ "ID": q.get("EarthquakeNo"), "Time": ei.get("OriginTime"),
77
+ "Lat": _to_float(epic.get("EpicenterLatitude")),
78
+ "Lon": _to_float(epic.get("EpicenterLongitude")),
79
+ "Depth": _to_float(depth_raw), "Magnitude": _to_float(mag_raw),
80
+ "Location": epic.get("Location"), "URL": q.get("Web"),
81
+ })
82
+ df = pd.DataFrame(rows)
83
+ if not df.empty and "Time" in df.columns:
84
+ df["Time"] = pd.to_datetime(df["Time"], errors="coerce").dt.tz_convert(TAIPEI_TZ)
85
+ return df
86
+
87
+ def fetch_significant_earthquakes(days: int = 7, limit: int = 5) -> str:
88
+ if not CWA_API_KEY: return "❌ 顯著地震查詢失敗:管理者尚未設定 CWA_API_KEY。"
89
+ now = datetime.now(timezone.utc)
90
+ time_from = (now - timedelta(days=days)).strftime("%Y-%m-%d")
91
+ params = {"Authorization": CWA_API_KEY, "format": "JSON", "timeFrom": time_from}
92
+ try:
93
+ r = requests.get(CWA_SIGNIFICANT_API, params=params, timeout=15)
94
+ r.raise_for_status()
95
+ data = r.json()
96
+ df = _parse_significant_earthquakes(data)
97
+ if df.empty: return f"✅ 過去 {days} 天內沒有顯著有感地震報告。"
98
+ df = df.sort_values(by="Time", ascending=False).head(limit)
99
+ lines = [f"🚨 CWA 最新顯著有感地震 (近{days}天內):", "-" * 20]
100
+ for _, row in df.iterrows():
101
+ mag_str = f"{row['Magnitude']:.1f}" if pd.notna(row['Magnitude']) else "—"
102
+ depth_str = f"{row['Depth']:.0f}" if pd.notna(row['Depth']) else "—"
103
+ lines.append(
104
+ f"時間: {row['Time'].strftime('%Y-%m-%d %H:%M') if pd.notna(row['Time']) else '—'}\n"
105
+ f"地點: {row['Location'] or '—'}\n"
106
+ f"規模: M{mag_str} | 深度: {depth_str} km\n"
107
+ f"報告: {row['URL'] or '無'}"
108
+ )
109
+ return "\n\n".join(lines)
110
+ except Exception as e:
111
+ return f"❌ 顯著地震查詢失敗:{e}"
112
+
113
+ def fetch_latest_significant_earthquake() -> dict | None:
114
+ if not CWA_API_KEY: raise ValueError("錯誤:尚未設定 CWA_API_KEY Secret。")
115
+ params = {"Authorization": CWA_API_KEY, "format": "JSON", "limit": 1, "orderby": "OriginTime desc"}
116
+ r = requests.get(CWA_SIGNIFICANT_API, params=params, timeout=15)
117
+ r.raise_for_status()
118
+ data = r.json()
119
+ df = _parse_significant_earthquakes(data)
120
+ if df.empty: return None
121
+
122
+ latest_eq_data = df.iloc[0].to_dict()
123
+
124
+ quakes = data.get("records", {}).get("Earthquake", [])
125
+ if quakes:
126
+ latest_eq_data["ImageURL"] = quakes[0].get("ReportImageURI")
127
+
128
+ if pd.notna(latest_eq_data.get("Time")):
129
+ latest_eq_data["TimeStr"] = latest_eq_data["Time"].strftime('%Y-%m-%d %H:%M')
130
+
131
+ return latest_eq_data