feat: Extract weather trend analysis into a new shared module and add initial web application and data collection components.
This commit is contained in:
+1
-1
@@ -349,7 +349,7 @@ def start_bot():
|
|||||||
)
|
)
|
||||||
open_meteo = weather_data.get("open-meteo", {})
|
open_meteo = weather_data.get("open-meteo", {})
|
||||||
metar = weather_data.get("metar", {})
|
metar = weather_data.get("metar", {})
|
||||||
mgm = weather_data.get("mgm", {})
|
mgm = weather_data.get("mgm") or {}
|
||||||
|
|
||||||
# 数值归一化
|
# 数值归一化
|
||||||
def _sf(v):
|
def _sf(v):
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ def analyze_weather_trend(
|
|||||||
|
|
||||||
metar = weather_data.get("metar", {})
|
metar = weather_data.get("metar", {})
|
||||||
open_meteo = weather_data.get("open-meteo", {})
|
open_meteo = weather_data.get("open-meteo", {})
|
||||||
|
mgm = weather_data.get("mgm") or {}
|
||||||
mb = weather_data.get("meteoblue", {})
|
mb = weather_data.get("meteoblue", {})
|
||||||
nws = weather_data.get("nws", {})
|
nws = weather_data.get("nws", {})
|
||||||
|
|
||||||
@@ -82,6 +83,10 @@ def analyze_weather_trend(
|
|||||||
current_forecasts["Meteoblue"] = _sf(mb.get("today_high"))
|
current_forecasts["Meteoblue"] = _sf(mb.get("today_high"))
|
||||||
if nws.get("today_high") is not None:
|
if nws.get("today_high") is not None:
|
||||||
current_forecasts["NWS"] = _sf(nws.get("today_high"))
|
current_forecasts["NWS"] = _sf(nws.get("today_high"))
|
||||||
|
|
||||||
|
mgm = weather_data.get("mgm", {})
|
||||||
|
if mgm and mgm.get("today_high") is not None:
|
||||||
|
current_forecasts["MGM"] = _sf(mgm.get("today_high"))
|
||||||
|
|
||||||
mm_forecasts = weather_data.get("multi_model", {}).get("forecasts", {})
|
mm_forecasts = weather_data.get("multi_model", {}).get("forecasts", {})
|
||||||
for m_name, m_val in mm_forecasts.items():
|
for m_name, m_val in mm_forecasts.items():
|
||||||
|
|||||||
@@ -506,16 +506,24 @@ class WeatherDataCollector:
|
|||||||
if daily_resp.status_code == 200:
|
if daily_resp.status_code == 200:
|
||||||
forecasts = daily_resp.json()
|
forecasts = daily_resp.json()
|
||||||
if forecasts and isinstance(forecasts, list):
|
if forecasts and isinstance(forecasts, list):
|
||||||
|
# Store today extra clearly
|
||||||
today = forecasts[0]
|
today = forecasts[0]
|
||||||
high_val = today.get("enYuksekGun1")
|
high_val = today.get("enYuksekGun1")
|
||||||
low_val = today.get("enDusukGun1")
|
low_val = today.get("enDusukGun1")
|
||||||
if high_val is not None:
|
if high_val is not None:
|
||||||
results["today_high"] = high_val
|
results["today_high"] = high_val
|
||||||
results["today_low"] = low_val
|
results["today_low"] = low_val
|
||||||
logger.info(
|
logger.info(f"📋 MGM 每日预报: 今天的最高温 {high_val}°C")
|
||||||
f"📋 MGM 每日预报: 最高 {high_val}°C, 最低 {low_val}°C (from {forecast_url})"
|
|
||||||
)
|
# Store all 5 days for multi_model_daily
|
||||||
break
|
results["daily_forecasts"] = {}
|
||||||
|
for i, day in enumerate(forecasts[:5]):
|
||||||
|
d_high = day.get("enYuksekGun1")
|
||||||
|
if d_high is not None:
|
||||||
|
# Calculate date (today + offset)
|
||||||
|
target_date = (datetime.now() + timedelta(days=i)).strftime("%Y-%m-%d")
|
||||||
|
results["daily_forecasts"][target_date] = d_high
|
||||||
|
break
|
||||||
else:
|
else:
|
||||||
# 记录所有可用字段,方便调试
|
# 记录所有可用字段,方便调试
|
||||||
available_keys = [
|
available_keys = [
|
||||||
@@ -556,6 +564,20 @@ class WeatherDataCollector:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.debug(f"MGM hourly failed: {e}")
|
logger.debug(f"MGM hourly failed: {e}")
|
||||||
|
|
||||||
|
# 4. Fallback for today_high (if daily forecast is missing it)
|
||||||
|
if "today_high" not in results:
|
||||||
|
# Try from current max
|
||||||
|
cur_max = results.get("current", {}).get("mgm_max_temp")
|
||||||
|
if cur_max is not None:
|
||||||
|
results["today_high"] = cur_max
|
||||||
|
logger.info(f"📋 MGM 每日预报: 使用当前测站最高温作为今日预报回退: {cur_max}°C")
|
||||||
|
elif "hourly" in results and results["hourly"]:
|
||||||
|
# Try from hourly
|
||||||
|
h_max = max((h["temp"] for h in results["hourly"] if h["temp"] is not None), default=None)
|
||||||
|
if h_max is not None:
|
||||||
|
results["today_high"] = h_max
|
||||||
|
logger.info(f"📋 MGM 每日预报: 使用小时预报最高温作为今日预报回退: {h_max}°C")
|
||||||
|
|
||||||
return results if "current" in results else None
|
return results if "current" in results else None
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"MGM API 请求失败 ({istno}): {e}")
|
logger.error(f"MGM API 请求失败 ({istno}): {e}")
|
||||||
|
|||||||
+6
-1
@@ -105,7 +105,7 @@ def _analyze(city: str, force_refresh: bool = False) -> Dict[str, Any]:
|
|||||||
raw = _weather.fetch_all_sources(city, lat=lat, lon=lon)
|
raw = _weather.fetch_all_sources(city, lat=lat, lon=lon)
|
||||||
om = raw.get("open-meteo", {})
|
om = raw.get("open-meteo", {})
|
||||||
metar = raw.get("metar", {})
|
metar = raw.get("metar", {})
|
||||||
mgm = raw.get("mgm", {})
|
mgm = raw.get("mgm") or {}
|
||||||
ens_raw = raw.get("ensemble", {})
|
ens_raw = raw.get("ensemble", {})
|
||||||
mm = raw.get("multi_model", {})
|
mm = raw.get("multi_model", {})
|
||||||
risk = CITY_RISK_PROFILES.get(city, {})
|
risk = CITY_RISK_PROFILES.get(city, {})
|
||||||
@@ -412,6 +412,11 @@ def _analyze(city: str, force_refresh: bool = False) -> Dict[str, Any]:
|
|||||||
if i < len(maxtemps) and maxtemps[i] is not None:
|
if i < len(maxtemps) and maxtemps[i] is not None:
|
||||||
day_m["Open-Meteo"] = _sf(maxtemps[i])
|
day_m["Open-Meteo"] = _sf(maxtemps[i])
|
||||||
|
|
||||||
|
# Add MGM per-day forecast
|
||||||
|
mgm_daily = mgm.get("daily_forecasts", {})
|
||||||
|
if d_str in mgm_daily:
|
||||||
|
day_m["MGM"] = _sf(mgm_daily[d_str])
|
||||||
|
|
||||||
d_val, d_winfo = None, ""
|
d_val, d_winfo = None, ""
|
||||||
if day_m:
|
if day_m:
|
||||||
try:
|
try:
|
||||||
|
|||||||
Reference in New Issue
Block a user