feat: Add initial web dashboard and centralize weather trend analysis logic into a shared engine.

This commit is contained in:
2569718930@qq.com
2026-03-20 18:16:21 +08:00
parent dbf10253a8
commit e4cc5b4263
5 changed files with 83 additions and 18 deletions
+9 -1
View File
@@ -7,6 +7,7 @@ from src.analysis.metar_narrator import describe_metar_report
from src.analysis.trend_engine import analyze_weather_trend
from src.data_collection.city_registry import ALIASES, CITY_REGISTRY
from src.data_collection.city_risk_profiles import get_city_risk_profile
from src.analysis.settlement_rounding import apply_city_settlement
FAHRENHEIT_CITIES = {
@@ -505,7 +506,7 @@ def build_city_query_report(
max_str = ""
if max_p is not None:
settled_val = int(max_p + 0.5)
settled_val = apply_city_settlement(city_name.lower(), max_p)
max_str = f" (最高: {max_p}{temp_symbol}"
if max_p_time:
max_str += f" @{max_p_time}"
@@ -520,12 +521,19 @@ def build_city_query_report(
)
if use_settlement_current:
# HKO/CWA detailed observations
wind = primary_current.get("wind_speed_kt")
wind_dir = primary_current.get("wind_dir")
humidity = primary_current.get("humidity")
msg_lines.append(
f" [{settlement_source_label}] 🌪 {wind or 0}kt ({wind_dir or 0}°) | 💧 湿度: {humidity or 'N/A'}%"
)
# Secondary METAR info if available for context
if metar:
m_wind = metar_current.get("wind_speed_kt")
m_dir = metar_current.get("wind_dir")
m_vis = metar_current.get("visibility_mi")
msg_lines.append(f" [METAR] 🌪 {m_wind or 0}kt ({m_dir or 0}°) | 👁️ {m_vis or 10}mi")
elif metar:
wind = metar_current.get("wind_speed_kt")
wind_dir = metar_current.get("wind_dir")
+34 -12
View File
@@ -127,6 +127,11 @@ def analyze_weather_trend(
mgm = weather_data.get("mgm", {})
if mgm and mgm.get("today_high") is not None:
current_forecasts["MGM"] = _sf(mgm.get("today_high"))
if weather_data.get("hko_forecast") is not None:
current_forecasts["HKO(港天文)"] = _sf(weather_data.get("hko_forecast"))
if weather_data.get("cwa_forecast") is not None:
current_forecasts["CWA(台气象)"] = _sf(weather_data.get("cwa_forecast"))
mm_forecasts = weather_data.get("multi_model", {}).get("forecasts", {})
for m_name, m_val in mm_forecasts.items():
@@ -515,23 +520,40 @@ def analyze_weather_trend(
# === Settlement boundary ===
if max_so_far is not None:
settled = apply_city_settlement(city_name, max_so_far)
from src.analysis.settlement_rounding import is_exact_settlement_city
is_floor = is_exact_settlement_city(str(city_name).lower())
fractional = max_so_far - int(max_so_far)
dist_to_boundary = abs(fractional - 0.5)
if dist_to_boundary <= 0.3:
if fractional < 0.5:
if is_floor:
# For flooring cities like HK, boundary is at 1.0 (approaching next integer)
dist_to_next = 1.0 - fractional
if dist_to_next <= 0.3:
msg = (
f"⚖️ <b>结算边界</b>:当前最高 {max_so_far}{temp_symbol}{settlement_source_label} 结算 "
f"<b>{settled}{temp_symbol}</b>,但只差 <b>{0.5 - fractional:.1f}°</b> "
f"<b>{settled}{temp_symbol}</b>,但只差 <b>{dist_to_next:.1f}°</b> "
f"就会进位到 {settled + 1}{temp_symbol}"
)
else:
msg = (
f"⚖️ <b>结算边界</b>:当前最高 {max_so_far}{temp_symbol}{settlement_source_label} 结算 "
f"<b>{settled}{temp_symbol}</b>,刚刚越过进位线,再降 "
f"<b>{fractional - 0.5:.1f}°</b> 就会回落到 {settled - 1}{temp_symbol}"
)
insights.append(msg)
ai_features.append(msg)
insights.append(msg)
ai_features.append(msg)
else:
# Standard rounding boundary at 0.5
dist_to_boundary = abs(fractional - 0.5)
if dist_to_boundary <= 0.3:
if fractional < 0.5:
msg = (
f"⚖️ <b>结算边界</b>:当前最高 {max_so_far}{temp_symbol}{settlement_source_label} 结算 "
f"<b>{settled}{temp_symbol}</b>,但只差 {0.5 - fractional:.1f}° "
f"就会进位到 {settled + 1}{temp_symbol}"
)
else:
msg = (
f"⚖️ <b>结算边界</b>:当前最高 {max_so_far}{temp_symbol}{settlement_source_label} 结算 "
f"<b>{settled}{temp_symbol}</b>,刚刚越过进位线,再降 "
f"<b>{fractional - 0.5:.1f}°</b> 就会回落到 {settled - 1}{temp_symbol}"
)
insights.append(msg)
ai_features.append(msg)
# === Peak window AI hints ===
if peak_hours: