cwadayi commited on
Commit
c0f48f1
·
verified ·
1 Parent(s): 61b7ef0

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +115 -81
app.py CHANGED
@@ -1,96 +1,130 @@
1
- from fastapi import FastAPI, HTTPException, Query
2
- from starlette.responses import JSONResponse
3
- from obspy.clients.fdsn import Client
4
- from obspy.core.utcdatetime import UTCDateTime
5
- import logging
6
- from datetime import date
7
 
8
- # Configure logging
9
- logging.basicConfig(level=logging.INFO)
 
 
 
 
10
 
11
- # Initialize the FastAPI app
12
- app = FastAPI(
13
- title="Dynamic Earthquake Catalog API",
14
- description="A robust API to fetch earthquake data from the IRIS FDSN web service using URL parameters.",
15
- version="2.1.0",
16
- )
 
 
 
 
 
 
 
17
 
18
- # Initialize the IRIS FDSN client
19
- try:
20
- client = Client("IRIS")
21
- logging.info("Successfully initialized IRIS FDSN client.")
22
- except Exception as e:
23
- client = None
24
- logging.error(f"Could not initialize IRIS FDSN client on startup: {e}")
25
-
26
- @app.get("/")
27
- def greet_json():
28
- """
29
- A simple 'Hello World' endpoint.
30
  """
31
- return {"Hello": "World!", "message": "Go to /docs to try the dynamic earthquake endpoint. cwadayi \n https://cwadayi-python-app.hf.space/earthquakes?start_date=2024-07-01&end_date=2024-07-07&min_magnitude=5.0"}
32
 
33
- @app.get("/earthquakes")
34
- async def get_earthquake_catalog(
35
- start_date: date = Query(default="2024-01-01", description="Start date in YYYY-MM-DD format"),
36
- end_date: date = Query(default="2024-01-07", description="End date in YYYY-MM-DD format"),
37
- min_magnitude: float = Query(default=4.5, description="Minimum earthquake magnitude (e.g., 5.5)", gt=0, le=10)
38
- ):
39
- """
40
- Fetches earthquake data based on date and magnitude parameters provided in the URL.
41
  """
42
- if not client:
43
- raise HTTPException(status_code=503, detail="The IRIS FDSN client is not available.")
 
 
 
 
 
 
 
 
44
 
45
- if start_date > end_date:
46
- raise HTTPException(status_code=400, detail="The start_date cannot be after the end_date.")
47
-
48
  try:
49
- starttime = UTCDateTime(start_date)
50
- endtime = UTCDateTime(end_date)
 
 
 
 
51
 
52
- logging.info(f"Fetching events from {starttime} to {endtime} with min magnitude {min_magnitude}.")
53
- catalog = client.get_events(
54
- starttime=starttime,
55
- endtime=endtime,
56
- minmagnitude=min_magnitude,
57
- )
58
- logging.info(f"Found {len(catalog)} events. Now processing.")
59
 
60
- earthquake_list = []
61
- for event in catalog:
62
- try:
63
- origin = event.preferred_origin()
64
- magnitude = event.preferred_magnitude()
 
 
 
 
 
 
 
 
65
 
66
- if not origin or not magnitude:
67
- continue
 
 
 
68
 
69
- # --- Key Change Start ---
70
- # Safely get the description using getattr. This avoids errors if the 'descriptions'
71
- # attribute doesn't exist, by providing a default empty list [].
72
- place = "N/A"
73
- descriptions = getattr(event, 'descriptions', [])
74
- if descriptions:
75
- place = descriptions[0].text
76
- # --- Key Change End ---
77
 
78
- earthquake_list.append({
79
- "time": origin.time.isoformat(),
80
- "latitude": origin.latitude,
81
- "longitude": origin.longitude,
82
- "depth_km": origin.depth / 1000.0 if origin.depth is not None else None,
83
- "magnitude": magnitude.mag,
84
- "magnitude_type": magnitude.magnitude_type,
85
- "place": place,
86
- })
87
- except Exception as e:
88
- logging.warning(f"Skipping event due to unexpected processing error: {e}")
89
- continue
90
-
91
- logging.info(f"Successfully processed {len(earthquake_list)} events.")
92
- return JSONResponse(content={"earthquakes": earthquake_list})
 
 
 
 
 
 
 
93
 
 
 
 
 
 
 
 
 
94
  except Exception as e:
