Use realtime METAR cluster for Moscow nearby maps
This commit is contained in:
@@ -43,7 +43,7 @@ def _provider_code_for_city(city: str) -> str:
|
|||||||
if normalized in {"busan", "seoul"}:
|
if normalized in {"busan", "seoul"}:
|
||||||
return "korea_kma"
|
return "korea_kma"
|
||||||
if normalized == "moscow":
|
if normalized == "moscow":
|
||||||
return "russia_station_web"
|
return "russia_metar_cluster"
|
||||||
if settlement_source == "hko":
|
if settlement_source == "hko":
|
||||||
return "hongkong_hko"
|
return "hongkong_hko"
|
||||||
if settlement_source == "cwa":
|
if settlement_source == "cwa":
|
||||||
@@ -138,6 +138,7 @@ def _metar_cluster_rows(raw: Dict[str, Any]) -> List[Dict[str, Any]]:
|
|||||||
temp=row.get("temp"),
|
temp=row.get("temp"),
|
||||||
lat=row.get("lat"),
|
lat=row.get("lat"),
|
||||||
lon=row.get("lon"),
|
lon=row.get("lon"),
|
||||||
|
obs_time=row.get("obs_time"),
|
||||||
source_code="metar_cluster",
|
source_code="metar_cluster",
|
||||||
source_label="METAR cluster",
|
source_label="METAR cluster",
|
||||||
is_official=False,
|
is_official=False,
|
||||||
@@ -530,22 +531,18 @@ class KoreaKmaNetworkProvider(CountryNetworkProvider):
|
|||||||
|
|
||||||
class RussiaStationWebNetworkProvider(CountryNetworkProvider):
|
class RussiaStationWebNetworkProvider(CountryNetworkProvider):
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
super().__init__("russia_station_web", "Russia station web")
|
super().__init__("russia_metar_cluster", "Moscow METAR network")
|
||||||
|
|
||||||
def official_nearby_current(self, city: str, raw: Dict[str, Any]) -> List[Dict[str, Any]]:
|
def official_nearby_current(self, city: str, raw: Dict[str, Any]) -> List[Dict[str, Any]]:
|
||||||
rows = _ru_rows(raw, city)
|
|
||||||
if rows:
|
|
||||||
return rows
|
|
||||||
return _metar_cluster_rows(raw)
|
return _metar_cluster_rows(raw)
|
||||||
|
|
||||||
def official_network_status(self, city: str, raw: Dict[str, Any]) -> Dict[str, Any]:
|
def official_network_status(self, city: str, raw: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
rows = self.official_nearby_current(city, raw)
|
rows = self.official_nearby_current(city, raw)
|
||||||
has_ru = bool(_ru_rows(raw, city))
|
|
||||||
return {
|
return {
|
||||||
"provider_code": self.provider_code,
|
"provider_code": self.provider_code,
|
||||||
"provider_label": self.provider_label,
|
"provider_label": self.provider_label,
|
||||||
"available": has_ru,
|
"available": bool(rows),
|
||||||
"mode": "official_web_crawl" if has_ru else ("fallback_metar_cluster" if rows else "reference_only"),
|
"mode": "realtime_metar_cluster" if rows else "reference_only",
|
||||||
"row_count": len(rows),
|
"row_count": len(rows),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -574,7 +571,7 @@ def get_country_network_provider(city: str) -> CountryNetworkProvider:
|
|||||||
return TurkeyMgmNetworkProvider()
|
return TurkeyMgmNetworkProvider()
|
||||||
if provider_code == "korea_kma":
|
if provider_code == "korea_kma":
|
||||||
return KoreaKmaNetworkProvider()
|
return KoreaKmaNetworkProvider()
|
||||||
if provider_code == "russia_station_web":
|
if provider_code == "russia_metar_cluster":
|
||||||
return RussiaStationWebNetworkProvider()
|
return RussiaStationWebNetworkProvider()
|
||||||
if provider_code == "japan_jma":
|
if provider_code == "japan_jma":
|
||||||
return JapanJmaNetworkProvider()
|
return JapanJmaNetworkProvider()
|
||||||
|
|||||||
@@ -445,6 +445,7 @@ class MetarSourceMixin:
|
|||||||
"wind_dir": obs.get("wdir"),
|
"wind_dir": obs.get("wdir"),
|
||||||
"wind_speed": obs.get("wspd"),
|
"wind_speed": obs.get("wspd"),
|
||||||
"wind_speed_kt": obs.get("wspd"),
|
"wind_speed_kt": obs.get("wspd"),
|
||||||
|
"obs_time": obs.get("obsTime") or obs.get("reportTime"),
|
||||||
"raw_metar": obs.get("rawOb"),
|
"raw_metar": obs.get("rawOb"),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -221,7 +221,7 @@ class RussiaStationSourceMixin:
|
|||||||
obs_time = parsed.get("obs_time")
|
obs_time = parsed.get("obs_time")
|
||||||
max_stale_sec = max(
|
max_stale_sec = max(
|
||||||
0,
|
0,
|
||||||
int(getattr(self, "ru_station_max_stale_sec", 72 * 3600)),
|
int(getattr(self, "ru_station_max_stale_sec", 4 * 3600)),
|
||||||
)
|
)
|
||||||
if obs_time and max_stale_sec > 0:
|
if obs_time and max_stale_sec > 0:
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ class WeatherDataCollector(OpenMeteoCacheMixin, SettlementSourceMixin, MetarSour
|
|||||||
CITY_METAR_CLUSTERS = {
|
CITY_METAR_CLUSTERS = {
|
||||||
"buenos aires": ["SAEZ", "SABE", "SADP", "SADF", "SADL", "SADJ"],
|
"buenos aires": ["SAEZ", "SABE", "SADP", "SADF", "SADL", "SADJ"],
|
||||||
"istanbul": ["LTFM", "LTBA", "LTFJ"],
|
"istanbul": ["LTFM", "LTBA", "LTFJ"],
|
||||||
"moscow": ["UUWW", "UUEE", "UUDD"],
|
"moscow": ["UUWW", "UUEE", "UUDD", "UUBW", "UUMO"],
|
||||||
"london": ["EGLL", "EGLC", "EGKK", "EGSS", "EGGW"],
|
"london": ["EGLL", "EGLC", "EGKK", "EGSS", "EGGW"],
|
||||||
"new york": ["KLGA", "KJFK", "KEWR", "KTEB", "KHPN"],
|
"new york": ["KLGA", "KJFK", "KEWR", "KTEB", "KHPN"],
|
||||||
"los angeles": ["KLAX", "KBUR", "KLGB", "KSNA", "KVNY"],
|
"los angeles": ["KLAX", "KBUR", "KLGB", "KSNA", "KVNY"],
|
||||||
@@ -213,7 +213,7 @@ class WeatherDataCollector(OpenMeteoCacheMixin, SettlementSourceMixin, MetarSour
|
|||||||
os.getenv("RU_STATION_CACHE_TTL_SEC", "300")
|
os.getenv("RU_STATION_CACHE_TTL_SEC", "300")
|
||||||
)
|
)
|
||||||
self.ru_station_max_stale_sec = int(
|
self.ru_station_max_stale_sec = int(
|
||||||
os.getenv("RU_STATION_MAX_STALE_SEC", str(72 * 3600))
|
os.getenv("RU_STATION_MAX_STALE_SEC", str(4 * 3600))
|
||||||
)
|
)
|
||||||
self._ru_station_cache: Dict[str, Dict] = {}
|
self._ru_station_cache: Dict[str, Dict] = {}
|
||||||
self._ru_station_cache_lock = threading.Lock()
|
self._ru_station_cache_lock = threading.Lock()
|
||||||
@@ -891,10 +891,10 @@ class WeatherDataCollector(OpenMeteoCacheMixin, SettlementSourceMixin, MetarSour
|
|||||||
)
|
)
|
||||||
if not official_rows:
|
if not official_rows:
|
||||||
return
|
return
|
||||||
results["ru_official_nearby"] = official_rows
|
# Pogodaiklimat station rows are SYNOP/archive-style reference observations,
|
||||||
if "mgm_nearby" not in results:
|
# not realtime enough for the map. Keep them out of official_nearby/mgm_nearby
|
||||||
results["mgm_nearby"] = official_rows
|
# so Moscow uses the live METAR cluster for nearby map temperatures.
|
||||||
results["nearby_source"] = "ru_station_web"
|
results["ru_reference_nearby"] = official_rows
|
||||||
|
|
||||||
def _attach_warsaw_official_nearby(
|
def _attach_warsaw_official_nearby(
|
||||||
self, results: Dict, use_fahrenheit: bool
|
self, results: Dict, use_fahrenheit: bool
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ def test_hko_provider_marks_explicit_official_station_as_anchor():
|
|||||||
assert snapshot["official_nearby"][0]["station_code"] == "LFS"
|
assert snapshot["official_nearby"][0]["station_code"] == "LFS"
|
||||||
|
|
||||||
|
|
||||||
def test_russia_provider_prefers_official_web_rows_when_available():
|
def test_moscow_provider_uses_realtime_metar_cluster_not_station_archive_rows():
|
||||||
raw = {
|
raw = {
|
||||||
"metar": {
|
"metar": {
|
||||||
"observation_time": "2026-04-06T10:00:00.000Z",
|
"observation_time": "2026-04-06T10:00:00.000Z",
|
||||||
@@ -171,11 +171,11 @@ def test_russia_provider_prefers_official_web_rows_when_available():
|
|||||||
|
|
||||||
snapshot = build_country_network_snapshot("moscow", raw)
|
snapshot = build_country_network_snapshot("moscow", raw)
|
||||||
|
|
||||||
assert snapshot["provider_code"] == "russia_station_web"
|
assert snapshot["provider_code"] == "russia_metar_cluster"
|
||||||
assert snapshot["official_network_status"]["available"] is True
|
assert snapshot["official_network_status"]["available"] is True
|
||||||
assert snapshot["official_network_status"]["mode"] == "official_web_crawl"
|
assert snapshot["official_network_status"]["mode"] == "realtime_metar_cluster"
|
||||||
assert snapshot["official_nearby"][0]["source_code"] == "ru_station_web"
|
assert snapshot["official_nearby"][0]["source_code"] == "metar_cluster"
|
||||||
assert snapshot["official_nearby"][0]["is_official"] is True
|
assert snapshot["official_nearby"][0]["is_airport_station"] is True
|
||||||
|
|
||||||
|
|
||||||
def test_city_detail_payload_exposes_airport_and_official_network_layers():
|
def test_city_detail_payload_exposes_airport_and_official_network_layers():
|
||||||
|
|||||||
Reference in New Issue
Block a user