Backfill runway history from latest AMSC overlay
This commit is contained in:
@@ -49,6 +49,8 @@ def test_overlay_replaces_amos_when_old_local_time_string_looks_later_than_new_u
|
|||||||
|
|
||||||
|
|
||||||
def test_overlay_appends_latest_amsc_points_to_runway_history():
|
def test_overlay_appends_latest_amsc_points_to_runway_history():
|
||||||
|
runway_writes = []
|
||||||
|
|
||||||
class FakeDB:
|
class FakeDB:
|
||||||
def get_latest_raw_observation(self, source, city):
|
def get_latest_raw_observation(self, source, city):
|
||||||
assert (source, city) == ("amsc_awos", "chengdu")
|
assert (source, city) == ("amsc_awos", "chengdu")
|
||||||
@@ -83,6 +85,9 @@ def test_overlay_appends_latest_amsc_points_to_runway_history():
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def append_runway_obs(self, **kwargs):
|
||||||
|
runway_writes.append(kwargs)
|
||||||
|
|
||||||
stale_payload = {
|
stale_payload = {
|
||||||
"name": "chengdu",
|
"name": "chengdu",
|
||||||
"temp_symbol": "°C",
|
"temp_symbol": "°C",
|
||||||
@@ -105,6 +110,11 @@ def test_overlay_appends_latest_amsc_points_to_runway_history():
|
|||||||
"time": "2026-06-15T10:51:00+00:00",
|
"time": "2026-06-15T10:51:00+00:00",
|
||||||
"temp": 28.1,
|
"temp": 28.1,
|
||||||
}
|
}
|
||||||
|
assert [(row["icao"], row["runway"], row["otime_utc"]) for row in runway_writes] == [
|
||||||
|
("ZUUU", "02L/20R", "2026-06-15T10:51:00+00:00"),
|
||||||
|
("ZUUU", "02R/20L", "2026-06-15T10:51:00+00:00"),
|
||||||
|
]
|
||||||
|
assert runway_writes[0]["target_runway_max"] == 28.4
|
||||||
|
|
||||||
|
|
||||||
def test_overlay_uses_latest_success_when_newer_status_row_has_no_observation():
|
def test_overlay_uses_latest_success_when_newer_status_row_has_no_observation():
|
||||||
|
|||||||
@@ -88,6 +88,15 @@ def _to_float(value: Any) -> Optional[float]:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _to_int(value: Any) -> Optional[int]:
|
||||||
|
try:
|
||||||
|
if value is None or value == "":
|
||||||
|
return None
|
||||||
|
return int(float(value))
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def _amsc_payload_has_observation(raw_payload: dict[str, Any]) -> bool:
|
def _amsc_payload_has_observation(raw_payload: dict[str, Any]) -> bool:
|
||||||
temp = raw_payload.get("temp_c") if raw_payload.get("temp_c") is not None else raw_payload.get("temp")
|
temp = raw_payload.get("temp_c") if raw_payload.get("temp_c") is not None else raw_payload.get("temp")
|
||||||
return _to_float(temp) is not None
|
return _to_float(temp) is not None
|
||||||
@@ -200,6 +209,8 @@ def _runway_history_temp(point: dict[str, Any]) -> Optional[float]:
|
|||||||
|
|
||||||
|
|
||||||
def _append_latest_amsc_runway_history(
|
def _append_latest_amsc_runway_history(
|
||||||
|
db: Any,
|
||||||
|
city: str,
|
||||||
payload: dict[str, Any],
|
payload: dict[str, Any],
|
||||||
row: dict[str, Any],
|
row: dict[str, Any],
|
||||||
raw_payload: dict[str, Any],
|
raw_payload: dict[str, Any],
|
||||||
@@ -226,6 +237,8 @@ def _append_latest_amsc_runway_history(
|
|||||||
history = deepcopy(history)
|
history = deepcopy(history)
|
||||||
|
|
||||||
use_fahrenheit = "F" in str(payload.get("temp_symbol") or "").upper()
|
use_fahrenheit = "F" in str(payload.get("temp_symbol") or "").upper()
|
||||||
|
appender = getattr(db, "append_runway_obs", None)
|
||||||
|
icao = str(raw_payload.get("icao") or row.get("station_code") or "").strip().upper()
|
||||||
changed = False
|
changed = False
|
||||||
for point in point_temperatures:
|
for point in point_temperatures:
|
||||||
if not isinstance(point, dict):
|
if not isinstance(point, dict):
|
||||||
@@ -262,6 +275,30 @@ def _append_latest_amsc_runway_history(
|
|||||||
runway_history.append(entry)
|
runway_history.append(entry)
|
||||||
changed = True
|
changed = True
|
||||||
history[runway] = runway_history
|
history[runway] = runway_history
|
||||||
|
if callable(appender) and icao:
|
||||||
|
try:
|
||||||
|
appender(
|
||||||
|
icao=icao,
|
||||||
|
city=city,
|
||||||
|
runway=runway,
|
||||||
|
tdz_temp=_to_float(point.get("tdz_temp")),
|
||||||
|
mid_temp=_to_float(point.get("mid_temp")),
|
||||||
|
end_temp=_to_float(point.get("end_temp")),
|
||||||
|
target_runway_max=_to_float(point.get("target_runway_max")),
|
||||||
|
wind_dir=_to_int(point.get("wind_dir")),
|
||||||
|
wind_speed=_to_float(point.get("wind_speed")),
|
||||||
|
rvr=_to_int(point.get("rvr")),
|
||||||
|
mor=_to_float(point.get("mor")),
|
||||||
|
humidity=_to_float(point.get("humidity")),
|
||||||
|
otime_utc=observed_at,
|
||||||
|
)
|
||||||
|
except Exception as exc:
|
||||||
|
logger.debug(
|
||||||
|
"latest AMSC runway history persist skipped city={} runway={}: {}",
|
||||||
|
city,
|
||||||
|
runway,
|
||||||
|
exc,
|
||||||
|
)
|
||||||
|
|
||||||
if changed:
|
if changed:
|
||||||
payload["runway_plate_history"] = history
|
payload["runway_plate_history"] = history
|
||||||
@@ -297,7 +334,7 @@ def overlay_latest_amsc_observation(
|
|||||||
for key in ("current", "airport_primary", "airport_current"):
|
for key in ("current", "airport_primary", "airport_current"):
|
||||||
changed = _merge_observation_block(next_payload, key, update, raw_epoch) or changed
|
changed = _merge_observation_block(next_payload, key, update, raw_epoch) or changed
|
||||||
|
|
||||||
changed = _append_latest_amsc_runway_history(next_payload, row, raw_payload) or changed
|
changed = _append_latest_amsc_runway_history(db, normalized_city, next_payload, row, raw_payload) or changed
|
||||||
|
|
||||||
canonical = next_payload.get("canonical_temperature")
|
canonical = next_payload.get("canonical_temperature")
|
||||||
canonical_epoch = _block_epoch(canonical)
|
canonical_epoch = _block_epoch(canonical)
|
||||||
|
|||||||
Reference in New Issue
Block a user