From 2424b685325dbf57157042e8893b02fe6d45ff87 Mon Sep 17 00:00:00 2001 From: "2569718930@qq.com" <2569718930@qq.com> Date: Mon, 1 Jun 2026 01:03:33 +0800 Subject: [PATCH] Compact scan runway history payload --- tests/test_scan_terminal_modules.py | 37 ++++++++++++++++++++++++++++- web/scan_terminal_city_row.py | 24 +++++++++++++++++-- 2 files changed, 58 insertions(+), 3 deletions(-) diff --git a/tests/test_scan_terminal_modules.py b/tests/test_scan_terminal_modules.py index 3cf15b9b..b906b1c5 100644 --- a/tests/test_scan_terminal_modules.py +++ b/tests/test_scan_terminal_modules.py @@ -6,6 +6,7 @@ from web.scan_terminal_payloads import ( build_stale_scan_terminal_payload, ) from web.scan_terminal_ranker import build_ranked_scan_terminal_result +from web.scan_terminal_city_row import _build_quick_row from web.routers.scan import router as scan_router @@ -137,6 +138,41 @@ def test_scan_terminal_snapshot_id_is_stable_for_same_ranked_inputs(): assert first.startswith("scan-") +def test_scan_terminal_quick_row_compacts_runway_history_for_list_payload(): + raw_history = { + "35R": [ + {"time": "2026-05-31T00:00:00+00:00", "temp": 22.11}, + {"time": "2026-05-31T00:01:00+00:00", "temp": 22.22}, + {"time": "2026-05-31T00:02:00+00:00", "temp": 22.33}, + {"time": "2026-05-31T00:10:00+00:00", "temp": 23.44}, + {"time": "2026-05-31T00:11:00+00:00", "temp": 23.55}, + ] + } + + row = _build_quick_row( + city="shanghai", + data={ + "display_name": "Shanghai", + "local_date": "2026-05-31", + "local_time": "2026-05-31T08:11:00+08:00", + "temp_symbol": "°C", + "current": {"temp": 22.3, "max_so_far": 23.0}, + "risk": {"airport": "Shanghai Pudong", "level": "medium"}, + "deb": {"prediction": 24.0}, + "probabilities": {"distribution": []}, + "multi_model": {}, + "runway_plate_history": raw_history, + }, + ) + + compact_history = row["runway_plate_history"]["35R"] + + assert len(compact_history) == 2 + assert compact_history[0]["temp"] == 22.3 + assert compact_history[1]["temp"] == 23.6 + assert len(str(row["runway_plate_history"])) < len(str(raw_history)) + + def test_metar_gate_vetoes_yes_when_observed_breaks_above_bucket(): row = { "id": "yes-row", @@ -158,4 +194,3 @@ def test_metar_gate_vetoes_yes_when_observed_breaks_above_bucket(): assert row["v4_metar_decision"] == "veto" assert row["ai_decision"] == "veto" assert "越过目标桶上沿" in row["ai_reason_zh"] - diff --git a/web/scan_terminal_city_row.py b/web/scan_terminal_city_row.py index fe79ee0c..28c7bc40 100644 --- a/web/scan_terminal_city_row.py +++ b/web/scan_terminal_city_row.py @@ -11,6 +11,22 @@ from web.scan_terminal_filters import ( market_region_from_tz_offset as _market_region_from_tz_offset, safe_int as _safe_int, ) +from web.services.city_payloads import aggregate_runway_history + + +SCAN_ROW_RUNWAY_HISTORY_RESOLUTION = "10m" +SCAN_ROW_MAX_RUNWAY_POINTS = 144 + + +def _compact_runway_plate_history_for_scan(raw_history: Any) -> Dict[str, List[Dict[str, Any]]]: + if not isinstance(raw_history, dict) or not raw_history: + return {} + compacted = aggregate_runway_history(raw_history, SCAN_ROW_RUNWAY_HISTORY_RESOLUTION) + return { + str(runway): points[-SCAN_ROW_MAX_RUNWAY_POINTS:] + for runway, points in compacted.items() + if isinstance(points, list) and points + } def _resolve_time_range_dates(data: Dict[str, Any], time_range: str) -> List[str]: @@ -132,7 +148,9 @@ def _build_terminal_row( "amos": data.get("amos") or None, "top_buckets": scan.get("top_buckets") or [], "all_buckets": scan.get("all_buckets") or [], - "runway_plate_history": data.get("runway_plate_history") or {}, + "runway_plate_history": _compact_runway_plate_history_for_scan( + data.get("runway_plate_history") + ), } @@ -232,7 +250,9 @@ def _build_quick_row( "is_primary_signal": True, "accepting_orders": False, "row_id": row_id, - "runway_plate_history": data.get("runway_plate_history") or {}, + "runway_plate_history": _compact_runway_plate_history_for_scan( + data.get("runway_plate_history") + ), } # Compute a simple edge: model top probability vs neutral best_model_prob = max(