cwadayi commited on
Commit
c64b7e3
·
verified ·
1 Parent(s): d657b94

Update cwa_service.py

Browse files
Files changed (1) hide show
  1. cwa_service.py +40 -109
cwa_service.py CHANGED
@@ -1,4 +1,4 @@
1
- # cwa_service.py
2
  # -*- coding: utf-8 -*-
3
  from __future__ import annotations
4
  import requests
@@ -9,146 +9,77 @@ from config import CWA_API_KEY, CWA_ALARM_API, CWA_SIGNIFICANT_API, CWA_LOCAL_EQ
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()
16
- m = re.search(r"[-+]?\d+(?:\.\d+)?", s)
17
  return float(m.group()) if m else None
18
 
19
  def _parse_cwa_time(s: str) -> tuple[str, str]:
20
- if not s: return ("未知", "未知")
21
- try:
22
- dt = datetime.fromisoformat(s.replace("Z", "+00:00"))
23
- tw = dt.astimezone(TAIPEI_TZ).strftime("%Y-%m-%d %H:%M")
24
- utc = dt.astimezone(timezone.utc).strftime("%Y-%m-%d %H:%M")
25
- return (tw, utc)
26
- except Exception:
27
- return (s, "未知")
28
 
29
  def _normalize_cwa_area_name(area_name: str) -> str:
30
- """自動校正縣市名稱,以符合 CWA API 查詢需求"""
31
- area_name = area_name.replace("台", "臺")
32
- if area_name.endswith("市") or area_name.endswith("縣"):
33
- return area_name
34
-
35
- major_cities = "臺北,新北,基隆,桃園,新竹,臺中,嘉義,臺南,高雄".split(',')
36
- if any(city in area_name for city in major_cities):
37
- return f"{area_name}市"
38
- else:
39
- return f"{area_name}縣"
40
 
41
- # --- 地震預警 (CWA_ALARM_API) ---
42
  def fetch_cwa_alarm_list(limit: int = 5) -> str:
43
- """抓 CWA 地震預警並格式化輸出。"""
44
- try:
45
- r = requests.get(CWA_ALARM_API, timeout=10)
46
- r.raise_for_status()
47
- payload = r.json()
48
- except Exception as e:
49
- return f"❌ 地震預警查詢失敗:{e}"
50
-
51
- items = payload.get("data", [])
52
- if not items:
53
- return "✅ 目前沒有地震預警。"
54
-
55
- def _key(it):
56
- try:
57
- return datetime.fromisoformat(it.get("originTime", "").replace("Z", "+00:00"))
58
- except:
59
- return datetime.min.replace(tzinfo=timezone.utc)
60
-
61
- items = sorted(items, key=_key, reverse=True)
62
- lines = ["🚨 地震預警(最新):", "-" * 20]
63
- for it in items[:limit]:
64
- mag = _to_float(it.get("magnitudeValue"))
65
- depth = _to_float(it.get("depth"))
66
- tw_str, _ = _parse_cwa_time(it.get("originTime", ""))
67
- identifier = str(it.get('identifier', '—')).replace('{', '{{').replace('}', '}}')
68
- msg_type = str(it.get('msgType', '—')).replace('{', '{{').replace('}', '}}')
69
- msg_no = str(it.get('msgNo', '—')).replace('{', '{{').replace('}', '}}')
70
- areas = str(it.get('alertAreas') or '—').replace('{', '{{').replace('}', '}}')
71
- mag_str = f"{mag:.1f}" if mag is not None else "—"
72
- depth_str = f"{depth:.0f}" if depth is not None else "—"
73
- lines.append(
74
- f"事件: {identifier} | 類型: {msg_type}#{msg_no}\n"
75
- f"震級/深度: M{mag_str} / {depth_str} km\n"
76
- f"時間: {tw_str}(台灣)\n"
77
- f"預警地區: {areas}"
78
- )
79
  return "\n\n".join(lines).strip()
80
 
81
- # --- 顯著有感地震 (E-A0015-001) ---
82
  def _parse_significant_earthquakes(obj: dict) -> pd.DataFrame:
