fix: align telegram runway slope with settlement endpoint

This commit is contained in:
2569718930@qq.com
2026-05-28 11:05:50 +08:00
parent f56c604677
commit eef611adb4
2 changed files with 70 additions and 6 deletions
+13 -6
View File
@@ -897,14 +897,24 @@ def _wind_regime_label(city: str, wind_dir: Optional[int], language: Optional[st
return None
def _compute_slope_15m(icao: str, current_temp: float) -> Optional[float]:
def _runway_row_temp_for_city(city: str, row: Dict[str, Any]) -> Optional[float]:
endpoint = _settlement_endpoint_for_point(city, row.get("runway"), row)
if endpoint is not None:
return _safe_float(endpoint.get("temp"))
target_runway_max = _safe_float(row.get("target_runway_max"))
if target_runway_max is not None:
return target_runway_max
return _safe_float(row.get("tdz_temp"))
def _compute_slope_15m(icao: str, current_temp: float, city: str = "") -> Optional[float]:
"""Estimate 15-minute temperature trend from runway_obs_log."""
try:
db = DBManager()
rows = db.get_runway_obs_recent(icao, minutes=20)
temps = []
for r in rows:
t = r.get("target_runway_max") or r.get("tdz_temp")
t = _runway_row_temp_for_city(city, r)
if t is not None:
temps.append(float(t))
if len(temps) >= 2:
@@ -1147,7 +1157,7 @@ def _build_airport_status_message(
# ── Heat model ──
wind_dir = amos.get("wind_dir") if is_amsc else None
slope_15m = _compute_slope_15m(amos_icao, display_temp) if is_amsc and display_temp is not None else None
slope_15m = _compute_slope_15m(amos_icao, display_temp, city) if is_amsc and display_temp is not None else None
heat_signal = _runway_heat_signal(display_temp or 0, slope_15m, wind_dir, city, language) if is_amsc else ""
wind_label = _wind_regime_label(city, wind_dir, language) if is_amsc and wind_dir is not None else None
@@ -1192,14 +1202,11 @@ def _build_airport_status_message(
end = pts.get("end_temp")
is_settlement = _is_settlement_runway(city, r1, r2)
marker = f" {_copy(language, '★Settlement', '★结算')}" if is_settlement else ""
tmax = pts.get("target_runway_max")
if tdz is not None or mid is not None or end is not None:
line = f"{r1}/{r2}{marker} TDZ:{_fmt(tdz)} MID:{_fmt(mid)} END:{_fmt(end)}"
settlement_line_endpoint = _settlement_endpoint_for_point(city, (r1, r2), pts) if is_settlement else None
if settlement_line_endpoint is not None:
line += f" settle:{float(settlement_line_endpoint['temp']):.1f}"
elif tmax is not None:
line += f" max:{float(tmax):.1f}"
lines.append(line)
else:
temp_symbol = str(city_weather.get("temp_symbol") or "°C").strip()
+57
View File
@@ -2,6 +2,7 @@ from src.utils.telegram_push import (
HIGH_FREQ_AIRPORT_CITIES,
HIGH_FREQ_AIRPORT_ICAO,
_build_airport_status_message,
_compute_slope_15m,
_run_high_freq_airport_cycle,
_telegram_push_language,
)
@@ -43,6 +44,7 @@ def test_airport_status_message_defaults_to_bilingual_runway_copy(monkeypatch):
assert "TDZ:23.0" in text
assert "Settlement runway now / 结算跑道当前:" in text
assert "Today's runway high / 今日跑道高点:" in text
assert "max:" not in text
assert "DEB: 24.0°C" in text
@@ -98,6 +100,61 @@ def test_airport_status_uses_tdz_when_settlement_target_is_first_runway():
assert "02L/20R ★Settlement / ★结算 TDZ:24.4 MID:26.1 END:27.9 settle:24.4" in text
assert "Settlement runway now / 结算跑道当前: 24.4°C" in text
assert "Settlement runway now / 结算跑道当前: 27.9°C" not in text
assert "max:" not in text
def test_airport_status_removes_max_when_runway_endpoints_are_shown():
text = _build_airport_status_message(
"shanghai",
{
"current": {"temp": 24.0},
"airport_current": {"max_so_far": 25.0, "max_temp_time": "07:00"},
"amos": {
"source": "amsc_awos",
"runway_obs": {
"runway_pairs": [("35R", "17L"), ("34L", "16R")],
"temperatures": [(25.2, None), (25.4, None)],
"point_temperatures": [
{"runway": "35R/17L", "tdz_temp": 25.2, "mid_temp": None, "end_temp": 24.6, "target_runway_max": 25.2},
{"runway": "34L/16R", "tdz_temp": 25.4, "mid_temp": None, "end_temp": 24.8, "target_runway_max": 25.4},
],
},
},
},
27.2,
"10:58",
)
assert "35R/17L ★Settlement / ★结算 TDZ:25.2 MID:-- END:24.6 settle:25.2" in text
assert "34L/16R TDZ:25.4 MID:-- END:24.8" in text
assert "max:" not in text
def test_telegram_slope_uses_settlement_endpoint_not_runway_max(monkeypatch):
import src.utils.telegram_push as telegram_push
class FakeDB:
def get_runway_obs_recent(self, icao, minutes=20):
return [
{
"runway": "20R/02L",
"tdz_temp": 33.7,
"mid_temp": 34.1,
"end_temp": 30.8,
"target_runway_max": 34.1,
},
{
"runway": "20R/02L",
"tdz_temp": 33.8,
"mid_temp": 34.5,
"end_temp": 31.2,
"target_runway_max": 34.5,
},
]
monkeypatch.setattr(telegram_push, "DBManager", lambda: FakeDB())
assert _compute_slope_15m("ZUCK", 31.2, "chongqing") == 0.4
def test_singapore_is_in_telegram_push_city_lists():