171 lines
6.2 KiB
Python
171 lines
6.2 KiB
Python
from src.data_collection.amsc_awos_sources import (
|
|
AmscAwosSourceMixin,
|
|
_amsc_parse_wind_plate_payload,
|
|
_amsc_supported_city_codes,
|
|
)
|
|
|
|
|
|
ZBAA_SAMPLE = {
|
|
"code": 200,
|
|
"msg": "操作成功",
|
|
"data": {
|
|
"18R/36L": {
|
|
"RNO": "18R/36L",
|
|
"OTIME": "2026-05-14 17:19:00",
|
|
"TDZ_TEMP": "20.8",
|
|
"MID_TEMP": "-",
|
|
"END_TEMP": "20.8",
|
|
"TDZ_HUMID": "67",
|
|
"END_HUMID": "67",
|
|
"METAR": "METAR ZBAA 141700Z 12003MPS 050V170 7000 NSC 21/15 Q1014 NOSIG=",
|
|
},
|
|
"18L/36R": {
|
|
"RNO": "18L/36R",
|
|
"OTIME": "2026-05-14 17:19:00",
|
|
"TDZ_TEMP": "21.0",
|
|
"MID_TEMP": "-",
|
|
"END_TEMP": "20.4",
|
|
"METAR": "METAR ZBAA 141700Z 12003MPS 050V170 7000 NSC 21/15 Q1014 NOSIG=",
|
|
},
|
|
"19/01": {
|
|
"RNO": "19/01",
|
|
"OTIME": "2026-05-14 17:19:00",
|
|
"TDZ_TEMP": "20.2",
|
|
"MID_TEMP": "-",
|
|
"END_TEMP": "20.2",
|
|
"METAR": "METAR ZBAA 141700Z 12003MPS 050V170 7000 NSC 21/15 Q1014 NOSIG=",
|
|
},
|
|
},
|
|
}
|
|
|
|
|
|
def test_parse_wind_plate_payload_normalizes_runway_point_temperatures():
|
|
parsed = _amsc_parse_wind_plate_payload(ZBAA_SAMPLE, city_key="beijing", icao="ZBAA")
|
|
|
|
assert parsed["source"] == "amsc_awos"
|
|
assert parsed["source_label"] == "AMSC AWOS Beijing Capital (ZBAA)"
|
|
assert parsed["observation_source_zh"] == "AMSC AWOS 跑道观测气温"
|
|
assert parsed["icao"] == "ZBAA"
|
|
assert parsed["temp_c"] == 20.2
|
|
assert parsed["temp_source"] == "settlement_runway_endpoint"
|
|
assert parsed["settlement_runway"] == "01"
|
|
assert parsed["settlement_runway_pair"] == "19/01"
|
|
assert parsed["settlement_runway_position"] == "end"
|
|
assert parsed["runway_temp_range"] == (20.2, 21.0)
|
|
assert parsed["observation_time"] == "2026-05-14T17:19:00+00:00"
|
|
assert parsed["observation_time_local"] == "2026-05-15 01:19:00"
|
|
assert parsed["raw_metar"].startswith("METAR ZBAA 141700Z")
|
|
|
|
runway_obs = parsed["runway_obs"]
|
|
assert runway_obs["runway_pairs"] == [("18R", "36L"), ("18L", "36R"), ("19", "01")]
|
|
assert runway_obs["temperatures"] == [(20.8, None), (21.0, None), (20.2, None)]
|
|
pt0 = runway_obs["point_temperatures"][0]
|
|
assert pt0["runway"] == "18R/36L"
|
|
assert pt0["tdz_temp"] == 20.8
|
|
assert pt0["mid_temp"] is None
|
|
assert pt0["end_temp"] == 20.8
|
|
assert pt0["target_runway_max"] == 20.8
|
|
assert pt0["wind_dir"] is None
|
|
assert pt0["wind_speed"] is None
|
|
assert pt0["rvr"] is None
|
|
assert pt0["mor"] is None
|
|
assert pt0["humidity"] == 67.0
|
|
settlement_pt = runway_obs["point_temperatures"][2]
|
|
assert settlement_pt["runway"] == "19/01"
|
|
assert settlement_pt["is_settlement"] is True
|
|
assert settlement_pt["target_runway_max"] == 20.2
|
|
|
|
|
|
def test_parse_wind_plate_payload_uses_settlement_runway_endpoint_temperature():
|
|
payload = {
|
|
"code": 200,
|
|
"data": {
|
|
"04/22": {
|
|
"RNO": "04/22",
|
|
"OTIME": "2026-05-14 17:19:00",
|
|
"TDZ_TEMP": "31.2",
|
|
"MID_TEMP": "33.4",
|
|
"END_TEMP": "32.6",
|
|
},
|
|
"05/23": {
|
|
"RNO": "05/23",
|
|
"OTIME": "2026-05-14 17:19:00",
|
|
"TDZ_TEMP": "34.8",
|
|
"MID_TEMP": "35.1",
|
|
"END_TEMP": "34.2",
|
|
},
|
|
},
|
|
}
|
|
|
|
parsed = _amsc_parse_wind_plate_payload(payload, city_key="wuhan", icao="ZHHH")
|
|
|
|
assert parsed is not None
|
|
assert parsed["temp_c"] == 31.2
|
|
assert parsed["temp_source"] == "settlement_runway_endpoint"
|
|
assert parsed["settlement_runway"] == "04"
|
|
assert parsed["settlement_runway_pair"] == "04/22"
|
|
assert parsed["settlement_runway_position"] == "tdz"
|
|
settlement_point = parsed["runway_obs"]["point_temperatures"][0]
|
|
assert settlement_point["is_settlement"] is True
|
|
assert settlement_point["settlement_runway"] == "04"
|
|
assert settlement_point["target_runway_max"] == 31.2
|
|
|
|
|
|
def test_parse_wind_plate_payload_uses_end_temperature_when_target_is_second_runway():
|
|
payload = {
|
|
"code": 200,
|
|
"data": {
|
|
"20R/02L": {
|
|
"RNO": "20R/02L",
|
|
"OTIME": "2026-05-14 17:19:00",
|
|
"TDZ_TEMP": "34.4",
|
|
"MID_TEMP": "35.2",
|
|
"END_TEMP": "33.7",
|
|
},
|
|
},
|
|
}
|
|
|
|
parsed = _amsc_parse_wind_plate_payload(payload, city_key="chongqing", icao="ZUCK")
|
|
|
|
assert parsed is not None
|
|
assert parsed["temp_c"] == 33.7
|
|
assert parsed["settlement_runway"] == "02L"
|
|
assert parsed["settlement_runway_pair"] == "20R/02L"
|
|
assert parsed["settlement_runway_position"] == "end"
|
|
assert parsed["runway_obs"]["point_temperatures"][0]["target_runway_max"] == 33.7
|
|
|
|
|
|
def test_parse_wind_plate_payload_rejects_unauthorized_or_empty_payloads():
|
|
assert _amsc_parse_wind_plate_payload(
|
|
{"errCode": -12010, "errMsg": "无权访问此接口"},
|
|
city_key="beijing",
|
|
icao="ZBAA",
|
|
) is None
|
|
assert _amsc_parse_wind_plate_payload({"code": 200, "data": {}}, city_key="beijing", icao="ZBAA") is None
|
|
|
|
|
|
def test_fetch_amsc_official_current_uses_domestic_city_whitelist(monkeypatch):
|
|
import src.data_collection.amsc_awos_sources as _amsc
|
|
from src.data_collection.observation_source_gate import reset_observation_source_gate_for_tests
|
|
|
|
monkeypatch.setenv("POLYWEATHER_OBSERVATION_SOURCE_DB_LOCK_ENABLED", "false")
|
|
reset_observation_source_gate_for_tests()
|
|
monkeypatch.setattr(_amsc, "AMSC_AWOS_BASE_URL", "https://www.amsc.net.cn/gateway/api/saas/rest/amc/AwosController/getWindPlate")
|
|
assert _amsc_supported_city_codes()["beijing"] == "ZBAA"
|
|
assert "new york" not in _amsc_supported_city_codes()
|
|
|
|
class FakeCollector(AmscAwosSourceMixin):
|
|
timeout = 1.0
|
|
|
|
def _amsc_http_get_json(self, url, *, headers=None):
|
|
assert "getWindPlate?cccc=ZBAA" in url
|
|
return ZBAA_SAMPLE
|
|
|
|
data = FakeCollector().fetch_amsc_awos_current("beijing")
|
|
|
|
assert data is not None
|
|
assert data["icao"] == "ZBAA"
|
|
assert data["temp_c"] == 20.2
|
|
assert data["runway_temp_range"] == (20.2, 21.0)
|
|
assert FakeCollector().fetch_amsc_awos_current("new york") is None
|