95
- logging.error(f"A major error occurred during the request: {e}")
96
- raise HTTPException(status_code=500, detail=f"An internal error occurred: {e}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # app.py
 
 
 
 
 
2
 
3
+ import gradio as gr
4
+ import html
5
+ import requests
6
+ import re
7
+ import xml.etree.ElementTree as ET
8
+ from datetime import datetime, timedelta, timezone
9
 
10
+ # ===============================================================
11
+ # Geocode 與縣市名稱的對照字典
12
+ # ===============================================================
13
+ GEOCODE_MAP = {
14
+ "10002": "宜蘭縣", "10004": "新竹縣", "10005": "苗栗縣",
15
+ "10007": "彰化縣", "10008": "南投縣", "10009": "雲林縣",
16
+ "10010": "嘉義縣", "10013": "屏東縣", "10014": "臺東縣",
17
+ "10015": "花蓮縣", "10016": "澎湖縣", "10017": "基隆市",
18
+ "10018": "新竹市", "10020": "嘉義市", "63": "臺北市",
19
+ "64": "高雄市", "65": "新北市", "66": "臺中市",
20
+ "67": "臺南市", "10003": "桃園市", "09007": "連江縣",
21
+ "09020": "金門縣"
22
+ }
23
 
24
+ # ===============================================================
25
+ # 主要功能:讀取並過濾地震速報
26
+ # ===============================================================
27
+ def get_disaster_warnings() -> str:
 
 
 
 
 
 
 
 
28
  """
29
+ Fetches and formats Earthquake Early Warnings from the CWA PWS feed.
30
 
31
+ This function retrieves the latest disaster warnings, filters them to show
32
+ only 'Earthquake Early Warning' (地震速報) alerts issued by the CWA, and
33
+ formats the output in Markdown, including affected counties.
 
 
 
 
 
34
  """
35
+ # 使用時間戳避免快取
36
+ timestamp = int(datetime.now().timestamp())
37
+ feed_url = f"https://cbs.tw/files/rssatomfeed.xml?_={timestamp}"
38
+ keywords = ["中央氣象署", "氣象署"]
39
+
40
+ headers = {
41
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
42
+ 'Cache-Control': 'no-cache',
43
+ 'Pragma': 'no-cache'
44
+ }
45
 
 
 
 
46
  try:
47
+ response = requests.get(feed_url, headers=headers, timeout=15)
48
+ response.raise_for_status()
49
+
50
+ xml_content = response.content.decode('utf-8')
51
+ xml_content = re.sub(' xmlns="[^"]+"', '', xml_content, count=1)
52
+ root = ET.fromstring(xml_content)
53
 
54
+ # 取得台灣時區
55
+ tw_tz = timezone(timedelta(hours=8))
56
+ current_time_str = datetime.now(tw_tz).strftime('%Y-%m-%d %H:%M:%S')
57
+
58
+ markdown_output = f"## 最新地震速報 (僅顯示來自:中央氣象署)\n---\n*最後更新時間:{current_time_str} (GMT+8)*\n\n"
59
+ messages_found = 0
 
60
 
61
+ for entry in root.findall('entry'):
62
+ summary_text = ''
63
+ for tag_name in ['summary', 'description']:
64
+ content_tag = entry.find(tag_name)
65
+ if content_tag is not None and content_tag.text:
66
+ summary_text = content_tag.text
67
+ break
68
+
69
+ title_tag = entry.find('title')
70
+ title = title_tag.text if title_tag is not None else ""
71
+
72
+ is_earthquake_alert = "地震速報" in title
73
+ is_from_cwa = any(keyword in summary_text for keyword in keywords)
74
 
75
+ if is_earthquake_alert and is_from_cwa:
76
+ messages_found += 1
77
+
78
+ sent_tag = entry.find('sent')
79
+ published_time = sent_tag.text if sent_tag is not None else "無發布時間"
80
 
81
+ markdown_output += f"### {html.escape(title)}\n"
82
+ markdown_output += f"**發布時間:** {html.escape(published_time)}\n\n"
 
 
 
 
 
 
83
 
84
+ area_tag = entry.find('area')
85
+ if area_tag is not None:
86
+ area_desc_tag = area_tag.find('areadesc')
87
+ area_desc = area_desc_tag.text if area_desc_tag is not None else ''
88
+
89
+ affected_locations = []
90
+ for geocode_tag in area_tag.findall('geocode'):
91
+ value_tag = geocode_tag.find('value')
92
+ if value_tag is not None:
93
+ code = value_tag.text
94
+ location_name = GEOCODE_MAP.get(code, f"未知代碼({code})")
95
+ affected_locations.append(location_name)
96
+
97
+ area_details = ""
98
+ if affected_locations:
99
+ area_details = f" ({'、'.join(affected_locations)})"
100
+
101
+ full_area_info = f"{html.escape(area_desc)}{area_details}"
102
+ markdown_output += f"**影響範圍:** {full_area_info}\n\n"
103
+
104
+ markdown_output += f"**內容:**\n{html.escape(summary_text)}\n\n"
105
+ markdown_output += "---\n"
106
 
107
+ if messages_found == 0:
108
+ return markdown_output + "目前沒有最新的地震速報。"
109
+
110
+ return markdown_output
111
+ except requests.exceptions.RequestException as e:
112
+ return f"網路請求失敗,無法取得資料: {e}"
113
+ except ET.ParseError as e:
114
+ return f"XML 資料解析失敗,請稍後再試: {e}"
115
  except Exception as e:
116
+ return f"讀取訊息時發生未預期的錯誤: {e}"
117
+
118
+ # ===============================================================
119
+ # 建立 Gradio 介面
120
+ # ===============================================================
121
+ with gr.Blocks(title="台灣地震速報") as app:
122
+ gr.Markdown("## 台灣地震速報")
123
+ gr.Markdown("自動讀取最新的地震速報,並過濾只顯示由「中央氣象署」發布的內容。")
124
+ output_markdown = gr.Markdown()
125
+ app.load(get_disaster_warnings, outputs=output_markdown, every=60) # 每60秒自動更新一次
126
+
127
+
128
+ # 啟動 Gradio 網頁介面
129
+ if __name__ == "__main__":
130
+ app.launch()