From 55cfb202da38fe2a33ec34a4877f1c51e6c9e727 Mon Sep 17 00:00:00 2001 From: "2569718930@qq.com" <2569718930@qq.com> Date: Mon, 15 Jun 2026 21:02:44 +0800 Subject: [PATCH] Label Turkey station observations as MGM --- .../__tests__/temperatureChartData.test.ts | 28 +++++++++++++++ .../scan-terminal/temperature-chart-logic.ts | 4 +++ frontend/lib/dashboard-types.ts | 7 ++++ tests/test_scan_terminal_modules.py | 34 +++++++++++++++++++ web/scan_terminal_city_row.py | 9 +++++ 5 files changed, 82 insertions(+) diff --git a/frontend/components/dashboard/scan-terminal/__tests__/temperatureChartData.test.ts b/frontend/components/dashboard/scan-terminal/__tests__/temperatureChartData.test.ts index 04cf1dde..d5c7f260 100644 --- a/frontend/components/dashboard/scan-terminal/__tests__/temperatureChartData.test.ts +++ b/frontend/components/dashboard/scan-terminal/__tests__/temperatureChartData.test.ts @@ -541,6 +541,34 @@ export function runTests() { "NOAA MADIS label should be reserved for US airports; non-US airport-primary fallback should use METAR wording", ); + const ankaraScanSeedChart = buildFullDayChartData( + { + city: "ankara", + local_date: "2026-06-14", + local_time: "15:10", + tz_offset_seconds: 3 * 60 * 60, + airport: "Esenboğa 机场", + temp_symbol: "°C", + } as any, + { + localDate: "2026-06-14", + localTime: "15:10", + times: ["00:00", "12:00", "18:00"], + temps: [15, 19, 18], + airportPrimary: { + temp: 19, + obs_time: "2026-06-14T12:10:00Z", + }, + airportPrimaryTodayObs: [["2026-06-14T12:10:00Z", 19]], + } as any, + false, + ); + const ankaraScanSeedSeries = ankaraScanSeedChart.series.find((item) => item.key === "madis"); + assert( + ankaraScanSeedSeries?.label === "MGM", + "Ankara scan-row-seeded airport-primary curve should default to MGM instead of NOAA MADIS when source metadata is missing", + ); + const guangzhouRunwayWithBadMadisChart = buildFullDayChartData( { city: "guangzhou", diff --git a/frontend/components/dashboard/scan-terminal/temperature-chart-logic.ts b/frontend/components/dashboard/scan-terminal/temperature-chart-logic.ts index 2087ce3d..444b2fcd 100644 --- a/frontend/components/dashboard/scan-terminal/temperature-chart-logic.ts +++ b/frontend/components/dashboard/scan-terminal/temperature-chart-logic.ts @@ -775,8 +775,12 @@ function airportPrimarySeriesLabel( row?: ScanOpportunityRow | null, ) { if (isHKO) return "HKO"; + const cityKey = normalizeCityKey(row?.city); const canonicalLabel = canonicalAirportPrimarySourceLabel(hourly); if (canonicalLabel === "MGM") return canonicalLabel; + if ((cityKey === "ankara" || cityKey === "istanbul") && (!canonicalLabel || canonicalLabel === "NOAA MADIS")) { + return "MGM"; + } const payloadLabel = String(hourly?.airportPrimary?.source_label || "").trim(); if (payloadLabel && !isGenericAirportPrimaryLabel(payloadLabel)) return payloadLabel; if (canonicalLabel === "NOAA MADIS" && !isUsAirportCode(airportCodeForSeriesLabel(hourly, row))) { diff --git a/frontend/lib/dashboard-types.ts b/frontend/lib/dashboard-types.ts index a28e2799..a5d740be 100644 --- a/frontend/lib/dashboard-types.ts +++ b/frontend/lib/dashboard-types.ts @@ -533,6 +533,13 @@ export interface ScanOpportunityRow { current_temp?: number | null; current_max_so_far?: number | null; wunderground_current?: AirportCurrentConditions; + icao?: string | null; + station_source_code?: string | null; + station_source_label?: string | null; + station_code?: string | null; + station_label?: string | null; + network_provider?: string | null; + network_provider_label?: string | null; metar_context?: { source?: string | null; station?: string | null; diff --git a/tests/test_scan_terminal_modules.py b/tests/test_scan_terminal_modules.py index 0be8dd7e..7fb53d67 100644 --- a/tests/test_scan_terminal_modules.py +++ b/tests/test_scan_terminal_modules.py @@ -603,6 +603,40 @@ def test_scan_terminal_quick_row_compacts_runway_history_for_list_payload(): assert len(str(row["runway_plate_history"])) < len(str(raw_history)) +def test_scan_terminal_quick_row_exposes_airport_primary_source_metadata(): + row = _build_quick_row( + city="ankara", + data={ + "display_name": "Ankara", + "local_date": "2026-06-14", + "local_time": "2026-06-14T15:10:00+03:00", + "temp_symbol": "°C", + "current": {"temp": 19.0, "max_so_far": 19.0}, + "risk": {"airport": "Esenboğa", "icao": "LTAC", "level": "medium"}, + "airport_primary": { + "station_code": "LTAC", + "station_label": "Esenboğa 机场", + "source_code": "mgm", + "source_label": "MGM", + }, + "official_network_source": "turkey_mgm", + "official_network_status": { + "provider_code": "turkey_mgm", + "provider_label": "MGM", + }, + "deb": {"prediction": 20.0}, + "probabilities": {"distribution": []}, + "multi_model": {}, + }, + ) + + assert row["icao"] == "LTAC" + assert row["station_source_code"] == "mgm" + assert row["station_source_label"] == "MGM" + assert row["station_code"] == "LTAC" + assert row["network_provider"] == "turkey_mgm" + + def test_metar_gate_vetoes_yes_when_observed_breaks_above_bucket(): row = { "id": "yes-row", diff --git a/web/scan_terminal_city_row.py b/web/scan_terminal_city_row.py index a1e080e8..85cdaf42 100644 --- a/web/scan_terminal_city_row.py +++ b/web/scan_terminal_city_row.py @@ -239,6 +239,8 @@ def _build_quick_row( ) -> Optional[Dict[str, Any]]: curr = data.get("current") or {} risk = data.get("risk") or {} + airport_primary = data.get("airport_primary") if isinstance(data.get("airport_primary"), dict) else {} + official_status = data.get("official_network_status") if isinstance(data.get("official_network_status"), dict) else {} deb = data.get("deb") or {} probs = data.get("probabilities") or {} multi = data.get("multi_model") or {} @@ -274,6 +276,13 @@ def _build_quick_row( "current_temp": curr.get("temp"), "current_max_so_far": curr.get("max_so_far"), "wunderground_current": data.get("wunderground_current") or {}, + "icao": str(risk.get("icao") or airport_primary.get("station_code") or ""), + "station_source_code": airport_primary.get("source_code") or data.get("official_network_source"), + "station_source_label": airport_primary.get("source_label") or official_status.get("provider_label"), + "station_code": airport_primary.get("station_code") or risk.get("icao"), + "station_label": airport_primary.get("station_label") or risk.get("airport"), + "network_provider": data.get("official_network_source") or official_status.get("provider_code"), + "network_provider_label": official_status.get("provider_label"), "deb_prediction": deb.get("prediction"), "model_cluster_sources": ( daily_entry.get("models")