Spaces:
Sleeping
Sleeping
Update cwa_service.py
Browse files- cwa_service.py +53 -12
cwa_service.py
CHANGED
|
@@ -33,21 +33,64 @@ def fetch_cwa_alarm_list(limit: int = 5) -> str:
|
|
| 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 "
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
|
|
@@ -55,8 +98,9 @@ def fetch_latest_significant_earthquake() -> dict | None:
|
|
| 55 |
"Authorization": CWA_API_KEY,
|
| 56 |
"format": "JSON",
|
| 57 |
"timeFrom": time_from,
|
| 58 |
-
"limit": 1,
|
| 59 |
-
|
|
|
|
| 60 |
}
|
| 61 |
|
| 62 |
r = requests.get(CWA_SIGNIFICANT_API, params=params, timeout=15)
|
|
@@ -69,26 +113,23 @@ def fetch_latest_significant_earthquake() -> dict | None:
|
|
| 69 |
|
| 70 |
q = quakes[0]
|
| 71 |
ei = q.get("EarthquakeInfo", {})
|
| 72 |
-
epic =
|
| 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 |
-
|
|
|
|
| 33 |
|
| 34 |
# --- 顯著有感地震 (E-A0015-001) (此區塊不變) ---
|
| 35 |
def _parse_significant_earthquakes(obj: dict) -> pd.DataFrame:
|
| 36 |
+
records = obj.get("records") or obj.get("Records") or {}
|
| 37 |
+
quakes = records.get("earthquake") or records.get("Earthquake") or []
|
| 38 |
+
rows = []
|
| 39 |
+
for q in quakes:
|
| 40 |
+
ei = q.get("EarthquakeInfo") or q.get("earthquakeInfo") or {}
|
| 41 |
+
epic = ei.get("Epicenter") or ei.get("epicenter") or {}
|
| 42 |
+
mag_info = ei.get("Magnitude") or ei.get("magnitude") or ei.get("EarthquakeMagnitude") or {}
|
| 43 |
+
depth_raw = ei.get("FocalDepth") or ei.get("depth") or ei.get("Depth")
|
| 44 |
+
mag_raw = mag_info.get("MagnitudeValue") or mag_info.get("magnitudeValue") or mag_info.get("Value") or mag_info.get("value")
|
| 45 |
+
rows.append({
|
| 46 |
+
"ID": q.get("EarthquakeNo"), "Time": ei.get("OriginTime"),
|
| 47 |
+
"Lat": _to_float(epic.get("EpicenterLatitude") or epic.get("epicenterLatitude")),
|
| 48 |
+
"Lon": _to_float(epic.get("EpicenterLongitude") or epic.get("epicenterLongitude")),
|
| 49 |
+
"Depth": _to_float(depth_raw), "Magnitude": _to_float(mag_raw),
|
| 50 |
+
"Location": epic.get("Location") or epic.get("location"),
|
| 51 |
+
"URL": q.get("Web") or q.get("ReportURL"),
|
| 52 |
+
})
|
| 53 |
+
df = pd.DataFrame(rows)
|
| 54 |
+
if not df.empty and "Time" in df.columns:
|
| 55 |
+
time_series = pd.to_datetime(df["Time"], errors="coerce")
|
| 56 |
+
if pd.api.types.is_datetime64_any_dtype(time_series):
|
| 57 |
+
df["Time"] = time_series.dt.tz_localize("UTC").dt.tz_convert(TAIPEI_TZ)
|
| 58 |
return df
|
| 59 |
|
| 60 |
def fetch_significant_earthquakes(days: int = 7, limit: int = 5) -> str:
|
| 61 |
# ... (此函式內容不變) ...
|
| 62 |
+
if not CWA_API_KEY: return "❌ 顯著地震查詢失敗:管理者尚未設定 CWA_API_KEY。"
|
| 63 |
+
now = datetime.now(timezone.utc)
|
| 64 |
+
time_from = (now - timedelta(days=days)).strftime("%Y-%m-%d")
|
| 65 |
+
params = {"Authorization": CWA_API_KEY, "format": "JSON", "timeFrom": time_from}
|
| 66 |
+
try:
|
| 67 |
+
r = requests.get(CWA_SIGNIFICANT_API, params=params, timeout=15)
|
| 68 |
+
r.raise_for_status()
|
| 69 |
+
data = r.json()
|
| 70 |
+
df = _parse_significant_earthquakes(data)
|
| 71 |
+
if df.empty: return f"✅ 過去 {days} 天內沒有顯著有感地震報告。"
|
| 72 |
+
df = df.sort_values(by="Time", ascending=False).head(limit)
|
| 73 |
+
lines = [f"🚨 CWA 最新顯著有感地震 (近{days}天內):", "-" * 20]
|
| 74 |
+
for _, row in df.iterrows():
|
| 75 |
+
mag_str = f"{row['Magnitude']:.1f}" if pd.notna(row['Magnitude']) else "—"
|
| 76 |
+
depth_str = f"{row['Depth']:.0f}" if pd.notna(row['Depth']) else "—"
|
| 77 |
+
lines.append(
|
| 78 |
+
f"時間: {row['Time'].strftime('%Y-%m-%d %H:%M') if pd.notna(row['Time']) else '—'}\n"
|
| 79 |
+
f"地點: {row['Location'] or '—'}\n"
|
| 80 |
+
f"規模: M{mag_str} | 深度: {depth_str} km\n"
|
| 81 |
+
f"報告: {row['URL'] or '無'}"
|
| 82 |
+
)
|
| 83 |
+
return "\n\n".join(lines)
|
| 84 |
+
except Exception as e:
|
| 85 |
+
return f"❌ 顯著地震查詢失敗:{e}"
|
| 86 |
|
| 87 |
+
|
| 88 |
+
# --- 最新一筆顯著地震 ---
|
| 89 |
def fetch_latest_significant_earthquake() -> dict | None:
|
| 90 |
"""從 CWA 獲取最新一筆顯著有感地震的詳細資料,包含報告圖。"""
|
| 91 |
if not CWA_API_KEY:
|
|
|
|
| 92 |
raise ValueError("錯誤:尚未設定 CWA_API_KEY Secret。")
|
| 93 |
|
|
|
|
| 94 |
now = datetime.now(timezone.utc)
|
| 95 |
time_from = (now - timedelta(days=1)).strftime("%Y-%m-%dT%H:%M:%S")
|
| 96 |
|
|
|
|
| 98 |
"Authorization": CWA_API_KEY,
|
| 99 |
"format": "JSON",
|
| 100 |
"timeFrom": time_from,
|
| 101 |
+
"limit": 1,
|
| 102 |
+
# [修正] 移除不支援的 sort 參數,API 預設即為時間倒序
|
| 103 |
+
# "sort": "OriginTime desc"
|
| 104 |
}
|
| 105 |
|
| 106 |
r = requests.get(CWA_SIGNIFICANT_API, params=params, timeout=15)
|
|
|
|
| 113 |
|
| 114 |
q = quakes[0]
|
| 115 |
ei = q.get("EarthquakeInfo", {})
|
| 116 |
+
epic = q.get("Epicenter", {})
|
| 117 |
mag_info = ei.get("Magnitude", {})
|
| 118 |
|
| 119 |
depth_raw = ei.get("FocalDepth") or ei.get("Depth")
|
| 120 |
mag_raw = mag_info.get("MagnitudeValue") or mag_info.get("value") or mag_info.get("Magnitude")
|
| 121 |
|
|
|
|
| 122 |
result = {
|
| 123 |
"Time": ei.get("OriginTime"),
|
| 124 |
"Location": epic.get("Location"),
|
| 125 |
"Magnitude": _to_float(mag_raw),
|
| 126 |
"Depth": _to_float(depth_raw),
|
| 127 |
"URL": q.get("Web"),
|
| 128 |
+
"ImageURL": q.get("ReportImageURI")
|
| 129 |
}
|
| 130 |
|
|
|
|
| 131 |
if result["Time"]:
|
| 132 |
dt = pd.to_datetime(result["Time"]).tz_localize("UTC").tz_convert(TAIPEI_TZ)
|
| 133 |
result["TimeStr"] = dt.strftime('%Y-%m-%d %H:%M')
|
| 134 |
|
| 135 |
return result
|
|
|