Fix WU historical current observation merge

This commit is contained in:
2569718930@qq.com
2026-05-29 17:20:07 +08:00
parent 8f52b1d80c
commit 9e3a4e2f45
13 changed files with 859 additions and 9 deletions
+14 -6
View File
@@ -11,6 +11,14 @@ from loguru import logger
import web.routes as legacy_routes
async def _overlay_cached_wunderground(city: str, payload: Dict[str, Any]) -> Dict[str, Any]:
return await run_in_threadpool(
legacy_routes._overlay_latest_wunderground_current,
city,
payload,
)
def _build_cities_payload() -> Dict[str, Any]:
out = []
deb_recent_index = legacy_routes._build_recent_deb_performance_index()
@@ -86,7 +94,7 @@ async def get_city_detail_payload(
if cached_entry:
if not legacy_routes._city_cache_is_fresh(cached_entry, legacy_routes.CITY_FULL_CACHE_TTL_SEC):
return await run_in_threadpool(legacy_routes._refresh_city_full_cache, city, False)
return cached_entry.get("payload") or {}
return await _overlay_cached_wunderground(city, cached_entry.get("payload") or {})
return await run_in_threadpool(legacy_routes._refresh_city_full_cache, city, False)
if detail_mode == "panel":
if force_refresh:
@@ -95,7 +103,7 @@ async def get_city_detail_payload(
if cached_entry:
if not legacy_routes._city_cache_is_fresh(cached_entry, legacy_routes.CITY_PANEL_CACHE_TTL_SEC):
return await run_in_threadpool(legacy_routes._refresh_city_panel_cache, city, False)
return cached_entry.get("payload") or {}
return await _overlay_cached_wunderground(city, cached_entry.get("payload") or {})
return await run_in_threadpool(legacy_routes._refresh_city_panel_cache, city, False)
if detail_mode == "nearby":
if force_refresh:
@@ -104,7 +112,7 @@ async def get_city_detail_payload(
if cached_entry:
if not legacy_routes._city_cache_is_fresh(cached_entry, legacy_routes.CITY_NEARBY_CACHE_TTL_SEC):
return await run_in_threadpool(legacy_routes._refresh_city_nearby_cache, city, False)
return cached_entry.get("payload") or {}
return await _overlay_cached_wunderground(city, cached_entry.get("payload") or {})
return await run_in_threadpool(legacy_routes._refresh_city_nearby_cache, city, False)
if detail_mode == "market":
if force_refresh:
@@ -113,7 +121,7 @@ async def get_city_detail_payload(
if cached_entry:
if not legacy_routes._market_analysis_cache_is_fresh(cached_entry):
return await run_in_threadpool(legacy_routes._refresh_city_market_cache, city, False)
return cached_entry.get("payload") or {}
return await _overlay_cached_wunderground(city, cached_entry.get("payload") or {})
return await run_in_threadpool(legacy_routes._refresh_city_market_cache, city, False)
return await run_in_threadpool(legacy_routes._analyze, city, force_refresh, False, detail_mode)
@@ -131,7 +139,7 @@ async def get_city_summary_payload(
if cached_entry:
if not legacy_routes._city_cache_is_fresh(cached_entry, legacy_routes.CITY_SUMMARY_CACHE_TTL_SEC):
return await run_in_threadpool(legacy_routes._refresh_city_summary_cache, city, False)
return cached_entry.get("payload") or {}
return await _overlay_cached_wunderground(city, cached_entry.get("payload") or {})
return await run_in_threadpool(legacy_routes._refresh_city_summary_cache, city, False)
@@ -154,7 +162,7 @@ async def get_city_detail_aggregate_payload(
if not legacy_routes._city_cache_is_fresh(cached_entry, legacy_routes.CITY_FULL_CACHE_TTL_SEC):
data = await run_in_threadpool(legacy_routes._refresh_city_full_cache, city, False)
else:
data = cached_entry.get("payload") or {}
data = await _overlay_cached_wunderground(city, cached_entry.get("payload") or {})
else:
data = await run_in_threadpool(legacy_routes._refresh_city_full_cache, city, False)
+4
View File
@@ -28,6 +28,7 @@ def build_city_summary_payload(data: Dict[str, Any]) -> Dict[str, Any]:
),
},
"deb": dict(deb) if deb else {"prediction": None},
"wunderground_current": data.get("wunderground_current") or {},
"deviation_monitor": data.get("deviation_monitor") or {},
"risk": {
"level": data.get("risk", {}).get("level"),
@@ -212,6 +213,7 @@ def build_city_detail_payload(
),
"airport_primary": data.get("airport_primary") or {},
"airport_primary_today_obs": data.get("airport_primary_today_obs") or [],
"wunderground_current": data.get("wunderground_current") 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 {},
@@ -224,6 +226,7 @@ def build_city_detail_payload(
"metar_recent_obs": data.get("metar_recent_obs") or [],
"metar_today_obs": data.get("metar_today_obs") or [],
"settlement_today_obs": data.get("settlement_today_obs") or [],
"wunderground_today_obs": (data.get("wunderground_current") or {}).get("today_obs") or [],
"hourly": data.get("hourly") or {},
"mgm_hourly": (data.get("mgm") or {}).get("hourly", []),
"forecast_daily": (data.get("forecast") or {}).get("daily", []),
@@ -266,6 +269,7 @@ def build_city_detail_payload(
"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 {},
"wunderground_current": data.get("wunderground_current") or {},
"amos": data.get("amos") or {},
"nearby_source": data.get("nearby_source")
or (
+48
View File
@@ -2,6 +2,7 @@ from __future__ import annotations
import os
import time
from copy import deepcopy
from datetime import datetime
from typing import Optional
@@ -53,6 +54,7 @@ from web.core import (
_resolve_auth_points, # noqa: F401 - compatibility export for tests and transitional routers
_resolve_weekly_profile, # noqa: F401 - compatibility export for tests and transitional routers
_sf,
_weather,
)
router = APIRouter()
@@ -329,6 +331,52 @@ def _refresh_city_full_cache(city: str, force_refresh: bool = False) -> dict:
return payload
def _overlay_wunderground_current(payload: dict, wunderground: dict) -> dict:
if not isinstance(payload, dict) or not isinstance(wunderground, dict) or not wunderground:
return payload
next_payload = deepcopy(payload)
next_payload["wunderground_current"] = dict(wunderground)
official = next_payload.get("official")
if isinstance(official, dict):
official["wunderground_current"] = dict(wunderground)
timeseries = next_payload.get("timeseries")
if isinstance(timeseries, dict):
timeseries["wunderground_today_obs"] = list(wunderground.get("today_obs") or [])
return next_payload
def _overlay_latest_wunderground_current(city: str, payload: dict) -> dict:
if not isinstance(payload, dict):
return payload
city_key = str(city or payload.get("name") or payload.get("city") or "").strip().lower()
if city_key not in CITIES:
return deepcopy(payload)
try:
utc_offset = int(
payload.get("utc_offset_seconds")
or (payload.get("overview") or {}).get("utc_offset_seconds")
or get_city_utc_offset_seconds(city_key)
or 0
)
except Exception:
utc_offset = get_city_utc_offset_seconds(city_key)
try:
latest_wu = _weather.fetch_wunderground_historical(
city_key,
use_fahrenheit=bool(CITIES[city_key].get("f")),
utc_offset=utc_offset,
)
except Exception as exc:
logger.warning("latest WU overlay failed city={} error={}", city_key, exc)
return deepcopy(payload)
if not isinstance(latest_wu, dict) or not latest_wu:
return deepcopy(payload)
return _overlay_wunderground_current(payload, latest_wu)
def _schedule_cache_refresh(
background_tasks: BackgroundTasks,