补充 CWA 官方日内观测兜底

This commit is contained in:
2569718930@qq.com
2026-06-16 17:08:31 +08:00
parent 20d64a880a
commit 482dc3c9e6
2 changed files with 100 additions and 1 deletions
+55
View File
@@ -553,3 +553,58 @@ def test_overlay_latest_cwa_resets_stale_taipei_detail_to_latest_local_day():
}
]
assert result["timeseries"]["metar_today_obs"] == result["metar_today_obs"]
def test_overlay_latest_cwa_uses_official_intraday_history_when_fetcher_is_empty(monkeypatch):
from datetime import datetime, timedelta, timezone
expected_date = (datetime.now(timezone.utc) + timedelta(hours=8)).strftime("%Y-%m-%d")
class FakeRepo:
def load_points(self, *, source_code, station_code, target_date):
assert source_code == "cwa"
assert station_code == "466920"
return [
{"time": "14:30", "temp": 28.8},
{"time": "15:30", "temp": 29.4},
]
class FakeWeather:
def fetch_cwa_taipei_settlement_current(self):
return None
monkeypatch.setattr(
observation_overlay,
"OfficialIntradayObservationRepository",
lambda: FakeRepo(),
)
result = observation_overlay.overlay_latest_cwa_observation(
FakeWeather(),
"taipei",
{
"name": "taipei",
"display_name": "Taipei",
"temp_symbol": "°C",
"local_date": "2026-06-14",
"local_time": "18:00",
"current": {
"temp": 26.0,
"source_code": "cwa",
"obs_time": "2026-06-14T10:00:00+00:00",
},
"airport_current": {
"temp": 26.0,
"source_code": "cwa",
"obs_time": "2026-06-14T10:00:00+00:00",
},
"metar_today_obs": [{"time": "18:00", "temp": 26.0}],
"timeseries": {"metar_today_obs": [{"time": "18:00", "temp": 26.0}]},
},
)
assert result["local_date"] == expected_date
assert result["local_time"] == "15:30"
assert result["airport_current"]["temp"] == 29.4
assert result["airport_current"]["source_code"] == "cwa"
assert result["airport_current"]["obs_time"] == f"{expected_date}T15:30:00+08:00"
+45 -1
View File
@@ -1,12 +1,13 @@
from __future__ import annotations
from copy import deepcopy
from datetime import datetime, timezone
from datetime import datetime, timedelta, timezone
import time
from typing import Any, Optional
from loguru import logger
from src.database.runtime_state import OfficialIntradayObservationRepository
from web.services.canonical_temperature import build_canonical_temperature
_RAW_AMSC_RUNWAY_HISTORY_CACHE: dict[tuple[str, bool], tuple[float, dict[str, list[dict[str, Any]]]]] = {}
@@ -905,6 +906,48 @@ def _latest_cwa_data_from_airport_obs_log(db: Any, city: str, use_fahrenheit: bo
}
def _latest_cwa_data_from_official_intraday_history(use_fahrenheit: bool) -> Optional[dict[str, Any]]:
try:
local_now = datetime.now(timezone.utc).astimezone(timezone(timedelta(hours=8)))
target_date = local_now.strftime("%Y-%m-%d")
points = OfficialIntradayObservationRepository().load_points(
source_code="cwa",
station_code="466920",
target_date=target_date,
)
except Exception as exc:
logger.debug("latest CWA official intraday history read failed: {}", exc)
return None
latest: Optional[dict[str, Any]] = None
latest_epoch: Optional[int] = None
for point in points if isinstance(points, list) else []:
if not isinstance(point, dict):
continue
time_text = str(point.get("time") or "").strip()
temp = _to_float(point.get("temp"))
if len(time_text) != 5 or ":" not in time_text or temp is None:
continue
obs_time = f"{target_date}T{time_text}:00+08:00"
epoch = parse_observation_epoch(obs_time)
if epoch is None:
continue
if latest_epoch is None or epoch > latest_epoch:
latest_epoch = epoch
latest = {
"source": "cwa",
"source_label": "CWA",
"station_code": "466920",
"station_name": "\u81fa\u5317",
"observation_time": obs_time,
"current": {
"temp": round(float(temp) * 9 / 5 + 32, 1) if use_fahrenheit else round(float(temp), 1),
},
"unit": "fahrenheit" if use_fahrenheit else "celsius",
}
return latest
def _newer_cwa_payload(left: Optional[dict[str, Any]], right: Optional[dict[str, Any]]) -> Optional[dict[str, Any]]:
if not isinstance(left, dict):
return right if isinstance(right, dict) else None
@@ -924,6 +967,7 @@ def overlay_latest_cwa_observation(weather, city, payload, db=None):
use_fahrenheit = "F" in str(payload.get("temp_symbol") or "").upper()
cwa_data = _latest_cwa_data_from_airport_obs_log(db, normalized_city, use_fahrenheit)
cwa_data = _newer_cwa_payload(cwa_data, _latest_cwa_data_from_official_intraday_history(use_fahrenheit))
fetcher = getattr(weather, "fetch_cwa_taipei_settlement_current", None)
if not callable(fetcher) and not cwa_data: