Expose station network coverage and settlement station details

This commit is contained in:
2569718930@qq.com
2026-04-06 07:21:59 +08:00
parent e4a5c4c8d5
commit bdc300bdd2
15 changed files with 1317 additions and 33 deletions
+31
View File
@@ -22,6 +22,7 @@ from web.core import (
)
from src.analysis.deb_algorithm import calculate_dynamic_weights
from src.analysis.settlement_rounding import apply_city_settlement
from src.data_collection.country_networks import build_country_network_snapshot
from src.data_collection.city_registry import ALIASES
from src.models.lgbm_daily_high import predict_lgbm_daily_high
@@ -817,6 +818,7 @@ def _analyze(city: str, force_refresh: bool = False) -> Dict[str, Any]:
if not isinstance(mm, dict):
mm = {}
risk = CITY_RISK_PROFILES.get(city, {})
network_snapshot = build_country_network_snapshot(city, raw)
# ── 2. Current conditions (city-specific settlement source first, then METAR/MGM fallback) ──
mc = metar.get("current", {}) if metar else {}
@@ -1495,6 +1497,16 @@ def _analyze(city: str, force_refresh: bool = False) -> Dict[str, Any]:
"raw_metar": mc.get("raw_metar"),
"source_label": "METAR",
},
"settlement_station": network_snapshot.get("settlement_station") or {},
"airport_primary": network_snapshot.get("airport_primary_current") or {},
"airport_primary_today_obs": network_snapshot.get("airport_primary_today_obs") or [],
"official_nearby": network_snapshot.get("official_nearby") or [],
"official_network_source": network_snapshot.get("official_network_source"),
"official_network_status": network_snapshot.get("official_network_status") or {},
"network_lead_signal": network_snapshot.get("network_lead_signal") or {},
"network_spread_signal": network_snapshot.get("network_spread_signal") or {},
"center_station_candidate": network_snapshot.get("center_station_candidate"),
"airport_vs_network_delta": network_snapshot.get("airport_vs_network_delta"),
"mgm": mgm_data,
"mgm_nearby": raw.get("mgm_nearby", []),
"nearby_source": raw.get("nearby_source") or ("mgm" if city.lower() in TURKISH_MGM_CITIES else "metar_cluster"),
@@ -1691,6 +1703,7 @@ def _build_city_detail_payload(
"current_temp": data.get("current", {}).get("temp"),
"settlement_source": data.get("current", {}).get("settlement_source"),
"settlement_source_label": data.get("current", {}).get("settlement_source_label"),
"settlement_station": data.get("settlement_station") or {},
"deb_prediction": data.get("deb", {}).get("prediction"),
"risk_level": data.get("risk", {}).get("level"),
"risk_warning": data.get("risk", {}).get("warning"),
@@ -1711,6 +1724,15 @@ def _build_city_detail_payload(
"mgm": data.get("mgm") or {},
"mgm_nearby": data.get("mgm_nearby") or [],
"nearby_source": data.get("nearby_source") or ("mgm" if str(data.get("name") or "").lower() in TURKISH_MGM_CITIES else "metar_cluster"),
"airport_primary": data.get("airport_primary") or {},
"airport_primary_today_obs": data.get("airport_primary_today_obs") or [],
"official_nearby": data.get("official_nearby") or [],
"official_network_source": data.get("official_network_source"),
"official_network_status": data.get("official_network_status") or {},
"network_lead_signal": data.get("network_lead_signal") or {},
"network_spread_signal": data.get("network_spread_signal") or {},
"center_station_candidate": data.get("center_station_candidate"),
"airport_vs_network_delta": data.get("airport_vs_network_delta"),
},
"timeseries": {
"metar_recent_obs": data.get("metar_recent_obs") or [],
@@ -1731,6 +1753,15 @@ def _build_city_detail_payload(
"taf": data.get("taf") or {},
"market_scan": market_scan,
"risk": data.get("risk"),
"settlement_station": data.get("settlement_station") or {},
"airport_primary": data.get("airport_primary") or {},
"official_nearby": data.get("official_nearby") or [],
"official_network_source": data.get("official_network_source"),
"official_network_status": data.get("official_network_status") or {},
"network_lead_signal": data.get("network_lead_signal") or {},
"network_spread_signal": data.get("network_spread_signal") or {},
"center_station_candidate": data.get("center_station_candidate"),
"airport_vs_network_delta": data.get("airport_vs_network_delta"),
"airport_current": data.get("airport_current") or {},
"nearby_source": data.get("nearby_source") or ("mgm" if str(data.get("name") or "").lower() in TURKISH_MGM_CITIES else "metar_cluster"),
"ai_analysis": data.get("ai_analysis") or "",
+2
View File
@@ -17,6 +17,7 @@ from loguru import logger
from src.utils.config_loader import load_config
from src.utils.config_validation import validate_runtime_env
from src.data_collection.weather_sources import WeatherDataCollector
from src.data_collection.country_networks import provider_coverage_summary
from src.data_collection.city_risk_profiles import CITY_RISK_PROFILES # noqa: F401
from src.data_collection.polymarket_readonly import PolymarketReadOnlyLayer
from src.auth.supabase_entitlement import SUPABASE_ENTITLEMENT, extract_bearer_token
@@ -748,5 +749,6 @@ def build_system_status_payload() -> Dict[str, Any]:
"metrics": build_metrics_summary(),
"probability": _probability_summary(),
"training_data": _training_data_summary(),
"station_networks": provider_coverage_summary(),
"cities_count": len(CITIES),
}
+18
View File
@@ -12,6 +12,7 @@ from src.analysis.deb_algorithm import load_history
from src.analysis.probability_snapshot_archive import load_snapshot_rows_for_day
from src.database.runtime_state import TrainingFeatureRecordRepository, TruthRecordRepository
from src.analysis.settlement_rounding import apply_city_settlement
from src.data_collection.country_networks import get_country_network_provider
from src.data_collection.city_registry import ALIASES
from src.utils.metrics import export_prometheus_metrics
from web.analysis_service import (
@@ -271,6 +272,7 @@ async def list_cities(request: Request):
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"
provider = get_country_network_provider(name)
out.append(
{
"name": name,
@@ -288,6 +290,10 @@ async def list_cities(request: Request):
settlement_source,
settlement_source.upper(),
),
"settlement_station_code": city_meta.get("settlement_station_code") or city_meta.get("icao"),
"settlement_station_label": city_meta.get("settlement_station_label") or city_meta.get("airport_name"),
"network_provider": provider.provider_code,
"network_provider_label": provider.provider_label,
"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),
@@ -329,6 +335,12 @@ async def city_history(request: Request, name: str):
features = feature_rows.get(day) or {}
if truth.get("actual_high") is not None:
record["actual_high"] = truth.get("actual_high")
record["settlement_source"] = truth.get("settlement_source")
record["settlement_station_code"] = truth.get("settlement_station_code")
record["settlement_station_label"] = truth.get("settlement_station_label")
record["truth_version"] = truth.get("truth_version")
record["updated_by"] = truth.get("updated_by")
record["truth_updated_at"] = truth.get("truth_updated_at")
if isinstance(features, dict):
if features.get("deb_prediction") is not None:
record["deb_prediction"] = features.get("deb_prediction")
@@ -379,6 +391,12 @@ async def city_history(request: Request, name: str):
"mu": float(mu) if mu is not None else None,
"mgm": float(mgm) if mgm is not None else None,
"forecasts": forecasts,
"settlement_source": rec.get("settlement_source"),
"settlement_station_code": rec.get("settlement_station_code"),
"settlement_station_label": rec.get("settlement_station_label"),
"truth_version": rec.get("truth_version"),
"updated_by": rec.get("updated_by"),
"truth_updated_at": rec.get("truth_updated_at"),
"actual_peak_time": peak_ref.get("actual_peak_time"),
"deb_at_peak_minus_12h": peak_ref.get("deb_at_peak_minus_12h"),
"deb_at_peak_minus_12h_time": peak_ref.get("deb_at_peak_minus_12h_time"),