Use latest AMSC runway timestamp for overlays
This commit is contained in:
@@ -164,6 +164,7 @@ def _amsc_parse_wind_plate_payload(
|
|||||||
valid_values = []
|
valid_values = []
|
||||||
observation_time = None
|
observation_time = None
|
||||||
observation_time_local = None
|
observation_time_local = None
|
||||||
|
observation_time_epoch = None
|
||||||
raw_metar = None
|
raw_metar = None
|
||||||
|
|
||||||
for key, raw_row in data.items():
|
for key, raw_row in data.items():
|
||||||
@@ -179,8 +180,22 @@ def _amsc_parse_wind_plate_payload(
|
|||||||
if not points:
|
if not points:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if observation_time is None:
|
row_observation_time, row_observation_time_local = _amsc_parse_utc_time(raw_row.get("OTIME"))
|
||||||
observation_time, observation_time_local = _amsc_parse_utc_time(raw_row.get("OTIME"))
|
row_observation_epoch = None
|
||||||
|
if row_observation_time:
|
||||||
|
try:
|
||||||
|
row_observation_epoch = datetime.fromisoformat(row_observation_time).timestamp()
|
||||||
|
except ValueError:
|
||||||
|
row_observation_epoch = None
|
||||||
|
if row_observation_epoch is not None and (
|
||||||
|
observation_time_epoch is None or row_observation_epoch > observation_time_epoch
|
||||||
|
):
|
||||||
|
observation_time = row_observation_time
|
||||||
|
observation_time_local = row_observation_time_local
|
||||||
|
observation_time_epoch = row_observation_epoch
|
||||||
|
elif observation_time is None and row_observation_time:
|
||||||
|
observation_time = row_observation_time
|
||||||
|
observation_time_local = row_observation_time_local
|
||||||
if raw_metar is None and raw_row.get("METAR"):
|
if raw_metar is None and raw_row.get("METAR"):
|
||||||
raw_metar = str(raw_row.get("METAR"))
|
raw_metar = str(raw_row.get("METAR"))
|
||||||
|
|
||||||
|
|||||||
@@ -135,6 +135,36 @@ def test_parse_wind_plate_payload_uses_end_temperature_when_target_is_second_run
|
|||||||
assert parsed["runway_obs"]["point_temperatures"][0]["target_runway_max"] == 33.7
|
assert parsed["runway_obs"]["point_temperatures"][0]["target_runway_max"] == 33.7
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_wind_plate_payload_uses_latest_runway_observation_time():
|
||||||
|
payload = {
|
||||||
|
"code": 200,
|
||||||
|
"data": {
|
||||||
|
"16L/34R": {
|
||||||
|
"RNO": "16L/34R",
|
||||||
|
"OTIME": "2026-06-14 10:44:00",
|
||||||
|
"TDZ_TEMP": "22.0",
|
||||||
|
"MID_TEMP": "22.1",
|
||||||
|
"END_TEMP": "22.2",
|
||||||
|
},
|
||||||
|
"17L/35R": {
|
||||||
|
"RNO": "17L/35R",
|
||||||
|
"OTIME": "2026-06-14 15:43:00",
|
||||||
|
"TDZ_TEMP": "24.9",
|
||||||
|
"MID_TEMP": "25.1",
|
||||||
|
"END_TEMP": "25.4",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
parsed = _amsc_parse_wind_plate_payload(payload, city_key="shanghai", icao="ZSPD")
|
||||||
|
|
||||||
|
assert parsed is not None
|
||||||
|
assert parsed["settlement_runway"] == "35R"
|
||||||
|
assert parsed["temp_c"] == 25.4
|
||||||
|
assert parsed["observation_time"] == "2026-06-14T15:43:00+00:00"
|
||||||
|
assert parsed["observation_time_local"] == "2026-06-14 23:43:00"
|
||||||
|
|
||||||
|
|
||||||
def test_parse_wind_plate_payload_rejects_unauthorized_or_empty_payloads():
|
def test_parse_wind_plate_payload_rejects_unauthorized_or_empty_payloads():
|
||||||
assert _amsc_parse_wind_plate_payload(
|
assert _amsc_parse_wind_plate_payload(
|
||||||
{"errCode": -12010, "errMsg": "无权访问此接口"},
|
{"errCode": -12010, "errMsg": "无权访问此接口"},
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ def test_scan_terminal_response_includes_backend_server_timing(monkeypatch):
|
|||||||
monkeypatch.setattr(
|
monkeypatch.setattr(
|
||||||
scan_api.legacy_routes,
|
scan_api.legacy_routes,
|
||||||
"build_scan_terminal_payload",
|
"build_scan_terminal_payload",
|
||||||
lambda filters, force_refresh=False, timing_recorder=None: {
|
lambda filters, force_refresh=False, timing_recorder=None, **_kwargs: {
|
||||||
"rows": [],
|
"rows": [],
|
||||||
"filters": filters,
|
"filters": filters,
|
||||||
"status": "ready",
|
"status": "ready",
|
||||||
@@ -155,7 +155,7 @@ def test_scan_terminal_stale_response_is_not_cached(monkeypatch):
|
|||||||
monkeypatch.setattr(
|
monkeypatch.setattr(
|
||||||
scan_api.legacy_routes,
|
scan_api.legacy_routes,
|
||||||
"build_scan_terminal_payload",
|
"build_scan_terminal_payload",
|
||||||
lambda filters, force_refresh=False, timing_recorder=None: {
|
lambda filters, force_refresh=False, timing_recorder=None, **_kwargs: {
|
||||||
"rows": [],
|
"rows": [],
|
||||||
"filters": filters,
|
"filters": filters,
|
||||||
"status": "ready",
|
"status": "ready",
|
||||||
|
|||||||
@@ -58,13 +58,18 @@ def _block_epoch(block: Any) -> Optional[int]:
|
|||||||
|
|
||||||
|
|
||||||
def _raw_amsc_epoch(row: dict[str, Any], raw_payload: dict[str, Any]) -> Optional[int]:
|
def _raw_amsc_epoch(row: dict[str, Any], raw_payload: dict[str, Any]) -> Optional[int]:
|
||||||
values = (
|
payload_values = (
|
||||||
raw_payload.get("observation_time"),
|
raw_payload.get("observation_time"),
|
||||||
raw_payload.get("observed_at"),
|
raw_payload.get("observed_at"),
|
||||||
row.get("observed_at"),
|
|
||||||
)
|
)
|
||||||
parsed = [epoch for epoch in (parse_observation_epoch(value) for value in values) if epoch is not None]
|
parsed = [
|
||||||
return max(parsed) if parsed else None
|
epoch
|
||||||
|
for epoch in (parse_observation_epoch(value) for value in payload_values)
|
||||||
|
if epoch is not None
|
||||||
|
]
|
||||||
|
if parsed:
|
||||||
|
return max(parsed)
|
||||||
|
return parse_observation_epoch(row.get("observed_at"))
|
||||||
|
|
||||||
|
|
||||||
def _to_float(value: Any) -> Optional[float]:
|
def _to_float(value: Any) -> Optional[float]:
|
||||||
|
|||||||
Reference in New Issue
Block a user