prefer newest observation for Telegram pushes

This commit is contained in:
2569718930@qq.com
2026-06-11 15:47:58 +08:00
parent 9a7257008e
commit c5e5e73ce1
2 changed files with 79 additions and 13 deletions
+44 -13
View File
@@ -1409,6 +1409,25 @@ def _airport_push_cache_max_age_sec(city: str) -> int:
return max(90, interval * 2)
def _cached_payload_observation_epoch(payload: Dict[str, Any]) -> Optional[int]:
amos = payload.get("amos") or {}
airport_primary = payload.get("airport_primary") or {}
airport_current = payload.get("airport_current") or {}
current = payload.get("current") or {}
candidates = [
amos.get("observation_time"),
airport_primary.get("obs_time"),
airport_current.get("obs_time"),
current.get("obs_time"),
]
parsed = [
timestamp
for timestamp in (_parse_observation_time_epoch(value) for value in candidates)
if timestamp is not None
]
return max(parsed) if parsed else None
def _read_cached_airport_city_weather(city: str, max_age_sec: Optional[int] = None) -> Optional[Dict[str, Any]]:
"""Read web city cache for Telegram without triggering collection."""
normalized_city = (city or "").strip().lower()
@@ -1416,7 +1435,7 @@ def _read_cached_airport_city_weather(city: str, max_age_sec: Optional[int] = No
return None
max_age = int(max_age_sec if max_age_sec is not None else _airport_push_cache_max_age_sec(normalized_city))
now_ts = time.time()
stale_candidate: Optional[Tuple[float, Dict[str, Any]]] = None
candidates: List[Tuple[int, int, float, str, Dict[str, Any]]] = []
try:
db = DBManager()
for kind in ("full", "panel"):
@@ -1428,6 +1447,7 @@ def _read_cached_airport_city_weather(city: str, max_age_sec: Optional[int] = No
if updated_at_ts <= 0 or not isinstance(payload, dict):
continue
age_sec = now_ts - updated_at_ts
is_fresh = age_sec <= max_age
if age_sec > max_age:
logger.debug(
"airport push cache stale city={} kind={} age_sec={} max_age_sec={}",
@@ -1436,21 +1456,32 @@ def _read_cached_airport_city_weather(city: str, max_age_sec: Optional[int] = No
round(age_sec, 1),
max_age,
)
if stale_candidate is None or updated_at_ts > stale_candidate[0]:
stale_candidate = (updated_at_ts, dict(payload))
continue
logger.debug(
"airport push cache hit city={} kind={} age_sec={}",
normalized_city,
kind,
round(max(0.0, age_sec), 1),
observation_ts = _cached_payload_observation_epoch(payload)
candidates.append(
(
int(observation_ts or 0),
1 if is_fresh else 0,
updated_at_ts,
kind,
dict(payload),
)
)
return dict(payload)
except Exception as exc:
logger.debug("airport push city cache read failed city={}: {}", normalized_city, exc)
if stale_candidate is not None:
logger.debug("airport push using stale cache city={}", normalized_city)
return stale_candidate[1]
if candidates:
observation_ts, is_fresh, updated_at_ts, kind, payload = max(
candidates,
key=lambda item: (item[0], item[1], item[2]),
)
logger.debug(
"airport push cache selected city={} kind={} age_sec={} observation_ts={} fresh={}",
normalized_city,
kind,
round(max(0.0, now_ts - updated_at_ts), 1),
observation_ts or None,
bool(is_fresh),
)
return payload
return None
+35
View File
@@ -251,6 +251,41 @@ def test_high_freq_airport_push_prefers_fresh_city_cache(monkeypatch):
assert bot.messages
def test_airport_push_prefers_cache_with_newest_observation(monkeypatch):
import src.utils.telegram_push as telegram_push
now = telegram_push.time.time()
class FakeDB:
def get_city_cache(self, kind, city):
assert city == "shanghai"
if kind == "full":
return {
"updated_at_ts": now,
"payload": {
"current": {"temp": 31.0},
"airport_primary": {"temp": 31.0, "obs_time": "15:00"},
},
}
return {
"updated_at_ts": now - 120,
"payload": {
"current": {"temp": 31.4},
"amos": {"observation_time": "2026-06-11T07:42:00+00:00"},
"airport_primary": {
"temp": 31.4,
"obs_time": "2026-06-11T07:42:00+00:00",
},
},
}
monkeypatch.setattr(telegram_push, "DBManager", lambda: FakeDB())
city_weather = telegram_push._read_cached_airport_city_weather("shanghai")
assert city_weather["airport_primary"]["temp"] == 31.4
def test_airport_push_fallback_analysis_does_not_force_observation_refresh(monkeypatch):
import src.utils.telegram_push as telegram_push
import web.app as web_app