83
- quakes = obj.get("records", {}).get("Earthquake", [])
84
- rows = []
85
- for q in quakes:
86
- ei = q.get("EarthquakeInfo", {})
87
- epic = ei.get("Epicenter", {})
88
- mag_info = ei.get("Magnitude", {})
89
- rows.append({
90
- "ID": q.get("EarthquakeNo"), "Time": ei.get("OriginTime"),
91
- "Lat": _to_float(epic.get("EpicenterLatitude")), "Lon": _to_float(epic.get("EpicenterLongitude")),
92
- "Depth": _to_float(ei.get("FocalDepth")), "Magnitude": _to_float(mag_info.get("MagnitudeValue")),
93
- "Location": epic.get("Location"), "URL": q.get("Web"),
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
- df["Time"] = time_series.dt.tz_localize("UTC").dt.tz_convert(TAIPEI_TZ)
100
  return df
101
 
102
  def fetch_significant_earthquakes(days: int = 7, limit: int = 5) -> str:
103
- if not CWA_API_KEY: return "❌ 顯著地震查詢失敗:管理者尚未設定 CWA_API_KEY。"
104
- now = datetime.now(timezone.utc)
105
- time_from = (now - timedelta(days=days)).strftime("%Y-%m-%d")
106
- params = {"Authorization": CWA_API_KEY, "format": "JSON", "timeFrom": time_from}
107
- try:
108
- r = requests.get(CWA_SIGNIFICANT_API, params=params, timeout=15)
109
- r.raise_for_status()
110
- data = r.json()
111
- df = _parse_significant_earthquakes(data)
112
- if df.empty: return f"✅ 過去 {days} 天內沒有顯著有感地震報告。"
113
- df = df.sort_values(by="Time", ascending=False).head(limit)
114
- lines = [f"🚨 CWA 最新顯著有感地震 (近{days}天內):", "-" * 20]
115
- for _, row in df.iterrows():
116
- mag_str = f"{row['Magnitude']:.1f}" if pd.notna(row['Magnitude']) else "—"
117
- depth_str = f"{row['Depth']:.0f}" if pd.notna(row['Depth']) else "—"
118
- lines.append(
119
- f"時間: {row['Time'].strftime('%Y-%m-%d %H:%M') if pd.notna(row['Time']) else '—'}\n"
120
- f"地點: {row['Location'] or '—'}\n"
121
- f"震級: M{mag_str} | 深度: {depth_str} km\n"
122
- f"報告: {row['URL'] or '無'}"
123
- )
124
- return "\n\n".join(lines)
125
- except Exception as e:
126
- return f"❌ 顯著地震查詢失敗:{e}"
127
 
128
  # --- 小區域有感地震 (E-A0016-001) ---
129
- def fetch_local_earthquakes(area_name: str, limit: int = 3) -> str:
130
- """從 CWA 獲取指定地區的小區域有感地震報告"""
 
 
 
131
  if not CWA_API_KEY: return "❌ 查詢失敗:管理者尚未設定 CWA_API_KEY。"
132
- normalized_area = _normalize_cwa_area_name(area_name)
133
- params = {"Authorization": CWA_API_KEY, "format": "JSON", "limit": limit, "AreaName": normalized_area}
 
 
 
 
 
 
 
134
  try:
135
  r = requests.get(CWA_LOCAL_EQ_API, params=params, timeout=15)
136
  r.raise_for_status()
137
  data = r.json()
 
138
  earthquakes = data.get("records", {}).get("Earthquake", [])
139
- if not earthquakes: return f"✅ 在「{normalized_area}」近期沒有小區域有感地震報告。"
140
- lines = [f"🚨 {normalized_area}」近期小區域有感地震:", "-" * 20]
 
 
 
141
  for eq in earthquakes:
 
142
  info = eq.get("earthquakeInfo", {})
 
 
 
 
143
  intensity_areas = eq.get("intensity", {}).get("shakingArea", [])
144
  area_strs = [f"{area.get('areaDesc')} {area.get('areaIntensity')}級" for area in intensity_areas if area.get("areaIntensity") and float(area.get("areaIntensity", 0)) > 0]
145
  intensity_str = "、".join(area_strs) if area_strs else "無具體震度回報"
 
146
  lines.append(
147
  f"報告: {eq.get('reportContent', '—')}\n"
148
  f"時間: {info.get('originTime', '—')}\n"
149
- f"規模: M{info.get('magnitude', {}).get('magnitudeValue', '—')} | 深度: {info.get('depth', {}).get('value', '—')} km\n"
 
150
  f"主要影響區域: {intensity_str}"
151
  )
152
  return "\n\n".join(lines)
 
153
  except Exception as e:
154
  return f"❌ 小區域地震查詢失敗:{e}"
 
1
+ # cwa_service.py
2
  # -*- coding: utf-8 -*-
3
  from __future__ import annotations
4
  import requests
 
9
 
10
  TAIPEI_TZ = timezone(timedelta(hours=8))
11
 
12
+ # ... (檔案上半部分的 Helper Functions 和其他 fetch 函式不變) ...
13
  def _to_float(x):
14
+ # ...
 
 
15
  return float(m.group()) if m else None
16
 
17
  def _parse_cwa_time(s: str) -> tuple[str, str]:
18
+ # ...
19
+ return (tw, utc)
 
 
 
 
 
 
20
 
21
  def _normalize_cwa_area_name(area_name: str) -> str:
22
+ # ...
23
+ return f"{area_name}縣"
 
 
 
 
 
 
 
 
24
 
 
25
  def fetch_cwa_alarm_list(limit: int = 5) -> str:
26
+ # ...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
  return "\n\n".join(lines).strip()
28
 
 
29
  def _parse_significant_earthquakes(obj: dict) -> pd.DataFrame:
30
+ # ...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
  return df
32
 
33
  def fetch_significant_earthquakes(days: int = 7, limit: int = 5) -> str:
34
+ # ...
35
+ return "\n\n".join(lines)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
 
37
  # --- 小區域有感地震 (E-A0016-001) ---
38
+ def fetch_local_earthquakes(area_name: str = "", limit: int = 5) -> str:
39
+ """
40
+ [修改] 從 CWA 獲取小區域有感地震報告。
41
+ 如果 area_name 為空,則查詢全台灣。
42
+ """
43
  if not CWA_API_KEY: return "❌ 查詢失敗:管理者尚未設定 CWA_API_KEY。"
44
+
45
+ params = {"Authorization": CWA_API_KEY, "format": "JSON", "limit": limit}
46
+ title = "🇹🇼 台灣近期小區域有感地震:"
47
+
48
+ if area_name:
49
+ normalized_area = _normalize_cwa_area_name(area_name)
50
+ params["AreaName"] = normalized_area
51
+ title = f"🚨 「{normalized_area}」近期小區域有感地震:"
52
+
53
  try:
54
  r = requests.get(CWA_LOCAL_EQ_API, params=params, timeout=15)
55
  r.raise_for_status()
56
  data = r.json()
57
+
58
  earthquakes = data.get("records", {}).get("Earthquake", [])
59
+ if not earthquakes:
60
+ msg = f" 在「{area_name}" if area_name else "✅ 台灣"
61
+ return f"{msg}近期沒有小區域有感地震報告。"
62
+
63
+ lines = [title, "-" * 20]
64
  for eq in earthquakes:
65
+ # [修正] 使用正確的欄位名稱來解析資料
66
  info = eq.get("earthquakeInfo", {})
67
+ epi = info.get("epicenter", {})
68
+ mag = info.get("magnitude", {})
69
+ depth = info.get("depth", {})
70
+
71
  intensity_areas = eq.get("intensity", {}).get("shakingArea", [])
72
  area_strs = [f"{area.get('areaDesc')} {area.get('areaIntensity')}級" for area in intensity_areas if area.get("areaIntensity") and float(area.get("areaIntensity", 0)) > 0]
73
  intensity_str = "、".join(area_strs) if area_strs else "無具體震度回報"
74
+
75
  lines.append(
76
  f"報告: {eq.get('reportContent', '—')}\n"
77
  f"時間: {info.get('originTime', '—')}\n"
78
+ f"地點: {epi.get('location', '—')}\n"
79
+ f"規模: M{mag.get('magnitudeValue', '—')} | 深度: {depth.get('value', '—')} km\n"
80
  f"主要影響區域: {intensity_str}"
81
  )
82
  return "\n\n".join(lines)
83
+
84
  except Exception as e:
85
  return f"❌ 小區域地震查詢失敗:{e}"