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

Update cwa_service.py

Browse files
Files changed (1) hide show
  1. cwa_service.py +17 -106
cwa_service.py CHANGED
@@ -1,32 +1,7 @@
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 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
- # --- 地震預警 (CWA_ALARM_API) ---
30
  def fetch_cwa_alarm_list(limit: int = 5) -> str:
31
  """抓 CWA 地震預警並格式化輸出。"""
32
  try:
@@ -55,7 +30,21 @@ def fetch_cwa_alarm_list(limit: int = 5) -> str:
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(
@@ -66,82 +55,4 @@ def fetch_cwa_alarm_list(limit: int = 5) -> str:
66
  )
67
  return "\n\n".join(lines).strip()
68
 
69
- # --- 顯著有感地震 (E-A0015-001) ---
70
- def _parse_significant_earthquakes(obj: dict) -> pd.DataFrame:
71
- records = obj.get("records") or obj.get("Records") or {}
72
- quakes = records.get("earthquake") or records.get("Earthquake") or []
73
- rows = []
74
- for q in quakes:
75
- ei = q.get("EarthquakeInfo") or q.get("earthquakeInfo") or {}
76
- epic = ei.get("Epicenter") or ei.get("epicenter") or {}
77
- mag_info = ei.get("Magnitude") or ei.get("magnitude") or ei.get("EarthquakeMagnitude") or {}
78
- depth_raw = ei.get("FocalDepth") or ei.get("depth") or ei.get("Depth")
79
- mag_raw = mag_info.get("MagnitudeValue") or mag_info.get("magnitudeValue") or mag_info.get("Value") or mag_info.get("value")
80
- rows.append({
81
- "ID": q.get("EarthquakeNo"), "Time": ei.get("OriginTime"),
82
- "Lat": _to_float(epic.get("EpicenterLatitude") or epic.get("epicenterLatitude")),
83
- "Lon": _to_float(epic.get("EpicenterLongitude") or epic.get("epicenterLongitude")),
84
- "Depth": _to_float(depth_raw), "Magnitude": _to_float(mag_raw),
85
- "Location": epic.get("Location") or epic.get("location"),
86
- "URL": q.get("Web") or q.get("ReportURL"),
87
- })
88
- df = pd.DataFrame(rows)
89
- if not df.empty and "Time" in df.columns:
90
- time_series = pd.to_datetime(df["Time"], errors="coerce")
91
- if pd.api.types.is_datetime64_any_dtype(time_series):
92
- df["Time"] = time_series.dt.tz_localize("UTC").dt.tz_convert(TAIPEI_TZ)
93
- return df
94
-
95
- def fetch_significant_earthquakes(days: int = 7, limit: int = 5) -> str:
96
- if not CWA_API_KEY: return "❌ 顯著地震查詢失敗:管理者尚未設定 CWA_API_KEY。"
97
- now = datetime.now(timezone.utc)
98
- time_from = (now - timedelta(days=days)).strftime("%Y-%m-%d")
99
- params = {"Authorization": CWA_API_KEY, "format": "JSON", "timeFrom": time_from}
100
- try:
101
- r = requests.get(CWA_SIGNIFICANT_API, params=params, timeout=15)
102
- r.raise_for_status()
103
- data = r.json()
104
- df = _parse_significant_earthquakes(data)
105
- if df.empty: return f"✅ 過去 {days} 天內沒有顯著有感地震報告。"
106
- df = df.sort_values(by="Time", ascending=False).head(limit)
107
- lines = [f"🚨 CWA 最新顯著有感地震 (近{days}天內):", "-" * 20]
108
- for _, row in df.iterrows():
109
- mag_str = f"{row['Magnitude']:.1f}" if pd.notna(row['Magnitude']) else "—"
110
- depth_str = f"{row['Depth']:.0f}" if pd.notna(row['Depth']) else "—"
111
- lines.append(
112
- f"時間: {row['Time'].strftime('%Y-%m-%d %H:%M') if pd.notna(row['Time']) else '—'}\n"
113
- f"地點: {row['Location'] or '—'}\n"
114
- f"規模: M{mag_str} | 深度: {depth_str} km\n"
115
- f"報告: {row['URL'] or '無'}"
116
- )
117
- return "\n\n".join(lines)
118
- except Exception as e:
119
- return f"❌ 顯著地震查詢失敗:{e}"
120
-
121
- # --- 最新一筆顯著地震 ---
122
- def fetch_latest_significant_earthquake() -> dict | None:
123
- """從 CWA 獲取最新一筆顯著地震,並重用現有的解析邏輯"""
124
- if not CWA_API_KEY:
125
- raise ValueError("錯誤:尚未設定 CWA_API_KEY Secret。")
126
-
127
- now = datetime.now(timezone.utc)
128
- time_from = (now - timedelta(days=2)).strftime("%Y-%m-%dT%H:%M:%S")
129
- params = {"Authorization": CWA_API_KEY, "format": "JSON", "limit": 1}
130
-
131
- r = requests.get(CWA_SIGNIFICANT_API, params=params, timeout=15)
132
- r.raise_for_status()
133
- data = r.json()
134
- df = _parse_significant_earthquakes(data)
135
- if df.empty:
136
- return None
137
-
138
- latest_eq_data = df.sort_values(by="Time", ascending=False).iloc[0].to_dict()
139
-
140
- quakes = data.get("records", {}).get("Earthquake", [])
141
- if quakes:
142
- latest_eq_data["ImageURL"] = quakes[0].get("ReportImageURI")
143
-
144
- if pd.notna(latest_eq_data.get("Time")):
145
- latest_eq_data["TimeStr"] = latest_eq_data["Time"].strftime('%Y-%m-%d %H:%M')
146
-
147
- return latest_eq_data
 
1
  # cwa_service.py
 
 
 
 
 
 
 
2
 
3
+ # (檔案中其他的程式碼保持不變...)
4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
  def fetch_cwa_alarm_list(limit: int = 5) -> str:
6
  """抓 CWA 地震預警並格式化輸出。"""
7
  try:
 
30
  identifier = str(it.get('identifier', '—')).replace('{', '{{').replace('}', '}}')
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 "—"
50
  lines.append(
 
55
  )
56
  return "\n\n".join(lines).strip()
57
 
58
+ # (檔案中其他的程式碼保持不變...)