Remove AI analysis and rank cities by recent DEB performance

This commit is contained in:
2569718930@qq.com
2026-04-05 07:13:00 +08:00
parent 343c5c9c2f
commit a5b5711863
17 changed files with 124 additions and 905 deletions
+1 -18
View File
@@ -22,7 +22,6 @@ from web.core import (
)
from src.analysis.deb_algorithm import calculate_dynamic_weights
from src.analysis.settlement_rounding import apply_city_settlement
from src.analysis.metar_narrator import describe_metar_report
from src.data_collection.city_registry import ALIASES
from src.models.lgbm_daily_high import predict_lgbm_daily_high
@@ -1166,7 +1165,6 @@ def _analyze(city: str, force_refresh: bool = False) -> Dict[str, Any]:
probability_raw_sigma = None
probability_calibrated_mu = None
probability_calibrated_sigma = None
ai_text = ""
dynamic_commentary = {"summary": "", "notes": []}
try:
_, _ai_context, sd = _trend_analyze(raw, sym, city)
@@ -1196,21 +1194,6 @@ def _analyze(city: str, force_refresh: bool = False) -> Dict[str, Any]:
except Exception as e:
logger.warning(f"Structured analysis skipped for {city}: {e}")
ai_text = describe_metar_report(
raw_metar=str(primary_current.get("raw_metar") or mc.get("raw_metar") or ""),
temp_symbol=sym,
fallback={
"icao": metar.get("icao"),
"station_name": metar.get("station_name"),
"temp": cur_temp,
"wind_speed_kt": _sf(primary_current.get("wind_speed_kt")),
"wind_dir": _sf(primary_current.get("wind_dir")),
"altimeter": _sf(primary_current.get("altimeter")),
"wx_desc": primary_current.get("wx_desc"),
"clouds": primary_current.get("clouds", []) or mc.get("clouds", []),
},
)
# ── 12. Hourly data (today only, for chart) ──
today_hourly: Dict[str, list] = {"times": [], "temps": [], "radiation": []}
for i, ts in enumerate(h_times):
@@ -1562,7 +1545,7 @@ def _analyze(city: str, force_refresh: bool = False) -> Dict[str, Any]:
"metar_today_obs": metar_today_obs_payload,
"metar_recent_obs": metar_recent_obs_payload,
"settlement_today_obs": settlement_today_obs,
"ai_analysis": ai_text,
"ai_analysis": "",
"updated_at": datetime.now(timezone.utc).isoformat(),
}
+74
View File
@@ -13,6 +13,7 @@ from src.analysis.deb_algorithm import (
load_history,
)
from src.analysis.probability_snapshot_archive import load_snapshot_rows_for_day
from src.analysis.settlement_rounding import apply_city_settlement
from src.data_collection.city_registry import ALIASES
from src.database.runtime_state import TruthRecordRepository
from src.utils.metrics import export_prometheus_metrics
@@ -53,6 +54,9 @@ from web.core import (
router = APIRouter()
_DEB_RECENT_LOOKBACK = 7
_DEB_RECENT_MIN_SAMPLES = 3
TRACKABLE_ANALYTICS_EVENTS = {
"signup_completed",
"dashboard_active",
@@ -174,6 +178,69 @@ def _normalize_city_or_404(name: str) -> str:
return city
def _history_file_path() -> str:
project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
return os.path.join(project_root, "data", "daily_records.json")
def _build_recent_deb_performance_index(
history_data: Optional[dict] = None,
*,
lookback: int = _DEB_RECENT_LOOKBACK,
min_samples: int = _DEB_RECENT_MIN_SAMPLES,
) -> dict[str, dict[str, object]]:
data = history_data if isinstance(history_data, dict) else load_history(_history_file_path())
index: dict[str, dict[str, object]] = {}
if not isinstance(data, dict):
return index
today = datetime.utcnow().strftime("%Y-%m-%d")
for city_name, rows in data.items():
if not isinstance(rows, dict):
continue
settled: list[tuple[str, float, float]] = []
for date_key in sorted(rows.keys(), reverse=True):
if date_key >= today:
continue
record = rows.get(date_key) or {}
if not isinstance(record, dict):
continue
actual = _sf(record.get("actual_high"))
deb_prediction = _sf(record.get("deb_prediction"))
if actual is None or deb_prediction is None:
continue
settled.append((date_key, actual, deb_prediction))
if len(settled) >= max(lookback, 1):
break
hit_count = 0
abs_errors: list[float] = []
for _, actual, deb_prediction in settled:
abs_errors.append(abs(deb_prediction - actual))
if apply_city_settlement(city_name, actual) == apply_city_settlement(city_name, deb_prediction):
hit_count += 1
sample_count = len(settled)
hit_rate = (hit_count / sample_count) if sample_count > 0 else None
if sample_count < min_samples:
tier = "other"
elif hit_rate is not None and hit_rate >= 0.67:
tier = "high"
elif hit_rate is not None and hit_rate >= 0.34:
tier = "medium"
else:
tier = "low"
index[str(city_name).strip().lower()] = {
"tier": tier,
"sample_count": sample_count,
"hit_rate": round(hit_rate, 4) if hit_rate is not None else None,
"mae": round(sum(abs_errors) / sample_count, 3) if sample_count > 0 else None,
"last_date": settled[0][0] if settled else None,
}
return index
@router.get("/healthz")
async def healthz():
payload = build_health_payload()
@@ -199,9 +266,11 @@ async def metrics():
async def list_cities(request: Request):
try:
out = []
deb_recent_index = _build_recent_deb_performance_index()
for name, info in CITIES.items():
risk = CITY_RISK_PROFILES.get(name, {})
city_meta = CITY_REGISTRY.get(name, {}) or {}
deb_recent = deb_recent_index.get(name, {})
settlement_source = str(info.get("settlement_source") or "metar").strip().lower() or "metar"
out.append(
{
@@ -220,6 +289,11 @@ async def list_cities(request: Request):
settlement_source,
settlement_source.upper(),
),
"deb_recent_tier": deb_recent.get("tier", "other"),
"deb_recent_hit_rate": deb_recent.get("hit_rate"),
"deb_recent_sample_count": deb_recent.get("sample_count", 0),
"deb_recent_mae": deb_recent.get("mae"),
"deb_recent_last_date": deb_recent.get("last_date"),
}
)
return {"cities": out}